From a0df0a527fc3a6954fc08651947a5cfe1455e652 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 28 Nov 2024 02:05:45 +0000 Subject: [PATCH 001/171] 8340731: Cleanup remaining IA64 references in hotspot code Reviewed-by: dholmes, aph --- src/hotspot/os/bsd/os_bsd.cpp | 6 +--- src/hotspot/os/linux/hugepages.cpp | 3 +- src/hotspot/os/linux/os_linux.cpp | 33 ++++++------------- src/hotspot/os/posix/signals_posix.cpp | 7 ---- src/hotspot/share/opto/chaitin.cpp | 1 - src/hotspot/share/opto/generateOptoStub.cpp | 4 --- src/hotspot/share/opto/memnode.hpp | 4 +-- src/hotspot/share/opto/output.cpp | 2 +- .../share/runtime/abstract_vm_version.cpp | 1 - src/hotspot/share/runtime/deoptimization.cpp | 6 +--- src/hotspot/share/runtime/frame.hpp | 7 ++-- src/hotspot/share/runtime/javaCalls.cpp | 5 --- src/hotspot/share/runtime/os.hpp | 3 +- src/hotspot/share/runtime/relocator.cpp | 2 +- .../share/utilities/elfFuncDescTable.cpp | 5 ++- .../share/utilities/elfFuncDescTable.hpp | 5 ++- src/hotspot/share/utilities/macros.hpp | 12 ------- 17 files changed, 25 insertions(+), 81 deletions(-) diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index a1887aa5975..8185797563c 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -229,8 +229,6 @@ size_t os::rss() { // Cpu architecture string #if defined(ZERO) static char cpu_arch[] = ZERO_LIBARCH; -#elif defined(IA64) -static char cpu_arch[] = "ia64"; #elif defined(IA32) static char cpu_arch[] = "i386"; #elif defined(AMD64) @@ -1192,8 +1190,6 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_386; #elif (defined AMD64) static Elf32_Half running_arch_code=EM_X86_64; - #elif (defined IA64) - static Elf32_Half running_arch_code=EM_IA_64; #elif (defined __powerpc64__) static Elf32_Half running_arch_code=EM_PPC64; #elif (defined __powerpc__) @@ -1214,7 +1210,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_68K; #else #error Method os::dll_load requires that one of following is defined:\ - IA32, AMD64, IA64, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K + IA32, AMD64, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K #endif // Identify compatibility class for VM's architecture and library's architecture diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index b71593487cf..c04ff7a4ca0 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -55,8 +55,7 @@ static size_t scan_default_hugepagesize() { // large_page_size on Linux is used to round up heap size. x86 uses either // 2M or 4M page, depending on whether PAE (Physical Address Extensions) - // mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. IA64 can use - // page as large as 1G. + // mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. // // Here we try to figure out page size by parsing /proc/meminfo and looking // for a line with the following format: diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index b2b9a798119..d51ca8a5ffb 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -461,26 +461,17 @@ bool os::Linux::get_tick_information(CPUPerfTicks* pticks, int which_logical_cpu } #ifndef SYS_gettid -// i386: 224, ia64: 1105, amd64: 186, sparc: 143 - #ifdef __ia64__ - #define SYS_gettid 1105 +// i386: 224, amd64: 186, sparc: 143 + #if defined(__i386__) + #define SYS_gettid 224 + #elif defined(__amd64__) + #define SYS_gettid 186 + #elif defined(__sparc__) + #define SYS_gettid 143 #else - #ifdef __i386__ - #define SYS_gettid 224 - #else - #ifdef __amd64__ - #define SYS_gettid 186 - #else - #ifdef __sparc__ - #define SYS_gettid 143 - #else - #error define gettid for the arch - #endif - #endif - #endif + #error "Define SYS_gettid for this architecture" #endif -#endif - +#endif // SYS_gettid // pid_t gettid() // @@ -1778,8 +1769,6 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_386; #elif (defined AMD64) || (defined X32) static Elf32_Half running_arch_code=EM_X86_64; -#elif (defined IA64) - static Elf32_Half running_arch_code=EM_IA_64; #elif (defined __sparc) && (defined _LP64) static Elf32_Half running_arch_code=EM_SPARCV9; #elif (defined __sparc) && (!defined _LP64) @@ -1812,7 +1801,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { static Elf32_Half running_arch_code=EM_LOONGARCH; #else #error Method os::dll_load requires that one of following is defined:\ - AARCH64, ALPHA, ARM, AMD64, IA32, IA64, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc + AARCH64, ALPHA, ARM, AMD64, IA32, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc #endif // Identify compatibility class for VM's architecture and library's architecture @@ -2719,8 +2708,6 @@ void os::get_summary_cpu_info(char* cpuinfo, size_t length) { strncpy(cpuinfo, "ARM", length); #elif defined(IA32) strncpy(cpuinfo, "x86_32", length); -#elif defined(IA64) - strncpy(cpuinfo, "IA64", length); #elif defined(PPC) strncpy(cpuinfo, "PPC64", length); #elif defined(RISCV) diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index bbf122fabfb..ddc7a05c2ff 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -960,10 +960,6 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGILL, ILL_PRVREG, "ILL_PRVREG", "Privileged register." }, { SIGILL, ILL_COPROC, "ILL_COPROC", "Coprocessor error." }, { SIGILL, ILL_BADSTK, "ILL_BADSTK", "Internal stack error." }, -#if defined(IA64) && defined(LINUX) - { SIGILL, ILL_BADIADDR, "ILL_BADIADDR", "Unimplemented instruction address" }, - { SIGILL, ILL_BREAK, "ILL_BREAK", "Application Break instruction" }, -#endif { SIGFPE, FPE_INTDIV, "FPE_INTDIV", "Integer divide by zero." }, { SIGFPE, FPE_INTOVF, "FPE_INTOVF", "Integer overflow." }, { SIGFPE, FPE_FLTDIV, "FPE_FLTDIV", "Floating-point divide by zero." }, @@ -977,9 +973,6 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t #if defined(AIX) // no explanation found what keyerr would be { SIGSEGV, SEGV_KEYERR, "SEGV_KEYERR", "key error" }, -#endif -#if defined(IA64) && !defined(AIX) - { SIGSEGV, SEGV_PSTKOVF, "SEGV_PSTKOVF", "Paragraph stack overflow" }, #endif { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." }, { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." }, diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 6252e31d898..c9d1491afc5 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -956,7 +956,6 @@ void PhaseChaitin::gather_lrg_masks( bool after_aggressive ) { // Each entry is reg_pressure_per_value,number_of_regs // RegL RegI RegFlags RegF RegD INTPRESSURE FLOATPRESSURE // IA32 2 1 1 1 1 6 6 - // IA64 1 1 1 1 1 50 41 // SPARC 2 2 2 2 2 48 (24) 52 (26) // SPARCV9 2 2 2 2 2 48 (24) 52 (26) // AMD64 1 1 1 1 1 14 15 diff --git a/src/hotspot/share/opto/generateOptoStub.cpp b/src/hotspot/share/opto/generateOptoStub.cpp index 2e3dc878c84..b6f09dcc439 100644 --- a/src/hotspot/share/opto/generateOptoStub.cpp +++ b/src/hotspot/share/opto/generateOptoStub.cpp @@ -224,10 +224,6 @@ void GraphKit::gen_stub(address C_function, store_to_memory(control(), adr_sp, null(), T_ADDRESS, MemNode::unordered); // Clear last_Java_pc store_to_memory(control(), adr_last_Java_pc, null(), T_ADDRESS, MemNode::unordered); -#if (defined(IA64) && !defined(AIX)) - Node* adr_last_Java_fp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_fp_offset())); - store_to_memory(control(), adr_last_Java_fp, null(), T_ADDRESS, MemNode::unordered); -#endif // For is-fancy-jump, the C-return value is also the branch target Node* target = map()->in(TypeFunc::Parms); diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 83ac80c043f..ee9db7d1cac 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -196,7 +196,7 @@ class LoadNode : public MemNode { // non-pinned LoadNode by the pinned LoadNode. ControlDependency _control_dependency; - // On platforms with weak memory ordering (e.g., PPC, Ia64) we distinguish + // On platforms with weak memory ordering (e.g., PPC) we distinguish // loads that can be reordered, and such requiring acquire semantics to // adhere to the Java specification. The required behaviour is stored in // this field. @@ -566,7 +566,7 @@ class LoadNKlassNode : public LoadNNode { // Store value; requires Store, Address and Value class StoreNode : public MemNode { private: - // On platforms with weak memory ordering (e.g., PPC, Ia64) we distinguish + // On platforms with weak memory ordering (e.g., PPC) we distinguish // stores that can be reordered, and such requiring release semantics to // adhere to the Java specification. The required behaviour is stored in // this field. diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index fa781bf23a6..ee91dc4323a 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -383,7 +383,7 @@ bool PhaseOutput::need_stack_bang(int frame_size_in_bytes) const { bool PhaseOutput::need_register_stack_bang() const { // Determine if we need to generate a register stack overflow check. // This is only used on architectures which have split register - // and memory stacks (ie. IA64). + // and memory stacks. // Bang if the method is not a stub function and has java calls return (C->stub_function() == nullptr && C->has_java_calls()); } diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 96da109dafe..590d164366b 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -187,7 +187,6 @@ const char* Abstract_VM_Version::vm_release() { #define CPU AARCH64_ONLY("aarch64") \ AMD64_ONLY("amd64") \ IA32_ONLY("x86") \ - IA64_ONLY("ia64") \ S390_ONLY("s390") \ RISCV64_ONLY("riscv64") #endif // !ZERO diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 10fd83750d4..1558f3c9bce 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -607,9 +607,6 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // where it will be very difficult to figure out what went wrong. Better // to die an early death here than some very obscure death later when the // trail is cold. - // Note: on ia64 this guarantee can be fooled by frames with no memory stack - // in that it will fail to detect a problem when there is one. This needs - // more work in tiger timeframe. guarantee(array->unextended_sp() == unpack_sp, "vframe_array_head must contain the vframeArray to unpack"); int number_of_frames = array->frames(); @@ -2342,8 +2339,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr } // Setting +ProfileTraps fixes the following, on all platforms: - // 4852688: ProfileInterpreter is off by default for ia64. The result is - // infinite heroic-opt-uncommon-trap/deopt/recompile cycles, since the + // The result is infinite heroic-opt-uncommon-trap/deopt/recompile cycles, since the // recompile relies on a MethodData* to record heroic opt failures. // Whether the interpreter is producing MDO data or not, we also need diff --git a/src/hotspot/share/runtime/frame.hpp b/src/hotspot/share/runtime/frame.hpp index fe531cff8a6..0363d7305d2 100644 --- a/src/hotspot/share/runtime/frame.hpp +++ b/src/hotspot/share/runtime/frame.hpp @@ -164,10 +164,9 @@ class frame { void patch_pc(Thread* thread, address pc); // Every frame needs to return a unique id which distinguishes it from all other frames. - // For sparc and ia32 use sp. ia64 can have memory frames that are empty so multiple frames - // will have identical sp values. For ia64 the bsp (fp) value will serve. No real frame - // should have an id() of null so it is a distinguishing value for an unmatchable frame. - // We also have relationals which allow comparing a frame to anoth frame's id() allow + // For sparc and ia32 use sp. + // No real frame should have an id() of null so it is a distinguishing value for an unmatchable frame. + // We also have relationals which allow comparing a frame to another frame's id() allowing // us to distinguish younger (more recent activation) from older (less recent activations) // A null id is only valid when comparing for equality. diff --git a/src/hotspot/share/runtime/javaCalls.cpp b/src/hotspot/share/runtime/javaCalls.cpp index b3b7b6f6834..e8f647fe484 100644 --- a/src/hotspot/share/runtime/javaCalls.cpp +++ b/src/hotspot/share/runtime/javaCalls.cpp @@ -116,11 +116,6 @@ JavaCallWrapper::~JavaCallWrapper() { ThreadStateTransition::transition_from_java(_thread, _thread_in_vm); // State has been restored now make the anchor frame visible for the profiler. - // Do this after the transition because this allows us to put an assert - // the Java->vm transition which checks to see that stack is not walkable - // on sparc/ia64 which will catch violations of the resetting of last_Java_frame - // invariants (i.e. _flags always cleared on return to Java) - _thread->frame_anchor()->copy(&_anchor); // Release handles after we are marked as being inside the VM again, since this diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 8e4a214b691..0bc543957c9 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -859,8 +859,7 @@ class os: AllStatic { // We don't attempt to become a debugger, so we only follow frames if that // does not require a lookup in the unwind table, which is part of the binary // file but may be unsafe to read after a fatal error. So on x86, we can - // only walk stack if %ebp is used as frame pointer; on ia64, it's not - // possible to walk C stack without having the unwind table. + // only walk stack if %ebp is used as frame pointer. static bool is_first_C_frame(frame *fr); static frame get_sender_for_C_frame(frame *fr); diff --git a/src/hotspot/share/runtime/relocator.cpp b/src/hotspot/share/runtime/relocator.cpp index 5b5ff2b83e6..dc244ce920d 100644 --- a/src/hotspot/share/runtime/relocator.cpp +++ b/src/hotspot/share/runtime/relocator.cpp @@ -200,7 +200,7 @@ bool Relocator::handle_code_changes() { bool Relocator::is_opcode_lookupswitch(Bytecodes::Code bc) { switch (bc) { case Bytecodes::_tableswitch: return false; - case Bytecodes::_lookupswitch: // not rewritten on ia64 + case Bytecodes::_lookupswitch: case Bytecodes::_fast_linearswitch: // rewritten _lookupswitch case Bytecodes::_fast_binaryswitch: return true; // rewritten _lookupswitch default: ShouldNotReachHere(); diff --git a/src/hotspot/share/utilities/elfFuncDescTable.cpp b/src/hotspot/share/utilities/elfFuncDescTable.cpp index bd9beb202c6..d23a1da68f4 100644 --- a/src/hotspot/share/utilities/elfFuncDescTable.cpp +++ b/src/hotspot/share/utilities/elfFuncDescTable.cpp @@ -34,11 +34,10 @@ ElfFuncDescTable::ElfFuncDescTable(FILE* file, Elf_Shdr shdr, int index) : _section(file, shdr), _file(file), _index(index) { assert(file, "null file handle"); // The actual function address (i.e. function entry point) is always the - // first value in the function descriptor (on IA64 and PPC64 they look as follows): + // first value in the function descriptor (on PPC64 they look as follows): // PPC64: [function entry point, TOC pointer, environment pointer] - // IA64 : [function entry point, GP (global pointer) value] // Unfortunately 'shdr.sh_entsize' doesn't always seem to contain this size (it's zero on PPC64) so we can't assert - // assert(IA64_ONLY(2) PPC64_ONLY(3) * sizeof(address) == shdr.sh_entsize, "Size mismatch for '.opd' section entries"); + // assert(PPC64_ONLY(3) * sizeof(address) == shdr.sh_entsize, "Size mismatch for '.opd' section entries"); _status = _section.status(); } diff --git a/src/hotspot/share/utilities/elfFuncDescTable.hpp b/src/hotspot/share/utilities/elfFuncDescTable.hpp index a0e67611554..daba24ee626 100644 --- a/src/hotspot/share/utilities/elfFuncDescTable.hpp +++ b/src/hotspot/share/utilities/elfFuncDescTable.hpp @@ -35,9 +35,8 @@ /* -On PowerPC-64 (and other architectures like for example IA64) a pointer to a -function is not just a plain code address, but instead a pointer to a so called -function descriptor (which is simply a structure containing 3 pointers). +On PowerPC-64 a pointer to a function is not just a plain code address, but instead a pointer +to a so-called function descriptor (which is simply a structure containing 3 pointers). This fact is also reflected in the ELF ABI for PowerPC-64. On architectures like x86 or SPARC, the ELF symbol table contains the start diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index 23094c9e8c4..b14905ff8e2 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -458,18 +458,6 @@ #define NOT_IA32(code) code #endif -// This is a REALLY BIG HACK, but on AIX unconditionally defines IA64. -// At least on AIX 7.1 this is a real problem because 'systemcfg.h' is indirectly included -// by 'pthread.h' and other common system headers. - -#if defined(IA64) && !defined(AIX) -#define IA64_ONLY(code) code -#define NOT_IA64(code) -#else -#define IA64_ONLY(code) -#define NOT_IA64(code) code -#endif - #ifdef AMD64 #define AMD64_ONLY(code) code #define NOT_AMD64(code) From 1a07d542ec810282eb78653698d098a24b35686f Mon Sep 17 00:00:00 2001 From: David Holmes Date: Thu, 28 Nov 2024 02:24:55 +0000 Subject: [PATCH 002/171] 8343703: Symbol name cleanups after JEP 479 Reviewed-by: kbarrett, amenkov --- src/hotspot/share/include/jvm.h | 7 --- src/hotspot/share/prims/jvmtiAgent.cpp | 39 +++++++-------- src/hotspot/share/runtime/os.cpp | 32 ++++-------- src/hotspot/share/runtime/os.hpp | 6 +-- .../share/native/libjava/NativeLibraries.c | 50 +++++++------------ 5 files changed, 46 insertions(+), 88 deletions(-) diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index bc46a2cf852..474d5baa487 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -1147,13 +1147,6 @@ JVM_GetClassFileVersion(JNIEnv *env, jclass current); JNIEXPORT jboolean JNICALL JVM_PrintWarningAtDynamicAgentLoad(void); -#define JNI_ONLOAD_SYMBOLS {"JNI_OnLoad"} -#define JNI_ONUNLOAD_SYMBOLS {"JNI_OnUnload"} -#define JVM_ONLOAD_SYMBOLS {"JVM_OnLoad"} -#define AGENT_ONLOAD_SYMBOLS {"Agent_OnLoad"} -#define AGENT_ONUNLOAD_SYMBOLS {"Agent_OnUnload"} -#define AGENT_ONATTACH_SYMBOLS {"Agent_OnAttach"} - /* * This structure is used by the launcher to get the default thread * stack size from the VM using JNI_GetDefaultJavaVMInitArgs() with a diff --git a/src/hotspot/share/prims/jvmtiAgent.cpp b/src/hotspot/share/prims/jvmtiAgent.cpp index 6dd205082a0..06702773d58 100644 --- a/src/hotspot/share/prims/jvmtiAgent.cpp +++ b/src/hotspot/share/prims/jvmtiAgent.cpp @@ -27,7 +27,6 @@ #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "jni.h" -#include "jvm.h" #include "jvm_io.h" #include "jvmtifiles/jvmtiEnv.hpp" #include "prims/jvmtiEnvBase.hpp" @@ -268,10 +267,10 @@ static void assert_preload(const JvmtiAgent* agent) { // For statically linked agents we can't rely on os_lib == nullptr because // statically linked agents could have a handle of RTLD_DEFAULT which == 0 on some platforms. // If this function returns true, then agent->is_static_lib() && agent->is_loaded(). -static bool load_agent_from_executable(JvmtiAgent* agent, const char* on_load_symbols[], size_t num_symbol_entries) { +static bool load_agent_from_executable(JvmtiAgent* agent, const char* on_load_symbol) { DEBUG_ONLY(assert_preload(agent);) - assert(on_load_symbols != nullptr, "invariant"); - return os::find_builtin_agent(agent, &on_load_symbols[0], num_symbol_entries); + assert(on_load_symbol != nullptr, "invariant"); + return os::find_builtin_agent(agent, on_load_symbol); } // Load the library from the absolute path of the agent, if available. @@ -310,7 +309,7 @@ static void* load_agent_from_relative_path(JvmtiAgent* agent, bool vm_exit_on_er } // For absolute and relative paths. -static void* load_library(JvmtiAgent* agent, const char* on_symbols[], size_t num_symbol_entries, bool vm_exit_on_error) { +static void* load_library(JvmtiAgent* agent, bool vm_exit_on_error) { return agent->is_absolute_path() ? load_agent_from_absolute_path(agent, vm_exit_on_error) : load_agent_from_relative_path(agent, vm_exit_on_error); } @@ -321,12 +320,11 @@ extern "C" { } // Find the OnLoad entry point for -agentlib: -agentpath: -Xrun agents. -// num_symbol_entries must be passed-in since only the caller knows the number of symbols in the array. -static OnLoadEntry_t lookup_On_Load_entry_point(JvmtiAgent* agent, const char* on_load_symbols[], size_t num_symbol_entries) { +static OnLoadEntry_t lookup_On_Load_entry_point(JvmtiAgent* agent, const char* on_load_symbol) { assert(agent != nullptr, "invariant"); if (!agent->is_loaded()) { - if (!load_agent_from_executable(agent, on_load_symbols, num_symbol_entries)) { - void* const library = load_library(agent, on_load_symbols, num_symbol_entries, /* vm exit on error */ true); + if (!load_agent_from_executable(agent, on_load_symbol)) { + void* const library = load_library(agent, /* vm exit on error */ true); assert(library != nullptr, "invariant"); agent->set_os_lib(library); agent->set_loaded(); @@ -334,17 +332,15 @@ static OnLoadEntry_t lookup_On_Load_entry_point(JvmtiAgent* agent, const char* o } assert(agent->is_loaded(), "invariant"); // Find the OnLoad function. - return CAST_TO_FN_PTR(OnLoadEntry_t, os::find_agent_function(agent, false, on_load_symbols, num_symbol_entries)); + return CAST_TO_FN_PTR(OnLoadEntry_t, os::find_agent_function(agent, false, on_load_symbol)); } static OnLoadEntry_t lookup_JVM_OnLoad_entry_point(JvmtiAgent* lib) { - const char* on_load_symbols[] = JVM_ONLOAD_SYMBOLS; - return lookup_On_Load_entry_point(lib, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*)); + return lookup_On_Load_entry_point(lib, "JVM_OnLoad"); } static OnLoadEntry_t lookup_Agent_OnLoad_entry_point(JvmtiAgent* agent) { - const char* on_load_symbols[] = AGENT_ONLOAD_SYMBOLS; - return lookup_On_Load_entry_point(agent, on_load_symbols, sizeof(on_load_symbols) / sizeof(char*)); + return lookup_On_Load_entry_point(agent, "Agent_OnLoad"); } void JvmtiAgent::convert_xrun_agent() { @@ -499,14 +495,13 @@ static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) { assert(agent->is_dynamic(), "invariant"); assert(st != nullptr, "invariant"); assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE, "not in live phase!"); - const char* on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS; - const size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols); + const char* on_attach_symbol = "Agent_OnAttach"; void* library = nullptr; bool previously_loaded; - if (load_agent_from_executable(agent, &on_attach_symbols[0], num_symbol_entries)) { + if (load_agent_from_executable(agent, on_attach_symbol)) { previously_loaded = JvmtiAgentList::is_static_lib_loaded(agent->name()); } else { - library = load_library(agent, &on_attach_symbols[0], num_symbol_entries, /* vm_exit_on_error */ false); + library = load_library(agent, /* vm_exit_on_error */ false); if (library == nullptr) { st->print_cr("%s was not loaded.", agent->name()); if (*ebuf != '\0') { @@ -531,10 +526,10 @@ static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) { assert(agent->is_loaded(), "invariant"); // The library was loaded so we attempt to lookup and invoke the Agent_OnAttach function. OnAttachEntry_t on_attach_entry = CAST_TO_FN_PTR(OnAttachEntry_t, - os::find_agent_function(agent, false, &on_attach_symbols[0], num_symbol_entries)); + os::find_agent_function(agent, false, on_attach_symbol)); if (on_attach_entry == nullptr) { - st->print_cr("%s is not available in %s", on_attach_symbols[0], agent->name()); + st->print_cr("%s is not available in %s", on_attach_symbol, agent->name()); unload_library(agent, library); return false; } @@ -629,10 +624,10 @@ extern "C" { } void JvmtiAgent::unload() { - const char* on_unload_symbols[] = AGENT_ONUNLOAD_SYMBOLS; + const char* on_unload_symbol = "Agent_OnUnload"; // Find the Agent_OnUnload function. Agent_OnUnload_t unload_entry = CAST_TO_FN_PTR(Agent_OnUnload_t, - os::find_agent_function(this, false, &on_unload_symbols[0], ARRAY_SIZE(on_unload_symbols))); + os::find_agent_function(this, false, on_unload_symbol)); if (unload_entry != nullptr) { // Invoke the Agent_OnUnload function JavaThread* thread = JavaThread::current(); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index d8e539ca115..deb2c30500c 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -546,49 +546,35 @@ void* os::native_java_library() { * executable if agent_lib->is_static_lib() == true or in the shared library * referenced by 'handle'. */ -void* os::find_agent_function(JvmtiAgent *agent_lib, bool check_lib, - const char *syms[], size_t syms_len) { +void* os::find_agent_function(JvmtiAgent *agent_lib, bool check_lib, const char *sym) { assert(agent_lib != nullptr, "sanity check"); - const char *lib_name; void *handle = agent_lib->os_lib(); void *entryName = nullptr; - char *agent_function_name; - size_t i; // If checking then use the agent name otherwise test is_static_lib() to // see how to process this lookup - lib_name = ((check_lib || agent_lib->is_static_lib()) ? agent_lib->name() : nullptr); - for (i = 0; i < syms_len; i++) { - agent_function_name = build_agent_function_name(syms[i], lib_name, agent_lib->is_absolute_path()); - if (agent_function_name == nullptr) { - break; - } + const char *lib_name = ((check_lib || agent_lib->is_static_lib()) ? agent_lib->name() : nullptr); + + char* agent_function_name = build_agent_function_name(sym, lib_name, agent_lib->is_absolute_path()); + if (agent_function_name != nullptr) { entryName = dll_lookup(handle, agent_function_name); FREE_C_HEAP_ARRAY(char, agent_function_name); - if (entryName != nullptr) { - break; - } } return entryName; } // See if the passed in agent is statically linked into the VM image. -bool os::find_builtin_agent(JvmtiAgent* agent, const char *syms[], - size_t syms_len) { - void *ret; - void *proc_handle; - void *save_handle; - +bool os::find_builtin_agent(JvmtiAgent* agent, const char* sym) { assert(agent != nullptr, "sanity check"); if (agent->name() == nullptr) { return false; } - proc_handle = get_default_process_handle(); + void* proc_handle = get_default_process_handle(); // Check for Agent_OnLoad/Attach_lib_name function - save_handle = agent->os_lib(); + void* save_handle = agent->os_lib(); // We want to look in this process' symbol table. agent->set_os_lib(proc_handle); - ret = find_agent_function(agent, true, syms, syms_len); + void* ret = find_agent_function(agent, true, sym); if (ret != nullptr) { // Found an entry point like Agent_OnLoad_lib_name so we have a static agent agent->set_static_lib(); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 0bc543957c9..2e52440ead4 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -777,12 +777,10 @@ class os: AllStatic { static void* get_default_process_handle(); // Check for static linked agent library - static bool find_builtin_agent(JvmtiAgent *agent_lib, const char *syms[], - size_t syms_len); + static bool find_builtin_agent(JvmtiAgent* agent_lib, const char* sym); // Find agent entry point - static void *find_agent_function(JvmtiAgent *agent_lib, bool check_lib, - const char *syms[], size_t syms_len); + static void* find_agent_function(JvmtiAgent* agent_lib, bool check_lib, const char* sym); // Provide wrapper versions of these functions to guarantee NUL-termination // in all cases. diff --git a/src/java.base/share/native/libjava/NativeLibraries.c b/src/java.base/share/native/libjava/NativeLibraries.c index df5ff343d61..b6624eccd1d 100644 --- a/src/java.base/share/native/libjava/NativeLibraries.c +++ b/src/java.base/share/native/libjava/NativeLibraries.c @@ -64,45 +64,31 @@ static jboolean initIDs(JNIEnv *env) */ static void *findJniFunction(JNIEnv *env, void *handle, const char *cname, jboolean isLoad) { - const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS; - const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS; - const char **syms; - int symsLen; + const char *sym; void *entryName = NULL; char *jniFunctionName; - int i; size_t len; // Check for JNI_On(Un)Load<_libname> function - if (isLoad) { - syms = onLoadSymbols; - symsLen = sizeof(onLoadSymbols) / sizeof(char *); - } else { - syms = onUnloadSymbols; - symsLen = sizeof(onUnloadSymbols) / sizeof(char *); + sym = isLoad ? "JNI_OnLoad" : "JNI_OnUnload"; + + // sym + '_' + cname + '\0' + if ((len = strlen(sym) + (cname != NULL ? (strlen(cname) + 1) : 0) + 1) > + FILENAME_MAX) { + goto done; } - for (i = 0; i < symsLen; i++) { - // cname + sym + '_' + '\0' - if ((len = (cname != NULL ? strlen(cname) : 0) + strlen(syms[i]) + 2) > - FILENAME_MAX) { - goto done; - } - jniFunctionName = malloc(len); - if (jniFunctionName == NULL) { - JNU_ThrowOutOfMemoryError(env, NULL); - goto done; - } - strcpy(jniFunctionName, syms[i]); - if (cname != NULL) { - strcat(jniFunctionName, "_"); - strcat(jniFunctionName, cname); - } - entryName = JVM_FindLibraryEntry(handle, jniFunctionName); - free(jniFunctionName); - if(entryName) { - break; - } + jniFunctionName = malloc(len); + if (jniFunctionName == NULL) { + JNU_ThrowOutOfMemoryError(env, NULL); + goto done; + } + strcpy(jniFunctionName, sym); + if (cname != NULL) { + strcat(jniFunctionName, "_"); + strcat(jniFunctionName, cname); } + entryName = JVM_FindLibraryEntry(handle, jniFunctionName); + free(jniFunctionName); done: return entryName; From ce9d543eb1bf26592320fae650fe15638d6d30cf Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 28 Nov 2024 06:57:51 +0000 Subject: [PATCH 003/171] 8345119: Some java/foreign tests wrongly assume aligned memory Reviewed-by: mcimadamore, jvernee --- test/jdk/java/foreign/TestByteBuffer.java | 28 +++++++++---------- .../jdk/java/foreign/TestDereferencePath.java | 4 +-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index 77b3dd3b105..a77f97018ca 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -171,7 +171,7 @@ static void checkBytes(MemorySegment base, SequenceLayout lay @Test public void testOffheap() { try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(tuples);; + MemorySegment segment = arena.allocate(tuples); initTuples(segment, tuples.elementCount()); ByteBuffer bb = segment.asByteBuffer(); @@ -385,7 +385,7 @@ static void checkByteArrayAlignment(MemoryLayout layout) { public void testScopedBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { Buffer bb; try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(bytes);; + MemorySegment segment = arena.allocate(bytes); bb = bufferFactory.apply(segment.asByteBuffer()); } //outside of session!! @@ -411,7 +411,7 @@ public void testScopedBuffer(Function bufferFactory, @NoInje public void testScopedBufferAndVarHandle(VarHandle bufferHandle) { ByteBuffer bb; try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(bytes);; + MemorySegment segment = arena.allocate(bytes, Long.BYTES); bb = segment.asByteBuffer(); for (Map.Entry e : varHandleMembers(bb, bufferHandle).entrySet()) { MethodHandle handle = e.getKey().bindTo(bufferHandle) @@ -445,7 +445,7 @@ public void testScopedBufferAndVarHandle(VarHandle bufferHandle) { @Test(dataProvider = "bufferOps") public void testDirectBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(bytes);; + MemorySegment segment = arena.allocate(bytes); Buffer bb = bufferFactory.apply(segment.asByteBuffer()); assertTrue(bb.isDirect()); DirectBuffer directBuffer = ((DirectBuffer)bb); @@ -458,7 +458,7 @@ public void testDirectBuffer(Function bufferFactory, @NoInje @Test(dataProvider="resizeOps") public void testResizeOffheap(Consumer checker, Consumer initializer, SequenceLayout seq) { try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(seq);; + MemorySegment segment = arena.allocate(seq); initializer.accept(segment); checker.accept(segment); } @@ -496,7 +496,7 @@ public void testResizeRoundtripHeap(Consumer checker, Consumer checker, Consumer initializer, SequenceLayout seq) { try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(seq);; + MemorySegment segment = arena.allocate(seq); initializer.accept(segment); MemorySegment second = MemorySegment.ofBuffer(segment.asByteBuffer()); checker.accept(second); @@ -507,7 +507,7 @@ public void testResizeRoundtripNative(Consumer checker, Consumer< public void testBufferOnClosedSession() { MemorySegment leaked; try (Arena arena = Arena.ofConfined()) { - leaked = arena.allocate(bytes);; + leaked = arena.allocate(bytes); } ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok byteBuffer.get(); // should throw @@ -615,7 +615,7 @@ public void testCopyHeapToNative(Consumer checker, Consumer checker, Consumer _unused) { @Test public void testRoundTripAccess() { try (Arena arena = Arena.ofConfined()) { - MemorySegment ms = arena.allocate(4, 1);; + MemorySegment ms = arena.allocate(4, 1); MemorySegment msNoAccess = ms.asReadOnly(); MemorySegment msRoundTrip = MemorySegment.ofBuffer(msNoAccess.asByteBuffer()); assertEquals(msRoundTrip.scope(), ms.scope()); @@ -741,7 +741,7 @@ public void closeableArenas(Supplier arenaSupplier) throws IOException { tmp.deleteOnExit(); try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ; Arena arena = arenaSupplier.get()) { - MemorySegment segment = arena.allocate(10, 1);; + MemorySegment segment = arena.allocate(10, 1); for (int i = 0; i < 10; i++) { segment.set(JAVA_BYTE, i, (byte) i); } @@ -780,7 +780,7 @@ public void testIOOnClosedSegmentBuffer(Supplier arenaSupplier) throws IO @Test public void buffersAndArraysFromSlices() { try (Arena arena = Arena.ofShared()) { - MemorySegment segment = arena.allocate(16, 1);; + MemorySegment segment = arena.allocate(16, 1); int newSize = 8; var slice = segment.asSlice(4, newSize); @@ -798,7 +798,7 @@ public void buffersAndArraysFromSlices() { @Test public void viewsFromSharedSegment() { try (Arena arena = Arena.ofShared()) { - MemorySegment segment = arena.allocate(16, 1);; + MemorySegment segment = arena.allocate(16, 1); var byteBuffer = segment.asByteBuffer(); byteBuffer.asReadOnlyBuffer(); byteBuffer.slice(0, 8); diff --git a/test/jdk/java/foreign/TestDereferencePath.java b/test/jdk/java/foreign/TestDereferencePath.java index 116f2d318fc..e2281cde235 100644 --- a/test/jdk/java/foreign/TestDereferencePath.java +++ b/test/jdk/java/foreign/TestDereferencePath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -173,7 +173,7 @@ void badDerefMisAligned() { ValueLayout.ADDRESS.withTargetLayout(ValueLayout.JAVA_INT).withName("x")); try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(struct.byteSize() + 1).asSlice(1); + MemorySegment segment = arena.allocate(struct.byteSize() + 1, struct.byteAlignment()).asSlice(1); VarHandle vhX = struct.varHandle(PathElement.groupElement("x"), PathElement.dereferenceElement()); vhX.set(segment, 0L, 42); // should throw } From 81c44e5eb469ceed555a982e65feefcfde340a0b Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Thu, 28 Nov 2024 07:54:00 +0000 Subject: [PATCH 004/171] 8344908: URLClassPath should not propagate IllegalArgumentException when finding resources in classpath URLs Reviewed-by: alanb --- .../jdk/internal/loader/URLClassPath.java | 6 +- .../share/classes/sun/net/www/ParseUtil.java | 1 + .../jdk/internal/loader/FileURLMapper.java | 27 ++- .../jdk/internal/loader/FileURLMapper.java | 25 ++- .../URLClassPath/ClassPathUnusableURLs.java | 188 ++++++++++++++++++ 5 files changed, 225 insertions(+), 22 deletions(-) create mode 100644 test/jdk/jdk/internal/loader/URLClassPath/ClassPathUnusableURLs.java diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 75418111f74..06e3442c244 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -903,7 +903,11 @@ private static class FileLoader extends Loader { private FileLoader(URL url) throws IOException { super(url); String path = url.getFile().replace('/', File.separatorChar); - path = ParseUtil.decode(path); + try { + path = ParseUtil.decode(path); + } catch (IllegalArgumentException iae) { + throw new IOException(iae); + } dir = (new File(path)).getCanonicalFile(); @SuppressWarnings("deprecation") var _unused = normalizedBase = new URL(getBaseURL(), "."); diff --git a/src/java.base/share/classes/sun/net/www/ParseUtil.java b/src/java.base/share/classes/sun/net/www/ParseUtil.java index def688ad96a..3a35f86ab88 100644 --- a/src/java.base/share/classes/sun/net/www/ParseUtil.java +++ b/src/java.base/share/classes/sun/net/www/ParseUtil.java @@ -171,6 +171,7 @@ private static byte unescape(String s, int i) { * Returns a new String constructed from the specified String by replacing * the URL escape sequences and UTF8 encoding with the characters they * represent. + * @throws IllegalArgumentException if {@code s} could not be decoded */ public static String decode(String s) { int n = s.length(); diff --git a/src/java.base/unix/classes/jdk/internal/loader/FileURLMapper.java b/src/java.base/unix/classes/jdk/internal/loader/FileURLMapper.java index 6507b2961b9..79d0c6faf5b 100644 --- a/src/java.base/unix/classes/jdk/internal/loader/FileURLMapper.java +++ b/src/java.base/unix/classes/jdk/internal/loader/FileURLMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,8 +25,10 @@ package jdk.internal.loader; +import java.io.IOException; import java.net.URL; import java.io.File; + import sun.net.www.ParseUtil; /** @@ -40,12 +42,12 @@ * @author Michael McMahon */ -public class FileURLMapper { +final class FileURLMapper { - URL url; - String path; + private final URL url; + private String path; - public FileURLMapper (URL url) { + FileURLMapper(URL url) { this.url = url; } @@ -53,15 +55,18 @@ public FileURLMapper (URL url) { * @return the platform specific path corresponding to the URL * so long as the URL does not contain a hostname in the authority field. */ - - public String getPath () { + String getPath() throws IOException { if (path != null) { return path; } String host = url.getHost(); if (host == null || host.isEmpty() || "localhost".equalsIgnoreCase(host)) { path = url.getFile(); - path = ParseUtil.decode(path); + try { + path = ParseUtil.decode(path); + } catch (IllegalArgumentException iae) { + throw new IOException(iae); + } } return path; } @@ -69,12 +74,12 @@ public String getPath () { /** * Checks whether the file identified by the URL exists. */ - public boolean exists () { - String s = getPath (); + boolean exists() throws IOException { + String s = getPath(); if (s == null) { return false; } else { - File f = new File (s); + File f = new File(s); return f.exists(); } } diff --git a/src/java.base/windows/classes/jdk/internal/loader/FileURLMapper.java b/src/java.base/windows/classes/jdk/internal/loader/FileURLMapper.java index 0053cf70e9d..b72bc31b206 100644 --- a/src/java.base/windows/classes/jdk/internal/loader/FileURLMapper.java +++ b/src/java.base/windows/classes/jdk/internal/loader/FileURLMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,8 +25,10 @@ package jdk.internal.loader; +import java.io.IOException; import java.net.URL; import java.io.File; + import sun.net.www.ParseUtil; /** @@ -36,12 +38,12 @@ * @author Michael McMahon */ -public class FileURLMapper { +final class FileURLMapper { - URL url; - String file; + private final URL url; + private String file; - public FileURLMapper (URL url) { + FileURLMapper (URL url) { this.url = url; } @@ -49,8 +51,7 @@ public FileURLMapper (URL url) { * @return the platform specific path corresponding to the URL, and in particular * returns a UNC when the authority contains a hostname */ - - public String getPath () { + String getPath() throws IOException { if (file != null) { return file; } @@ -63,13 +64,17 @@ public String getPath () { return file; } String path = url.getFile().replace('/', '\\'); - file = ParseUtil.decode(path); + try { + file = ParseUtil.decode(path); + } catch (IllegalArgumentException iae) { + throw new IOException(iae); + } return file; } - public boolean exists() { + boolean exists() throws IOException { String path = getPath(); - File f = new File (path); + File f = new File(path); return f.exists(); } } diff --git a/test/jdk/jdk/internal/loader/URLClassPath/ClassPathUnusableURLs.java b/test/jdk/jdk/internal/loader/URLClassPath/ClassPathUnusableURLs.java new file mode 100644 index 00000000000..420425f37fc --- /dev/null +++ b/test/jdk/jdk/internal/loader/URLClassPath/ClassPathUnusableURLs.java @@ -0,0 +1,188 @@ +/* + * 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. + */ + +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import jdk.internal.loader.Resource; +import jdk.internal.loader.URLClassPath; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assumptions.abort; + +/* + * @test + * @bug 8344908 + * @summary verify that when locating resources, the URLClassPath can function properly + * without throwing unexpected exceptions when any URL in the classpath is unusable + * @modules java.base/jdk.internal.loader + * @run junit ClassPathUnusableURLs + */ +public class ClassPathUnusableURLs { + + private static final Path SCRATCH_DIR = Path.of(".").normalize(); + private static final String RESOURCE_NAME = "foo.txt"; + private static final String SMILEY_EMOJI = "\uD83D\uDE00"; + + private static Path ASCII_DIR; + private static Path EMOJI_DIR; + private static Path JAR_FILE_IN_EMOJI_DIR; + private static int NUM_EXPECTED_LOCATED_RESOURCES; + + + @BeforeAll + static void beforeAll() throws Exception { + try { + EMOJI_DIR = Files.createTempDirectory(SCRATCH_DIR, SMILEY_EMOJI); + } catch (IllegalArgumentException iae) { + iae.printStackTrace(); // for debug purpose + // if we can't create a directory with an emoji in its path name, + // then skip the entire test + abort("Skipping test since emoji directory couldn't be created: " + iae); + } + // successful creation of the dir, continue with the test + Files.createFile(EMOJI_DIR.resolve(RESOURCE_NAME)); + + ASCII_DIR = Files.createTempDirectory(SCRATCH_DIR, "test-urlclasspath"); + Files.createFile(ASCII_DIR.resolve(RESOURCE_NAME)); + + // create a jar file containing the resource + JAR_FILE_IN_EMOJI_DIR = Files.createTempDirectory(SCRATCH_DIR, SMILEY_EMOJI) + .resolve("foo.jar"); + final Manifest manifest = new Manifest(); + manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + try (OutputStream fos = Files.newOutputStream(JAR_FILE_IN_EMOJI_DIR); + JarOutputStream jos = new JarOutputStream(fos, manifest)) { + + final JarEntry jarEntry = new JarEntry(RESOURCE_NAME); + jos.putNextEntry(jarEntry); + jos.write("hello".getBytes(US_ASCII)); + jos.closeEntry(); + } + // Even if the resource is present in more than one classpath element, + // we expect it to be found by the URLClassPath only in the path which has just ascii + // characters. URLClassPath currently doesn't have the ability to serve resources + // from paths containing emoji character(s). + NUM_EXPECTED_LOCATED_RESOURCES = 1; + } + + /** + * Constructs a URLClassPath and then exercises the URLClassPath.findResource() + * and URLClassPath.findResources() methods and expects them to return the expected + * resources. + */ + @Test + void testFindResource() { + // start an empty URL classpath + final URLClassPath urlc = new URLClassPath(new URL[0]); + final String[] classpathElements = getClassPathElements(); + try { + // use addFile() to construct classpath + for (final String path : classpathElements) { + urlc.addFile(path); + } + // findResource() + assertNotNull(urlc.findResource(RESOURCE_NAME), "findResource() failed to locate" + + " resource: " + RESOURCE_NAME + " in classpath: " + + Arrays.toString(classpathElements)); + // findResources() + final Enumeration locatedResources = urlc.findResources(RESOURCE_NAME); + assertNotNull(locatedResources, "findResources() failed to" + + " locate resource: " + RESOURCE_NAME + " in classpath: " + + Arrays.toString(classpathElements)); + int numFound = 0; + while (locatedResources.hasMoreElements()) { + System.out.println("located " + locatedResources.nextElement() + + " for resource " + RESOURCE_NAME); + numFound++; + } + assertEquals(NUM_EXPECTED_LOCATED_RESOURCES, numFound, + "unexpected number of resources located for " + RESOURCE_NAME); + } finally { + urlc.closeLoaders(); + } + } + + /** + * Constructs a URLClassPath and then exercises the URLClassPath.getResource() + * and URLClassPath.getResources() methods and expects them to return the expected + * resources. + */ + @Test + void testGetResource() { + // start an empty URL classpath + final URLClassPath urlc = new URLClassPath(new URL[0]); + final String[] classpathElements = getClassPathElements(); + try { + // use addFile() to construct classpath + for (final String path : classpathElements) { + urlc.addFile(path); + } + // getResource() + assertNotNull(urlc.getResource(RESOURCE_NAME), "getResource() failed to locate" + + " resource: " + RESOURCE_NAME + " in classpath: " + + Arrays.toString(classpathElements)); + // getResources() + final Enumeration locatedResources = urlc.getResources(RESOURCE_NAME); + assertNotNull(locatedResources, "getResources() failed to" + + " locate resource: " + RESOURCE_NAME + " in classpath: " + + Arrays.toString(classpathElements)); + int numFound = 0; + while (locatedResources.hasMoreElements()) { + System.out.println("located " + locatedResources.nextElement().getURL() + + " for resource " + RESOURCE_NAME); + numFound++; + } + assertEquals(NUM_EXPECTED_LOCATED_RESOURCES, numFound, + "unexpected number of resources located for " + RESOURCE_NAME); + } finally { + urlc.closeLoaders(); + } + } + + private static String[] getClassPathElements() { + // Maintain the order - in context of this test, paths with emojis + // or those which can't serve the resource should come before the + // path that can serve the resource. + return new String[]{ + // non-existent path + ASCII_DIR.resolve("non-existent").toString(), + // existing emoji dir + EMOJI_DIR.toString(), + // existing jar file in a emoji dir + JAR_FILE_IN_EMOJI_DIR.toString(), + // existing ascii dir + ASCII_DIR.toString() + }; + } +} From 103338534f71309e4cc0ba289075fab768e66cd4 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Thu, 28 Nov 2024 07:59:48 +0000 Subject: [PATCH 005/171] 8344967: Some tests in TestFill do not use the test parameter Reviewed-by: jvernee --- test/jdk/java/foreign/TestFill.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/foreign/TestFill.java b/test/jdk/java/foreign/TestFill.java index e5f69587f2f..d9bde891120 100644 --- a/test/jdk/java/foreign/TestFill.java +++ b/test/jdk/java/foreign/TestFill.java @@ -87,7 +87,7 @@ void testValues(int value) { @MethodSource("sizes") void testReadOnly(int len) { try (var arena = Arena.ofConfined()) { - var segment = arena.allocate(10).asReadOnly(); + var segment = arena.allocate(len).asReadOnly(); assertThrows(IllegalArgumentException.class, () -> segment.fill(VALUE)); } } @@ -96,7 +96,7 @@ void testReadOnly(int len) { @MethodSource("sizes") void testConfinement(int len) { try (var arena = Arena.ofConfined()) { - var segment = arena.allocate(10); + var segment = arena.allocate(len); AtomicReference ex = new AtomicReference<>(); CompletableFuture future = CompletableFuture.runAsync(() -> { try { From e096660a18905bf1394d722790c5c3883e55dedc Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 28 Nov 2024 09:06:34 +0000 Subject: [PATCH 006/171] 8345043: [ASAN] methodMatcher.cpp report reading from a region of size 0 [-Werror=stringop-overread] Reviewed-by: kbarrett, dholmes --- src/hotspot/share/compiler/methodMatcher.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/compiler/methodMatcher.cpp b/src/hotspot/share/compiler/methodMatcher.cpp index 1a0ade2fadb..0bd5cdd8501 100644 --- a/src/hotspot/share/compiler/methodMatcher.cpp +++ b/src/hotspot/share/compiler/methodMatcher.cpp @@ -219,21 +219,22 @@ bool MethodMatcher::match(Symbol* candidate, Symbol* match, Mode match_mode) con static MethodMatcher::Mode check_mode(char name[], const char*& error_msg) { int match = MethodMatcher::Exact; + size_t len = strlen(name); if (name[0] == '*') { - if (strlen(name) == 1) { + if (len == 1) { return MethodMatcher::Any; } match |= MethodMatcher::Suffix; - memmove(name, name + 1, strlen(name + 1) + 1); + memmove(name, name + 1, len); // Include terminating nul in move. + len--; } - size_t len = strlen(name); if (len > 0 && name[len - 1] == '*') { match |= MethodMatcher::Prefix; name[--len] = '\0'; } - if (strlen(name) == 0) { + if (len == 0) { error_msg = "** Not a valid pattern"; return MethodMatcher::Any; } From d791f4b98d93e5fc64e3191402cc5091e0553592 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 28 Nov 2024 09:29:49 +0000 Subject: [PATCH 007/171] 8341585: Test java/foreign/TestUpcallStress.java should mark as /native Reviewed-by: luhenry, pminborg --- test/jdk/java/foreign/TestUpcallStress.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/foreign/TestUpcallStress.java b/test/jdk/java/foreign/TestUpcallStress.java index b7a53dd6c01..d910723b559 100644 --- a/test/jdk/java/foreign/TestUpcallStress.java +++ b/test/jdk/java/foreign/TestUpcallStress.java @@ -31,7 +31,7 @@ * @build NativeTestHelper CallGeneratorHelper TestUpcallBase * @bug 8337753 * - * @run testng/othervm/timeout=3200 + * @run testng/native/othervm/timeout=3200 * -Xcheck:jni * -XX:+IgnoreUnrecognizedVMOptions * -XX:-VerifyDependencies From 56f1e4ef0524515c7f1ad65bc3f08a0e8dd0a29a Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 28 Nov 2024 09:35:51 +0000 Subject: [PATCH 008/171] 8344093: Implement JEP 501: Deprecate the 32-bit x86 Port for Removal Reviewed-by: ihse, simonis, dholmes --- .github/workflows/main.yml | 22 +--------------------- doc/building.html | 7 +++---- doc/building.md | 6 ++---- make/autoconf/platform.m4 | 16 ++++++++-------- 4 files changed, 14 insertions(+), 37 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aed03f55536..210d53be658 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ on: platforms: description: 'Platform(s) to execute on (comma separated, e.g. "linux-x64, macos, aarch64")' required: true - default: 'linux-x64, linux-x86-hs, linux-x64-variants, linux-cross-compile, alpine-linux-x64, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs' + default: 'linux-x64, linux-x64-variants, linux-cross-compile, alpine-linux-x64, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs' configure-arguments: description: 'Additional configure arguments' required: false @@ -62,7 +62,6 @@ jobs: EXCLUDED_PLATFORMS: 'alpine-linux-x64' outputs: linux-x64: ${{ steps.include.outputs.linux-x64 }} - linux-x86-hs: ${{ steps.include.outputs.linux-x86-hs }} linux-x64-variants: ${{ steps.include.outputs.linux-x64-variants }} linux-cross-compile: ${{ steps.include.outputs.linux-cross-compile }} alpine-linux-x64: ${{ steps.include.outputs.alpine-linux-x64 }} @@ -145,7 +144,6 @@ jobs: } echo "linux-x64=$(check_platform linux-x64 linux x64)" >> $GITHUB_OUTPUT - echo "linux-x86-hs=$(check_platform linux-x86-hs linux x86)" >> $GITHUB_OUTPUT echo "linux-x64-variants=$(check_platform linux-x64-variants variants)" >> $GITHUB_OUTPUT echo "linux-cross-compile=$(check_platform linux-cross-compile cross-compile)" >> $GITHUB_OUTPUT echo "alpine-linux-x64=$(check_platform alpine-linux-x64 alpine-linux x64)" >> $GITHUB_OUTPUT @@ -170,24 +168,6 @@ jobs: make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.prepare.outputs.linux-x64 == 'true' - build-linux-x86-hs: - name: linux-x86-hs - needs: prepare - uses: ./.github/workflows/build-linux.yml - with: - platform: linux-x86 - make-target: 'hotspot' - gcc-major-version: '10' - gcc-package-suffix: '-multilib' - apt-architecture: 'i386' - # Some multilib libraries do not have proper inter-dependencies, so we have to - # install their dependencies manually. - apt-extra-packages: 'libfreetype-dev:i386 libtiff-dev:i386 libcupsimage2-dev:i386 libffi-dev:i386' - extra-conf-options: '--with-target-bits=32 --enable-fallback-linker --enable-libffi-bundling' - configure-arguments: ${{ github.event.inputs.configure-arguments }} - make-arguments: ${{ github.event.inputs.make-arguments }} - if: needs.prepare.outputs.linux-x86-hs == 'true' - build-linux-x64-hs-nopch: name: linux-x64-hs-nopch needs: prepare diff --git a/doc/building.html b/doc/building.html index 63af224584a..cd73863f879 100644 --- a/doc/building.html +++ b/doc/building.html @@ -329,8 +329,8 @@

Building on x86

Even for 32-bit builds, it is recommended to use a 64-bit build machine, and instead create a 32-bit target using --with-target-bits=32.

-

Note: The Windows 32-bit x86 port is deprecated and may be removed in -a future release.

+

Note: The 32-bit x86 port is deprecated and may be removed in a +future release.

Building on aarch64

At a minimum, a machine with 8 cores is advisable, as well as 8 GB of RAM. (The more cores to use, the more memory you need.) At least 6 GB of @@ -393,8 +393,7 @@

Operating System to list successes or failures of building on different platforms.

Windows

Windows XP is not a supported platform, but all newer Windows should -be able to build the JDK. (Note: The Windows 32-bit x86 port is -deprecated and may be removed in a future release.)

+be able to build the JDK.

On Windows, it is important that you pay attention to the instructions in the Special Considerations.

diff --git a/doc/building.md b/doc/building.md index 466e8d7edf8..99bc509dc70 100644 --- a/doc/building.md +++ b/doc/building.md @@ -134,8 +134,7 @@ space is required. Even for 32-bit builds, it is recommended to use a 64-bit build machine, and instead create a 32-bit target using `--with-target-bits=32`. -Note: The Windows 32-bit x86 port is deprecated and may be removed in a future -release. +Note: The 32-bit x86 port is deprecated and may be removed in a future release. ### Building on aarch64 @@ -191,8 +190,7 @@ on different platforms. ### Windows Windows XP is not a supported platform, but all newer Windows should be able to -build the JDK. (Note: The Windows 32-bit x86 port is deprecated and may be -removed in a future release.) +build the JDK. On Windows, it is important that you pay attention to the instructions in the [Special Considerations](#special-considerations). diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 3f977058a51..5b363e0704a 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -666,14 +666,14 @@ AC_DEFUN([PLATFORM_CHECK_DEPRECATION], [ AC_ARG_ENABLE(deprecated-ports, [AS_HELP_STRING([--enable-deprecated-ports@<:@=yes/no@:>@], [Suppress the error when configuring for a deprecated port @<:@no@:>@])]) - # if test "x$OPENJDK_TARGET_CPU" = xx86; then - # if test "x$enable_deprecated_ports" = "xyes"; then - # AC_MSG_WARN([The x86 port is deprecated and may be removed in a future release.]) - # else - # AC_MSG_ERROR(m4_normalize([The 32-bit x86 port is deprecated and may be removed in a future release. - # Use --enable-deprecated-ports=yes to suppress this error.])) - # fi - # fi + if test "x$OPENJDK_TARGET_CPU" = xx86; then + if test "x$enable_deprecated_ports" = "xyes"; then + AC_MSG_WARN([The 32-bit x86 port is deprecated and may be removed in a future release.]) + else + AC_MSG_ERROR(m4_normalize([The 32-bit x86 port is deprecated and may be removed in a future release. + Use --enable-deprecated-ports=yes to suppress this error.])) + fi + fi ]) AC_DEFUN_ONCE([PLATFORM_SETUP_OPENJDK_BUILD_OS_VERSION], From d33ad07c32f23aee799750c9964ab26d0cbe56f4 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Thu, 28 Nov 2024 09:54:25 +0000 Subject: [PATCH 009/171] 8334493: Remove SecurityManager Permissions infrastructure from DiagnosticCommands Reviewed-by: lmesnik, alanb, coleenp --- src/hotspot/os/linux/mallocInfoDcmd.hpp | 6 +- src/hotspot/os/linux/trimCHeapDCmd.hpp | 6 +- .../classfile/classLoaderHierarchyDCmd.hpp | 7 +- .../share/classfile/classLoaderStats.hpp | 6 - src/hotspot/share/jfr/dcmd/jfrDcmds.hpp | 30 +-- .../share/logging/logDiagnosticCommand.hpp | 5 - .../share/memory/metaspace/metaspaceDCmd.hpp | 7 +- src/hotspot/share/nmt/nmtDCmd.hpp | 7 +- .../share/services/diagnosticCommand.cpp | 3 +- .../share/services/diagnosticCommand.hpp | 175 ------------------ .../share/services/diagnosticFramework.cpp | 2 +- .../share/services/diagnosticFramework.hpp | 32 +--- src/hotspot/share/services/management.cpp | 4 - .../management/DiagnosticCommandMBean.java | 22 +-- .../internal/DiagnosticCommandImpl.java | 40 ---- .../internal/DiagnosticCommandInfo.java | 48 +---- .../libmanagement_ext/DiagnosticCommandImpl.c | 12 +- 17 files changed, 16 insertions(+), 396 deletions(-) diff --git a/src/hotspot/os/linux/mallocInfoDcmd.hpp b/src/hotspot/os/linux/mallocInfoDcmd.hpp index 9f52d83fba3..f2559fa1571 100644 --- a/src/hotspot/os/linux/mallocInfoDcmd.hpp +++ b/src/hotspot/os/linux/mallocInfoDcmd.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -41,10 +41,6 @@ class MallocInfoDcmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = { "java.lang.management.ManagementPermission", "monitor", nullptr }; - return p; - } void execute(DCmdSource source, TRAPS) override; }; diff --git a/src/hotspot/os/linux/trimCHeapDCmd.hpp b/src/hotspot/os/linux/trimCHeapDCmd.hpp index 32a49798ea8..53b94f4bb56 100644 --- a/src/hotspot/os/linux/trimCHeapDCmd.hpp +++ b/src/hotspot/os/linux/trimCHeapDCmd.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021 SAP SE. All rights reserved. - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -42,10 +42,6 @@ class TrimCLibcHeapDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = { "java.lang.management.ManagementPermission", "control", nullptr }; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; diff --git a/src/hotspot/share/classfile/classLoaderHierarchyDCmd.hpp b/src/hotspot/share/classfile/classLoaderHierarchyDCmd.hpp index 26c765443c9..cd89c9cb978 100644 --- a/src/hotspot/share/classfile/classLoaderHierarchyDCmd.hpp +++ b/src/hotspot/share/classfile/classLoaderHierarchyDCmd.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -46,11 +46,6 @@ class ClassLoaderHierarchyDCmd: public DCmdWithParser { static const char* impact() { return "Medium: Depends on number of class loaders and classes loaded."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } static int num_arguments() { return 3; } virtual void execute(DCmdSource source, TRAPS); diff --git a/src/hotspot/share/classfile/classLoaderStats.hpp b/src/hotspot/share/classfile/classLoaderStats.hpp index 4818ddff609..06d375b3e9b 100644 --- a/src/hotspot/share/classfile/classLoaderStats.hpp +++ b/src/hotspot/share/classfile/classLoaderStats.hpp @@ -58,12 +58,6 @@ class ClassLoaderStatsDCmd : public DCmd { static int num_arguments() { return 0; } - - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } }; diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp index 45e1addeb87..8e7dad5a20c 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -59,10 +59,6 @@ class JfrStartFlightRecordingDCmd : public JfrDCmd { static const char* impact() { return "Medium: Depending on the settings for a recording, the impact can range from low to high."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual const char* javaClass() const { return "jdk/jfr/internal/dcmd/DCmdStart"; } @@ -84,10 +80,6 @@ class JfrDumpFlightRecordingDCmd : public JfrDCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual const char* javaClass() const { return "jdk/jfr/internal/dcmd/DCmdDump"; } @@ -109,10 +101,6 @@ class JfrCheckFlightRecordingDCmd : public JfrDCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual const char* javaClass() const { return "jdk/jfr/internal/dcmd/DCmdCheck"; } @@ -134,10 +122,6 @@ class JfrStopFlightRecordingDCmd : public JfrDCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual const char* javaClass() const { return "jdk/jfr/internal/dcmd/DCmdStop"; } @@ -159,10 +143,6 @@ class JfrViewFlightRecordingDCmd : public JfrDCmd { static const char* impact() { return "Medium"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual const char* javaClass() const { return "jdk/jfr/internal/dcmd/DCmdView"; } @@ -184,10 +164,6 @@ class JfrQueryFlightRecordingDCmd : public JfrDCmd { static const char* impact() { return "Medium"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual const char* javaClass() const { return "jdk/jfr/internal/dcmd/DCmdQuery"; } @@ -225,10 +201,6 @@ class JfrConfigureFlightRecorderDCmd : public DCmdWithParser { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } static int num_arguments() { return 10; } virtual void execute(DCmdSource source, TRAPS); virtual void print_help(const char* name) const; diff --git a/src/hotspot/share/logging/logDiagnosticCommand.hpp b/src/hotspot/share/logging/logDiagnosticCommand.hpp index e63509bea9e..a27832306b7 100644 --- a/src/hotspot/share/logging/logDiagnosticCommand.hpp +++ b/src/hotspot/share/logging/logDiagnosticCommand.hpp @@ -58,11 +58,6 @@ class LogDiagnosticCommand : public DCmdWithParser { static const char* description() { return "Lists current log configuration, enables/disables/configures a log output, or rotates all logs."; } - - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "control", nullptr}; - return p; - } }; #endif // SHARE_LOGGING_LOGDIAGNOSTICCOMMAND_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp index 453b6721202..69377057950 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -52,11 +52,6 @@ class MetaspaceDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on number of classes loaded."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } static int num_arguments() { return 8; } virtual void execute(DCmdSource source, TRAPS); }; diff --git a/src/hotspot/share/nmt/nmtDCmd.hpp b/src/hotspot/share/nmt/nmtDCmd.hpp index c70d17a481f..2a9f9be49bb 100644 --- a/src/hotspot/share/nmt/nmtDCmd.hpp +++ b/src/hotspot/share/nmt/nmtDCmd.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -53,11 +53,6 @@ class NMTDCmd: public DCmdWithParser { static const char* impact() { return "Medium"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); private: diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index b807a42661c..b081ce29e26 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -151,8 +151,7 @@ void DCmd::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); // Enhanced JMX Agent Support - // These commands won't be exported via the DiagnosticCommandMBean until an - // appropriate permission is created for them + // These commands not currently exported via the DiagnosticCommandMBean uint32_t jmx_agent_export_flags = DCmd_Source_Internal | DCmd_Source_AttachAPI; DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(jmx_agent_export_flags, true,false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(jmx_agent_export_flags, true,false)); diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 92e5849cbcd..30b2be2a61b 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -64,11 +64,6 @@ class VersionDCmd : public DCmd { return "Print JVM version information."; } static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.util.PropertyPermission", - "java.vm.version", "read"}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -80,11 +75,6 @@ class CommandLineDCmd : public DCmd { return "Print the command line used to start this VM instance."; } static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS) { Arguments::print_on(_output); } @@ -101,11 +91,6 @@ class PrintSystemPropertiesDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.util.PropertyPermission", - "*", "read"}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -123,11 +108,6 @@ class PrintVMFlagsDCmd : public DCmdWithParser { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -146,11 +126,6 @@ class SetVMFlagDCmd : public DCmdWithParser { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -164,11 +139,6 @@ class JVMTIDataDumpDCmd : public DCmd { static const char* impact() { return "High"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -186,11 +156,6 @@ class JVMTIAgentLoadDCmd : public DCmdWithParser { return "Load JVMTI native agent."; } static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; #endif // INCLUDE_JVMTI @@ -208,11 +173,6 @@ class VMDynamicLibrariesDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -240,11 +200,6 @@ class VMInfoDCmd : public DCmd { return "Print information about JVM environment and status."; } static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -284,11 +239,6 @@ class HeapInfoDCmd : public DCmd { static const char* impact() { return "Medium"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -303,11 +253,6 @@ class FinalizerInfoDCmd : public DCmd { static const char* impact() { return "Medium"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -334,11 +279,6 @@ class HeapDumpDCmd : public DCmdWithParser { return "High: Depends on Java heap size and content. " "Request a full GC unless the '-all' option is specified."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; #endif // INCLUDE_SERVICES @@ -360,11 +300,6 @@ class ClassHistogramDCmd : public DCmdWithParser { static const char* impact() { return "High: Depends on Java heap size and content."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -387,11 +322,6 @@ class ClassHierarchyDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on number of loaded classes."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -412,11 +342,6 @@ class DumpSharedArchiveDCmd: public DCmdWithParser { static const char* impact() { return "Medium: Pause time depends on number of loaded classes"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; #endif // INCLUDE_CDS @@ -436,11 +361,6 @@ class ThreadDumpDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on the number of threads."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -546,12 +466,6 @@ class JMXStatusDCmd : public DCmd { return "Print the management agent status."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } - virtual void execute(DCmdSource source, TRAPS); }; @@ -568,11 +482,6 @@ class CompileQueueDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -592,11 +501,6 @@ class PerfMapDCmd : public DCmdWithParser { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; #endif // LINUX @@ -613,11 +517,6 @@ class CodeListDCmd : public DCmd { static const char* impact() { return "Medium"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -633,11 +532,6 @@ class CodeCacheDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -659,11 +553,6 @@ class CodeHeapAnalyticsDCmd : public DCmdWithParser { return "Low: Depends on code heap size and content. " "Holds CodeCache_lock during analysis step, usually sub-second duration."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; //---< END >--- CodeHeap State Analytics. @@ -680,11 +569,6 @@ class CompilerDirectivesPrintDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -700,11 +584,6 @@ class CompilerDirectivesRemoveDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -723,11 +602,6 @@ class CompilerDirectivesAddDCmd : public DCmdWithParser { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -743,11 +617,6 @@ class CompilerDirectivesClearDCmd : public DCmd { static const char* impact() { return "Low"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -809,11 +678,6 @@ class SymboltableDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on Java content."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -832,11 +696,6 @@ class StringtableDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on Java content."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -855,11 +714,6 @@ class SystemDictionaryDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on Java content."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -878,11 +732,6 @@ class ClassesDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on number of loaded classes."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -902,11 +751,6 @@ class EventLogDCmd : public DCmdWithParser { static const char* impact() { return "Low: Depends on event log size. "; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -929,10 +773,6 @@ class ThreadDumpToFileDCmd : public DCmdWithParser { static const char* impact() { return "Medium: Depends on the number of threads."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -952,11 +792,6 @@ class CompilationMemoryStatisticDCmd: public DCmdWithParser { static const char* impact() { return "Medium: Pause time depends on number of compiled methods"; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "monitor", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -970,11 +805,6 @@ class SystemMapDCmd : public DCmd { return "Prints an annotated process memory map of the VM process (linux and Windows only)."; } static const char* impact() { return "Medium; can be high for very large java heaps."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; @@ -988,11 +818,6 @@ class SystemDumpMapDCmd : public DCmdWithParser { return "Dumps an annotated process memory map to an output file (linux and Windows only)."; } static const char* impact() { return "Medium; can be high for very large java heaps."; } - static const JavaPermission permission() { - JavaPermission p = {"java.lang.management.ManagementPermission", - "control", nullptr}; - return p; - } virtual void execute(DCmdSource source, TRAPS); }; diff --git a/src/hotspot/share/services/diagnosticFramework.cpp b/src/hotspot/share/services/diagnosticFramework.cpp index 16f074c9cb5..27870d9d933 100644 --- a/src/hotspot/share/services/diagnosticFramework.cpp +++ b/src/hotspot/share/services/diagnosticFramework.cpp @@ -587,7 +587,7 @@ GrowableArray* DCmdFactory::DCmdInfo_list(DCmdSource source ) { if (!factory->is_hidden() && (factory->export_flags() & source)) { array->append(new DCmdInfo(factory->name(), factory->description(), factory->impact(), - factory->permission(), factory->num_arguments(), + factory->num_arguments(), factory->is_enabled())); } factory = factory->next(); diff --git a/src/hotspot/share/services/diagnosticFramework.hpp b/src/hotspot/share/services/diagnosticFramework.hpp index 357482ec5a1..16a7ecbe48e 100644 --- a/src/hotspot/share/services/diagnosticFramework.hpp +++ b/src/hotspot/share/services/diagnosticFramework.hpp @@ -40,16 +40,6 @@ enum DCmdSource { DCmd_Source_MBean = 0x04U // invocation via a MBean }; -// Warning: strings referenced by the JavaPermission struct are passed to -// the native part of the JDK. Avoid use of dynamically allocated strings -// that could be de-allocated before the JDK native code had time to -// convert them into Java Strings. -struct JavaPermission { - const char* _class; - const char* _name; - const char* _action; -}; - // CmdLine is the class used to handle a command line containing a single // diagnostic command and its arguments. It provides methods to access the // command name and the beginning of the arguments. The class is also @@ -127,23 +117,20 @@ class DCmdInfo : public ResourceObj { const char* const _name; /* Name of the diagnostic command */ const char* const _description; /* Short description */ const char* const _impact; /* Impact on the JVM */ - const JavaPermission _permission; /* Java Permission required to execute this command if any */ const int _num_arguments; /* Number of supported options or arguments */ const bool _is_enabled; /* True if the diagnostic command can be invoked, false otherwise */ public: DCmdInfo(const char* name, const char* description, const char* impact, - JavaPermission permission, int num_arguments, bool enabled) - : _name(name), _description(description), _impact(impact), _permission(permission), + : _name(name), _description(description), _impact(impact), _num_arguments(num_arguments), _is_enabled(enabled) {} const char* name() const { return _name; } bool name_equals(const char* cmd_name) const; const char* description() const { return _description; } const char* impact() const { return _impact; } - const JavaPermission& permission() const { return _permission; } int num_arguments() const { return _num_arguments; } bool is_enabled() const { return _is_enabled; } }; @@ -261,19 +248,6 @@ class DCmd : public AnyObj { // impact depends on the heap size. static const char* impact() { return "Low: No impact"; } - // The permission() method returns the description of Java Permission. This - // permission is required when the diagnostic command is invoked via the - // DiagnosticCommandMBean. The rationale for this permission check is that - // the DiagnosticCommandMBean can be used to perform remote invocations of - // diagnostic commands through the PlatformMBeanServer. The (optional) Java - // Permission associated with each diagnostic command should ease the work - // of system administrators to write policy files granting permissions to - // execute diagnostic commands to remote users. Any diagnostic command with - // a potential impact on security should overwrite this method. - static const JavaPermission permission() { - JavaPermission p = {nullptr, nullptr, nullptr}; - return p; - } // num_arguments() is used by the DCmdFactoryImpl::get_num_arguments() template functions. // All subclasses should override this to report the actual number of arguments. static int num_arguments() { return 0; } @@ -387,7 +361,6 @@ class DCmdFactory: public CHeapObj { virtual const char* name() const = 0; virtual const char* description() const = 0; virtual const char* impact() const = 0; - virtual const JavaPermission permission() const = 0; virtual const char* disabled_message() const = 0; // Register a DCmdFactory to make a diagnostic command available. // Once registered, a diagnostic command must not be unregistered. @@ -431,9 +404,6 @@ template class DCmdFactoryImpl : public DCmdFactory { const char* impact() const { return DCmdClass::impact(); } - const JavaPermission permission() const { - return DCmdClass::permission(); - } const char* disabled_message() const { return DCmdClass::disabled_message(); } diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 7729fb08808..8b8efc577cb 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -2013,10 +2013,6 @@ JVM_ENTRY(void, jmm_GetDiagnosticCommandInfo(JNIEnv *env, jobjectArray cmds, infoArray[i].name = info->name(); infoArray[i].description = info->description(); infoArray[i].impact = info->impact(); - JavaPermission p = info->permission(); - infoArray[i].permission_class = p._class; - infoArray[i].permission_name = p._name; - infoArray[i].permission_action = p._action; infoArray[i].num_arguments = info->num_arguments(); infoArray[i].enabled = info->is_enabled(); } diff --git a/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java b/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java index 4e64e17f45f..d5fedf93855 100644 --- a/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java +++ b/src/jdk.management/share/classes/com/sun/management/DiagnosticCommandMBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -138,26 +138,6 @@ * True if the diagnostic command is enabled, false otherwise * * - * dcmd.permissionClassString - * Some diagnostic command might require a specific permission to be - * executed, in addition to the MBeanPermission to invoke their - * associated MBean operation. This field returns the fully qualified - * name of the permission class or null if no permission is required - * - * - * - * dcmd.permissionNameString - * The fist argument of the permission required to execute this - * diagnostic command or null if no permission is required - * - * - * dcmd.permissionActionString - * The second argument of the permission required to execute this - * diagnostic command or null if the permission constructor has only - * one argument (like the ManagementPermission) or if no permission - * is required - * - * * dcmd.argumentsDescriptor * A Descriptor instance containing the descriptions of options and * arguments supported by the diagnostic command (see below) diff --git a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java index 6e456c7ad02..89c292b37d0 100644 --- a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java +++ b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java @@ -28,7 +28,6 @@ import com.sun.management.DiagnosticCommandMBean; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.security.Permission; import java.util.*; import javax.management.Attribute; import javax.management.AttributeList; @@ -104,48 +103,12 @@ private class Wrapper { String name; String cmd; DiagnosticCommandInfo info; - Permission permission; Wrapper(String name, String cmd, DiagnosticCommandInfo info) throws InstantiationException { this.name = name; this.cmd = cmd; this.info = info; - this.permission = null; - Exception cause = null; - if (info.getPermissionClass() != null) { - try { - Class c = Class.forName(info.getPermissionClass()); - if (info.getPermissionAction() == null) { - try { - Constructor constructor = c.getConstructor(String.class); - permission = (Permission) constructor.newInstance(info.getPermissionName()); - - } catch (InstantiationException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException ex) { - cause = ex; - } - } - if (permission == null) { - try { - Constructor constructor = c.getConstructor(String.class, String.class); - permission = (Permission) constructor.newInstance( - info.getPermissionName(), - info.getPermissionAction()); - } catch (InstantiationException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException ex) { - cause = ex; - } - } - } catch (ClassNotFoundException ex) { } - if (permission == null) { - InstantiationException iex = - new InstantiationException("Unable to instantiate required permission"); - iex.initCause(cause); - } - } } public String execute(String[] args) { @@ -297,9 +260,6 @@ private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException map.put("dcmd.name", w.info.getName()); map.put("dcmd.description", w.info.getDescription()); map.put("dcmd.vmImpact", w.info.getImpact()); - map.put("dcmd.permissionClass", w.info.getPermissionClass()); - map.put("dcmd.permissionName", w.info.getPermissionName()); - map.put("dcmd.permissionAction", w.info.getPermissionAction()); map.put("dcmd.enabled", w.info.isEnabled()); StringBuilder sb = new StringBuilder(); sb.append("help "); diff --git a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandInfo.java b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandInfo.java index 1184a40b64c..90632e5332d 100644 --- a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandInfo.java +++ b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -38,9 +38,6 @@ class DiagnosticCommandInfo { private final String name; private final String description; private final String impact; - private final String permissionClass; - private final String permissionName; - private final String permissionAction; private final boolean enabled; private final List arguments; @@ -73,43 +70,6 @@ String getImpact() { return impact; } - /** - * Returns the name of the permission class required to be allowed - * to invoke the diagnostic command, or null if no permission - * is required. - * - * @return the name of the permission class name required to be allowed - * to invoke the diagnostic command, or null if no permission - * is required - */ - String getPermissionClass() { - return permissionClass; - } - - /** - * Returns the permission name required to be allowed to invoke the - * diagnostic command, or null if no permission is required. - * - * @return the permission name required to be allowed to invoke the - * diagnostic command, or null if no permission is required - */ - String getPermissionName() { - return permissionName; - } - - /** - * Returns the permission action required to be allowed to invoke the - * diagnostic command, or null if no permission is required or - * if the permission has no action specified. - * - * @return the permission action required to be allowed to invoke the - * diagnostic command, or null if no permission is required or - * if the permission has no action specified - */ - String getPermissionAction() { - return permissionAction; - } - /** * Returns {@code true} if the diagnostic command is enabled, * {@code false} otherwise. The enabled/disabled @@ -134,17 +94,13 @@ List getArgumentsInfo() { } DiagnosticCommandInfo(String name, String description, - String impact, String permissionClass, - String permissionName, String permissionAction, + String impact, boolean enabled, List arguments) { this.name = name; this.description = description; this.impact = impact; - this.permissionClass = permissionClass; - this.permissionName = permissionName; - this.permissionAction = permissionAction; this.enabled = enabled; this.arguments = arguments; } diff --git a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c index b931c6e9893..6c0554a5c32 100644 --- a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c +++ b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -191,10 +191,9 @@ Java_com_sun_management_internal_DiagnosticCommandImpl_getDiagnosticCommandInfo } jmm_interface_management_ext->GetDiagnosticCommandInfo(env, commands, dcmd_info_array); for (i=0; i jname, jdesc, jimpact, cmd, args, obj - // 3 => permission class, name, action - (*env)->PushLocalFrame(env, 6 + 3); + (*env)->PushLocalFrame(env, 6); cmd = (*env)->GetObjectArrayElement(env, commands, i); args = getDiagnosticCommandArgumentInfoArray(env, @@ -218,11 +217,8 @@ Java_com_sun_management_internal_DiagnosticCommandImpl_getDiagnosticCommandInfo obj = JNU_NewObjectByName(env, "com/sun/management/internal/DiagnosticCommandInfo", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;)V", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;)V", jname, jdesc, jimpact, - dcmd_info_array[i].permission_class==NULL?NULL:(*env)->NewStringUTF(env,dcmd_info_array[i].permission_class), - dcmd_info_array[i].permission_name==NULL?NULL:(*env)->NewStringUTF(env,dcmd_info_array[i].permission_name), - dcmd_info_array[i].permission_action==NULL?NULL:(*env)->NewStringUTF(env,dcmd_info_array[i].permission_action), dcmd_info_array[i].enabled, args); if (obj == NULL) { From edfe28541a6ed94357f873aa69778c7eba707cbb Mon Sep 17 00:00:00 2001 From: Robbin Ehn Date: Thu, 28 Nov 2024 12:05:23 +0000 Subject: [PATCH 010/171] 8344306: RISC-V: Add zicond Reviewed-by: fyang, luhenry, mli --- src/hotspot/cpu/riscv/assembler_riscv.hpp | 32 ++++ .../cpu/riscv/c1_LIRAssembler_riscv.cpp | 1 + .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 46 +++++- .../cpu/riscv/c2_MacroAssembler_riscv.hpp | 1 - src/hotspot/cpu/riscv/globals_riscv.hpp | 1 + .../cpu/riscv/macroAssembler_riscv.cpp | 141 ++++++++++++++++++ .../cpu/riscv/macroAssembler_riscv.hpp | 11 ++ src/hotspot/cpu/riscv/vm_version_riscv.hpp | 4 + .../os_cpu/linux_riscv/riscv_hwprobe.cpp | 3 + .../gtest/riscv/test_assembler_riscv.cpp | 109 ++++++++++++++ 10 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/gtest/riscv/test_assembler_riscv.cpp diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 7334ec675e3..31713d7362a 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -3107,6 +3107,38 @@ enum Nf { #undef INSN +// -------------- Zicond Instruction Definitions -------------- +// Zicond conditional operations extension + private: + enum CZERO_OP : unsigned int { + CZERO_NEZ = 0b111, + CZERO_EQZ = 0b101 + }; + + template + void czero(Register Rd, Register Rs1, Register Rs2) { + assert_cond(UseZicond); + uint32_t insn = 0; + patch ((address)&insn, 6, 0, 0b0110011); // bits: 7, name: 0x33, attr: ['OP'] + patch_reg((address)&insn, 7, Rd); // bits: 5, name: 'rd' + patch ((address)&insn, 14, 12, OP_VALUE); // bits: 3, name: 0x7, attr: ['CZERO.NEZ'] / 0x5, attr: ['CZERO.EQZ']} + patch_reg((address)&insn, 15, Rs1); // bits: 5, name: 'rs1', attr: ['value'] + patch_reg((address)&insn, 20, Rs2); // bits: 5, name: 'rs2', attr: ['condition'] + patch ((address)&insn, 31, 25, 0b0000111); // bits: 7, name: 0x7, attr: ['CZERO'] + emit_int32(insn); + } + + public: + // Moves zero to a register rd, if the condition rs2 is equal to zero, otherwise moves rs1 to rd. + void czero_eqz(Register rd, Register rs1_value, Register rs2_condition) { + czero(rd, rs1_value, rs2_condition); + } + + // Moves zero to a register rd, if the condition rs2 is nonzero, otherwise moves rs1 to rd. + void czero_nez(Register rd, Register rs1_value, Register rs2_condition) { + czero(rd, rs1_value, rs2_condition); + } + // -------------- ZCB Instruction Definitions -------------- // Zcb additional C instructions private: diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index eb715227f7e..b7edd3d231f 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -870,6 +870,7 @@ void LIR_Assembler::emit_op3(LIR_Op3* op) { } } +// Consider using cmov (Zicond) void LIR_Assembler::cmove(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Opr result, BasicType type, LIR_Opr cmp_opr1, LIR_Opr cmp_opr2) { Label label; diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 49efb619093..2125f67cf9d 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2003,10 +2003,48 @@ void C2_MacroAssembler::enc_cmpEqNe_imm0_branch(int cmpFlag, Register op1, Label } void C2_MacroAssembler::enc_cmove(int cmpFlag, Register op1, Register op2, Register dst, Register src) { - Label L; - cmp_branch(cmpFlag ^ (1 << neg_cond_bits), op1, op2, L); - mv(dst, src); - bind(L); + bool is_unsigned = (cmpFlag & unsigned_branch_mask) == unsigned_branch_mask; + int op_select = cmpFlag & (~unsigned_branch_mask); + + switch (op_select) { + case BoolTest::eq: + cmov_eq(op1, op2, dst, src); + break; + case BoolTest::ne: + cmov_ne(op1, op2, dst, src); + break; + case BoolTest::le: + if (is_unsigned) { + cmov_leu(op1, op2, dst, src); + } else { + cmov_le(op1, op2, dst, src); + } + break; + case BoolTest::ge: + if (is_unsigned) { + cmov_geu(op1, op2, dst, src); + } else { + cmov_ge(op1, op2, dst, src); + } + break; + case BoolTest::lt: + if (is_unsigned) { + cmov_ltu(op1, op2, dst, src); + } else { + cmov_lt(op1, op2, dst, src); + } + break; + case BoolTest::gt: + if (is_unsigned) { + cmov_gtu(op1, op2, dst, src); + } else { + cmov_gt(op1, op2, dst, src); + } + break; + default: + assert(false, "unsupported compare condition"); + ShouldNotReachHere(); + } } // Set dst to NaN if any NaN input. diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index 2d14f98780d..a8eb0df419c 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -98,7 +98,6 @@ // refer to conditional_branches and float_conditional_branches static const int bool_test_bits = 3; - static const int neg_cond_bits = 2; static const int unsigned_branch_mask = 1 << bool_test_bits; static const int double_branch_mask = 1 << bool_test_bits; diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index ffbb7c58911..6772fae50ca 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -110,6 +110,7 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \ product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \ product(bool, UseZicboz, false, EXPERIMENTAL, "Use Zicboz instructions") \ + product(bool, UseZicond, false, DIAGNOSTIC, "Use Zicond instructions") \ product(bool, UseZihintpause, false, EXPERIMENTAL, \ "Use Zihintpause instructions") \ product(bool, UseZtso, false, EXPERIMENTAL, "Assume Ztso memory model") \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index cf3c851a7ec..c39a086838d 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1128,6 +1128,147 @@ void MacroAssembler::wrap_label(Register r1, Register r2, Label &L, #undef INSN +// cmov +void MacroAssembler::cmov_eq(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + xorr(t0, cmp1, cmp2); + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bne(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_ne(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + xorr(t0, cmp1, cmp2); + czero_nez(dst, dst, t0); + czero_eqz(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + beq(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_le(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + slt(t0, cmp2, cmp1); + czero_eqz(dst, dst, t0); + czero_nez(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bgt(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_leu(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + sltu(t0, cmp2, cmp1); + czero_eqz(dst, dst, t0); + czero_nez(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bgtu(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_ge(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + slt(t0, cmp1, cmp2); + czero_eqz(dst, dst, t0); + czero_nez(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + blt(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_geu(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + sltu(t0, cmp1, cmp2); + czero_eqz(dst, dst, t0); + czero_nez(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bltu(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_lt(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + slt(t0, cmp1, cmp2); + czero_nez(dst, dst, t0); + czero_eqz(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bge(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_ltu(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + sltu(t0, cmp1, cmp2); + czero_nez(dst, dst, t0); + czero_eqz(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bgeu(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_gt(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + slt(t0, cmp2, cmp1); + czero_nez(dst, dst, t0); + czero_eqz(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + ble(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + +void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Register src) { + if (UseZicond) { + sltu(t0, cmp2, cmp1); + czero_nez(dst, dst, t0); + czero_eqz(t0, src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + bleu(cmp1, cmp2, no_set); + mv(dst, src); + bind(no_set); +} + // Float compare branch instructions #define INSN(NAME, FLOATCMP, BRANCH) \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 54f7127106b..cbf69e93c5c 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -626,6 +626,17 @@ class MacroAssembler: public Assembler { void bltz(Register Rs, const address dest); void bgtz(Register Rs, const address dest); + void cmov_eq(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_ne(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_le(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_leu(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_ge(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_geu(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_lt(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_ltu(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_gt(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_gtu(Register cmp1, Register cmp2, Register dst, Register src); + public: // We try to follow risc-v asm menomics. // But as we don't layout a reachable GOT, diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 8fdde0094f4..e08838c3a6f 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -116,6 +116,8 @@ class VM_Version : public Abstract_VM_Version { // // Zfh Half-Precision Floating-Point instructions // + // Zicond Conditional operations + // // Zicsr Control and Status Register (CSR) Instructions // Zifencei Instruction-Fetch Fence // Zic64b Cache blocks must be 64 bytes in size, naturally aligned in the address space. @@ -164,6 +166,7 @@ class VM_Version : public Abstract_VM_Version { decl(ext_Zvbb , "Zvbb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvbb)) \ decl(ext_Zvfh , "Zvfh" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvfh)) \ decl(ext_Zvkn , "Zvkn" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZvkn)) \ + decl(ext_Zicond , "Zicond" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \ decl(mvendorid , "VendorId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ decl(marchid , "ArchId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ decl(mimpid , "ImpId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \ @@ -223,6 +226,7 @@ class VM_Version : public Abstract_VM_Version { RV_ENABLE_EXTENSION(UseZicbom) \ RV_ENABLE_EXTENSION(UseZicbop) \ RV_ENABLE_EXTENSION(UseZicboz) \ + RV_ENABLE_EXTENSION(UseZicond) \ RV_ENABLE_EXTENSION(UseZihintpause) \ static void useRVA23U64Profile(); diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 2020e2fdb24..f785d935393 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -181,6 +181,9 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) { VM_Version::ext_Zvfh.enable_feature(); } + if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICOND)) { + VM_Version::ext_Zicond.enable_feature(); + } if (is_valid(RISCV_HWPROBE_KEY_CPUPERF_0)) { VM_Version::unaligned_access.enable_feature( query[RISCV_HWPROBE_KEY_CPUPERF_0].value & RISCV_HWPROBE_MISALIGNED_MASK); diff --git a/test/hotspot/gtest/riscv/test_assembler_riscv.cpp b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp new file mode 100644 index 00000000000..152b997b3c8 --- /dev/null +++ b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Rivos Inc. 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" + +#if (defined(RISCV) || defined(RISCV64)) && !defined(ZERO) + +#include "asm/assembler.inline.hpp" +#include "asm/macroAssembler.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/orderAccess.hpp" +#include "unittest.hpp" + +typedef int64_t (*zicond_func)(int64_t cmp1, int64_t cmp2, int64_t dst, int64_t src); +typedef void (MacroAssembler::*cmov_func)(Register cmp1, Register cmp2, Register dst, Register src); + +class CmovTester { + public: + static void test(cmov_func func, int64_t a0, int64_t a1, int64_t a2, int64_t a3, int64_t result) { + BufferBlob* bb = BufferBlob::create("riscvTest", 128); + CodeBuffer code(bb); + MacroAssembler _masm(&code); + address entry = _masm.pc(); + { + ((&_masm)->*func)(c_rarg0, c_rarg1, c_rarg2, c_rarg3); + _masm.mv(c_rarg0, c_rarg2); + _masm.ret(); + } + _masm.flush(); + OrderAccess::cross_modify_fence(); + int64_t ret = ((zicond_func)entry)(a0, a1, a2, a3); + ASSERT_EQ(ret, result); + BufferBlob::free(bb); + } +}; + +void run_cmov_tests() { + // If 42(a0) eq 42(a1): assign dest(a2/66) the src(a3/77), expect result: 77 + CmovTester::test(&MacroAssembler::cmov_eq, 42, 42, 66, 77, 77); + // If 41(a0) eq 42(a1): assign dest(a2/66) the src(a3/77), expect result: 66 + CmovTester::test(&MacroAssembler::cmov_eq, 41, 42, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_ne, 41, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_ne, 42, 42, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_le, 41, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_le, 42, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_le, 42, -1, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_leu, 41, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_leu, 42, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_leu, -1, 42, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_ge, 43, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_ge, 42, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_ge, -1, 42, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_geu, 43, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_geu, 42, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_geu, 42, -1, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_lt, 41, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_lt, 42, 42, 66, 77, 66); + CmovTester::test(&MacroAssembler::cmov_lt, 42, -1, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_ltu, 41, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_ltu, 42, 42, 66, 77, 66); + CmovTester::test(&MacroAssembler::cmov_ltu, -1, 42, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_gt, 43, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_gt, 42, 42, 66, 77, 66); + CmovTester::test(&MacroAssembler::cmov_gt, -1, 42, 66, 77, 66); + + CmovTester::test(&MacroAssembler::cmov_gtu, 43, 42, 66, 77, 77); + CmovTester::test(&MacroAssembler::cmov_gtu, 42, 42, 66, 77, 66); + CmovTester::test(&MacroAssembler::cmov_gtu, 42, -1, 66, 77, 66); +} + +TEST_VM(RiscV, cmov) { + run_cmov_tests(); + if (UseZicond) { + UseZicond = false; + run_cmov_tests(); + UseZicond = true; + } +} + +#endif // RISCV From db535c86bc56b89b7213b3b097d80935fe9e8516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Schejbal?= Date: Thu, 28 Nov 2024 13:12:45 +0000 Subject: [PATCH 011/171] 8313367: SunMSCAPI cannot read Local Computer certs w/o Windows elevation Reviewed-by: weijun --- .../windows/native/libsunmscapi/security.cpp | 12 ++++++--- test/jdk/sun/security/mscapi/AllTypes.java | 27 +++---------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp index 4787708779d..747db000158 100644 --- a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp +++ b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -444,7 +444,7 @@ JNIEXPORT void JNICALL Java_sun_security_mscapi_CKeyStore_loadKeysOrCertificateC } else if (jCertStoreLocation == KEYSTORE_LOCATION_LOCALMACHINE) { hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, NULL, - CERT_SYSTEM_STORE_LOCAL_MACHINE, pszCertStoreName); + CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_MAXIMUM_ALLOWED_FLAG, pszCertStoreName); } else { PP("jCertStoreLocation is not a valid value"); @@ -798,11 +798,15 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_CSignature_signHash ::CryptGetProvParam((HCRYPTPROV)hCryptProv, PP_CONTAINER, //deprecated (BYTE *)pbData, &cbData, 0); + DWORD keysetType = 0; + DWORD keysetTypeLen = sizeof(keysetType); + ::CryptGetProvParam((HCRYPTPROV)hCryptProv, PP_KEYSET_TYPE, //deprecated + (BYTE*)&keysetType, &keysetTypeLen, 0); + // Acquire an alternative CSP handle if (::CryptAcquireContext(&hCryptProvAlt, LPCSTR(pbData), NULL, //deprecated - PROV_RSA_AES, 0) == FALSE) + PROV_RSA_AES, 0 | keysetType) == FALSE) { - ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); __leave; } diff --git a/test/jdk/sun/security/mscapi/AllTypes.java b/test/jdk/sun/security/mscapi/AllTypes.java index f9c98860702..9f5fb2f13d6 100644 --- a/test/jdk/sun/security/mscapi/AllTypes.java +++ b/test/jdk/sun/security/mscapi/AllTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -45,33 +45,12 @@ public static void main(String[] args) throws Exception { var nr = test("windows-root"); var nmu = test("windows-my-currentuser"); var nru = test("windows-root-currentuser"); - var hasAdminPrivileges = detectIfRunningWithAdminPrivileges(); - var nmm = adminTest("windows-my-localmachine", hasAdminPrivileges); - var nrm = adminTest("windows-root-localmachine", hasAdminPrivileges); + var nmm = test("windows-my-localmachine"); + var nrm = test("windows-root-localmachine"); Asserts.assertEQ(nm, nmu); Asserts.assertEQ(nr, nru); } - private static boolean detectIfRunningWithAdminPrivileges() { - try { - Process p = Runtime.getRuntime().exec("reg query \"HKU\\S-1-5-19\""); - p.waitFor(); - return (p.exitValue() == 0); - } - catch (Exception ex) { - System.out.println("Warning: unable to detect admin privileges, assuming none"); - return false; - } - } - - private static List adminTest(String type, boolean hasAdminPrivileges) throws Exception { - if (hasAdminPrivileges) { - return test(type); - } - System.out.println("Ignoring: " + type + " as it requires admin privileges"); - return null; - } - private static List test(String type) throws Exception { var stdType = "Windows-" + type.substring(8).toUpperCase(Locale.ROOT); SecurityTools.keytool("-storetype " + type + " -list") From 1e086b1d7305769b59271e2fa428c003216dd52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Thu, 28 Nov 2024 13:15:10 +0000 Subject: [PATCH 012/171] 8340103: Add internal set_flag function to VMATree Reviewed-by: stuefe, azafari, gziemski --- src/hotspot/share/nmt/nmtTreap.hpp | 36 +++++ src/hotspot/share/nmt/vmatree.cpp | 105 +++++++++++++- src/hotspot/share/nmt/vmatree.hpp | 55 +++++-- test/hotspot/gtest/nmt/test_vmatree.cpp | 184 +++++++++++++++++++++++- 4 files changed, 366 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/nmt/nmtTreap.hpp b/src/hotspot/share/nmt/nmtTreap.hpp index 70063497439..b6be654f127 100644 --- a/src/hotspot/share/nmt/nmtTreap.hpp +++ b/src/hotspot/share/nmt/nmtTreap.hpp @@ -234,6 +234,10 @@ class Treap { this->remove_all(); } + int size() { + return _node_count; + } + void upsert(const K& k, const V& v) { TreapNode* found = find(_root, k); if (found != nullptr) { @@ -304,6 +308,38 @@ class Treap { return candidate; } + TreapNode* closest_gt(const K& key) { + TreapNode* candidate = nullptr; + TreapNode* pos = _root; + while (pos != nullptr) { + int cmp_r = COMPARATOR::cmp(pos->key(), key); + if (cmp_r > 0) { + // Found a match, try to find a better one. + candidate = pos; + pos = pos->_left; + } else if (cmp_r <= 0) { + pos = pos->_right; + } + } + return candidate; + } + + struct Range { + TreapNode* start; + TreapNode* end; + Range(TreapNode* start, TreapNode* end) + : start(start), end(end) {} + }; + + // Return the range [start, end) + // where start->key() <= addr < end->key(). + // Failure to find the range leads to start and/or end being null. + Range find_enclosing_range(K addr) { + TreapNode* start = closest_leq(addr); + TreapNode* end = closest_gt(addr); + return Range(start, end); + } + // Visit all TreapNodes in ascending key order. template void visit_in_order(F f) const { diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp index 65a5bdb94ae..ec4f405f1c9 100644 --- a/src/hotspot/share/nmt/vmatree.cpp +++ b/src/hotspot/share/nmt/vmatree.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "logging/log.hpp" #include "nmt/vmatree.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::StackIndex{}, mtNone}; @@ -82,12 +83,12 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType // Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A. // Consider the case where the found node is the start of a region enclosing [A,B) - stB.out = leqA_n->val().out; + stB.out = out_state(leqA_n); // Direct address match. if (leqA_n->key() == A) { // Take over in state from old address. - stA.in = leqA_n->val().in; + stA.in = in_state(leqA_n); // We may now be able to merge two regions: // If the node's old state matches the new, it becomes a noop. That happens, for example, @@ -113,7 +114,7 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType // We add a new node, but only if there would be a state change. If there would not be a // state change, we just omit the node. // That happens, for example, when reserving within an already reserved region with identical metadata. - stA.in = leqA_n->val().out; // .. and the region's prior state is the incoming state + stA.in = out_state(leqA_n); // .. and the region's prior state is the incoming state if (stA.is_noop()) { // Nothing to do. } else { @@ -134,7 +135,7 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType // outgoing state. _tree.visit_range_in_order(A + 1, B + 1, [&](TreapNode* head) { int cmp_B = PositionComparator::cmp(head->key(), B); - stB.out = head->val().out; + stB.out = out_state(head); if (cmp_B < 0) { // Record all nodes preceding B. to_be_deleted_inbetween_a_b.push({head->key(), head->val()}); @@ -215,3 +216,99 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType } return diff; } + +#ifdef ASSERT +void VMATree::print_on(outputStream* out) { + visit_in_order([&](TreapNode* current) { + out->print(SIZE_FORMAT " (%s) - %s - ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()), + statetype_to_string(out_state(current).type())); + }); + out->cr(); +} +#endif + +VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, const MemTag tag) { + auto pos = [](TreapNode* n) { return n->key(); }; + position from = start; + position end = from+size; + size_t remsize = size; + VMATreap::Range range(nullptr, nullptr); + + // Find the next range to adjust and set range, remsize and from + // appropriately. If it returns false, there is no valid next range. + auto find_next_range = [&]() -> bool { + range = _tree.find_enclosing_range(from); + if ((range.start == nullptr && range.end == nullptr) || + (range.start != nullptr && range.end == nullptr)) { + // There is no range containing the starting address + assert(range.start->val().out.type() == StateType::Released, "must be"); + return false; + } else if (range.start == nullptr && range.end != nullptr) { + position found_end = pos(range.end); + if (found_end >= end) { + // The found address is outside of our range, we can end now. + return false; + } + // There is at least one range [found_end, ?) which starts within [start, end) + // Use this as the range instead. + range = _tree.find_enclosing_range(found_end); + remsize = end - found_end; + from = found_end; + } + return true; + }; + + bool success = find_next_range(); + if (!success) return SummaryDiff(); + assert(range.start != nullptr && range.end != nullptr, "must be"); + + end = MIN2(from + remsize, pos(range.end)); + IntervalState& out = out_state(range.start); + StateType type = out.type(); + + SummaryDiff diff; + // Ignore any released ranges, these must be mtNone and have no stack + if (type != StateType::Released) { + RegionData new_data = RegionData(out.stack(), tag); + SummaryDiff result = register_mapping(from, end, type, new_data); + diff.add(result); + } + + remsize = remsize - (end - from); + from = end; + + // If end < from + sz then there are multiple ranges for which to set the flag. + while (end < from + remsize) { + // Using register_mapping may invalidate the already found range, so we must + // use find_next_range repeatedly + bool success = find_next_range(); + if (!success) return diff; + assert(range.start != nullptr && range.end != nullptr, "must be"); + + end = MIN2(from + remsize, pos(range.end)); + IntervalState& out = out_state(range.start); + StateType type = out.type(); + + if (type != StateType::Released) { + RegionData new_data = RegionData(out.stack(), tag); + SummaryDiff result = register_mapping(from, end, type, new_data); + diff.add(result); + } + remsize = remsize - (end - from); + from = end; + } + + return diff; +} + +#ifdef ASSERT +void VMATree::SummaryDiff::print_on(outputStream* out) { + for (int i = 0; i < mt_number_of_tags; i++) { + if (tag[i].reserve == 0 && tag[i].commit == 0) { + continue; + } + out->print_cr("Tag %s R: " INT64_FORMAT " C: " INT64_FORMAT, NMTUtil::tag_to_enum_name((MemTag)i), tag[i].reserve, + tag[i].commit); + } +} +#endif diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp index cfb3c8ab524..75f814c81c0 100644 --- a/src/hotspot/share/nmt/vmatree.hpp +++ b/src/hotspot/share/nmt/vmatree.hpp @@ -26,10 +26,11 @@ #ifndef SHARE_NMT_VMATREE_HPP #define SHARE_NMT_VMATREE_HPP +#include "nmt/memTag.hpp" #include "nmt/nmtNativeCallStackStorage.hpp" #include "nmt/nmtTreap.hpp" -#include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" #include // A VMATree stores a sequence of points on the natural number line. @@ -42,6 +43,7 @@ class VMATree { // A position in memory. public: using position = size_t; + using size = size_t; class PositionComparator { public: @@ -140,6 +142,14 @@ class VMATree { private: VMATreap _tree; + static IntervalState& in_state(TreapNode* node) { + return node->val().in; + } + + static IntervalState& out_state(TreapNode* node) { + return node->val().out; + } + // AddressState saves the necessary information for performing online summary accounting. struct AddressState { position address; @@ -162,6 +172,7 @@ class VMATree { delta reserve; delta commit; }; + struct SummaryDiff { SingleDiff tag[mt_number_of_tags]; SummaryDiff() { @@ -169,26 +180,47 @@ class VMATree { tag[i] = SingleDiff{0, 0}; } } + + void add(SummaryDiff& other) { + for (int i = 0; i < mt_number_of_tags; i++) { + tag[i].reserve += other.tag[i].reserve; + tag[i].commit += other.tag[i].commit; + } + } + +#ifdef ASSERT + void print_on(outputStream* out); +#endif }; private: SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false); public: - SummaryDiff reserve_mapping(position from, position sz, const RegionData& metadata) { - return register_mapping(from, from + sz, StateType::Reserved, metadata, false); + SummaryDiff reserve_mapping(position from, size size, const RegionData& metadata) { + return register_mapping(from, from + size, StateType::Reserved, metadata, false); + } + + SummaryDiff commit_mapping(position from, size size, const RegionData& metadata, bool use_tag_inplace = false) { + return register_mapping(from, from + size, StateType::Committed, metadata, use_tag_inplace); } - SummaryDiff commit_mapping(position from, position sz, const RegionData& metadata, bool use_tag_inplace = false) { - return register_mapping(from, from + sz, StateType::Committed, metadata, use_tag_inplace); + // Given an interval and a tag, find all reserved and committed ranges at least + // partially contained within that interval and set their tag to the one provided. + // This may cause merging and splitting of ranges. + // Released regions are ignored. + SummaryDiff set_tag(position from, size size, MemTag tag); + + SummaryDiff uncommit_mapping(position from, size size, const RegionData& metadata) { + return register_mapping(from, from + size, StateType::Reserved, metadata, true); } - SummaryDiff uncommit_mapping(position from, position sz, const RegionData& metadata) { - return register_mapping(from, from + sz, StateType::Reserved, metadata, true); + SummaryDiff release_mapping(position from, size size) { + return register_mapping(from, from + size, StateType::Released, VMATree::empty_regiondata); } - SummaryDiff release_mapping(position from, position sz) { - return register_mapping(from, from + sz, StateType::Released, VMATree::empty_regiondata); + VMATreap& tree() { + return _tree; } public: @@ -196,6 +228,11 @@ class VMATree { void visit_in_order(F f) const { _tree.visit_in_order(f); } + +#ifdef ASSERT + void print_on(outputStream* out); +#endif + }; #endif diff --git a/test/hotspot/gtest/nmt/test_vmatree.cpp b/test/hotspot/gtest/nmt/test_vmatree.cpp index d7a9a5fcff7..3a61e0c7cca 100644 --- a/test/hotspot/gtest/nmt/test_vmatree.cpp +++ b/test/hotspot/gtest/nmt/test_vmatree.cpp @@ -284,6 +284,188 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { } } +TEST_VM_F(NMTVMATreeTest, SetTag) { + using State = VMATree::StateType; + struct testrange { + VMATree::position from; + VMATree::position to; + MemTag tag; + NCS::StackIndex stack; + State state; + }; + + // Take a sorted list of testranges and check that those and only those are found in the tree. + auto expect_equivalent_form = [&](auto& expected, VMATree& tree) { + // With auto& our arrays do not deteriorate to pointers but are kept as testrange[N] + // so this actually works! + int len = sizeof(expected) / sizeof(testrange); + VMATree::position previous_to = 0; + for (int i = 0; i < len; i++) { + testrange expect = expected[i]; + assert(previous_to == 0 || previous_to <= expect.from, "the expected list must be sorted"); + previous_to = expect.to; + + VMATree::VMATreap::Range found = tree.tree().find_enclosing_range(expect.from); + ASSERT_NE(nullptr, found.start); + ASSERT_NE(nullptr, found.end); + // Same region + EXPECT_EQ(expect.from, found.start->key()); + EXPECT_EQ(expect.to, found.end->key()); + // Same tag + EXPECT_EQ(expect.tag, found.start->val().out.mem_tag()); + EXPECT_EQ(expect.tag, found.end->val().in.mem_tag()); + // Same stack + EXPECT_EQ(expect.stack, found.start->val().out.stack()); + EXPECT_EQ(expect.stack, found.end->val().in.stack()); + // Same state + EXPECT_EQ(expect.state, found.start->val().out.type()); + EXPECT_EQ(expect.state, found.end->val().in.type()); + } + // expected must cover all nodes + EXPECT_EQ(len+1, tree.tree().size()); + }; + NCS::StackIndex si = NCS::StackIndex(); + Tree::RegionData rd(si, mtNone); + + { // The gc/cds case with only reserved data + testrange expected[2]{ + { 0, 500, mtGC, si, State::Reserved}, + {500, 600, mtClassShared, si, State::Reserved} + }; + VMATree tree; + + tree.reserve_mapping(0, 600, rd); + + tree.set_tag(0, 500, mtGC); + tree.set_tag(500, 100, mtClassShared); + expect_equivalent_form(expected, tree); + } + + { // Now let's add in some committed data + testrange expected[]{ + { 0, 100, mtGC, si, State::Reserved}, + {100, 225, mtGC, si, State::Committed}, + {225, 500, mtGC, si, State::Reserved}, + {500, 550, mtClassShared, si, State::Reserved}, + {550, 560, mtClassShared, si, State::Committed}, + {560, 565, mtClassShared, si, State::Reserved}, + {565, 575, mtClassShared, si, State::Committed}, + {575, 600, mtClassShared, si, State::Reserved} + }; + VMATree tree; + + tree.reserve_mapping(0, 600, rd); + // The committed areas + tree.commit_mapping(100, 125, rd); + tree.commit_mapping(550, 10, rd); + tree.commit_mapping(565, 10, rd); + // OK, set tag + tree.set_tag(0, 500, mtGC); + tree.set_tag(500, 100, mtClassShared); + expect_equivalent_form(expected, tree); + } + + { // Setting the tag for adjacent regions with same stacks should merge the regions + testrange expected[]{ + {0, 200, mtGC, si, State::Reserved} + }; + VMATree tree; + Tree::RegionData gc(si, mtGC); + Tree::RegionData compiler(si, mtCompiler); + tree.reserve_mapping(0, 100, gc); + tree.reserve_mapping(100, 100, compiler); + tree.set_tag(0, 200, mtGC); + expect_equivalent_form(expected, tree); + } + + { // Setting the tag for adjacent regions with different stacks should NOT merge the regions + NCS::StackIndex si1 = 1; + NCS::StackIndex si2 = 2; + testrange expected[]{ + { 0, 100, mtGC, si1, State::Reserved}, + {100, 200, mtGC, si2, State::Reserved} + }; + VMATree tree; + Tree::RegionData gc(si1, mtGC); + Tree::RegionData compiler(si2, mtCompiler); + tree.reserve_mapping(0, 100, gc); + tree.reserve_mapping(100, 100, compiler); + tree.set_tag(0, 200, mtGC); + expect_equivalent_form(expected, tree); + } + + { // Setting the tag in the middle of a range causes a split + testrange expected[]{ + { 0, 100, mtCompiler, si, State::Reserved}, + {100, 150, mtGC, si, State::Reserved}, + {150, 200, mtCompiler, si, State::Reserved} + }; + VMATree tree; + Tree::RegionData compiler(si, mtCompiler); + tree.reserve_mapping(0, 200, compiler); + tree.set_tag(100, 50, mtGC); + expect_equivalent_form(expected, tree); + } + + { // Setting the tag in between two ranges causes a split + testrange expected[]{ + { 0, 75, mtGC, si, State::Reserved}, + { 75, 125, mtClass, si, State::Reserved}, + {125, 200, mtCompiler, si, State::Reserved}, + }; + VMATree tree; + Tree::RegionData gc(si, mtGC); + Tree::RegionData compiler(si, mtCompiler); + tree.reserve_mapping(0, 100, gc); + tree.reserve_mapping(100, 100, compiler); + tree.set_tag(75, 50, mtClass); + expect_equivalent_form(expected, tree); + } + + { // Holes in the address range are acceptable and untouched + testrange expected[]{ + { 0, 50, mtGC, si, State::Reserved}, + {50, 75, mtNone, si, State::Released}, + {75, 80, mtGC, si, State::Reserved}, + {80, 100, mtClassShared, si, State::Reserved} + }; + VMATree tree; + Tree::RegionData class_shared(si, mtClassShared); + tree.reserve_mapping(0, 50, class_shared); + tree.reserve_mapping(75, 25, class_shared); + tree.set_tag(0, 80, mtGC); + expect_equivalent_form(expected, tree); + } + + { // Check that setting tag with 'hole' not consisting of any regions work + testrange expected[]{ + {10, 20, mtCompiler, si, State::Reserved} + }; + VMATree tree; + Tree::RegionData class_shared(si, mtClassShared); + tree.reserve_mapping(10, 10, class_shared); + tree.set_tag(0, 100, mtCompiler); + expect_equivalent_form(expected, tree); + } + + { // Check that multiple holes still work + testrange expected[]{ + { 0, 1, mtGC, si, State::Reserved}, + { 1, 50, mtNone, si, State::Released}, + {50, 75, mtGC, si, State::Reserved}, + {75, 99, mtNone, si, State::Released}, + {99, 100, mtGC, si, State::Reserved} + }; + VMATree tree; + Tree::RegionData class_shared(si, mtClassShared); + tree.reserve_mapping(0, 100, class_shared); + tree.release_mapping(1, 49); + tree.release_mapping(75, 24); + tree.set_tag(0, 100, mtGC); + expect_equivalent_form(expected, tree); + } +} + // Tests for summary accounting TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { { // Fully enclosed re-reserving works correctly. @@ -330,7 +512,7 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(100, diff.reserve); } - { // Adjacent reserved mappings with different flags + { // Adjacent reserved mappings with different tags Tree::RegionData rd(NCS::StackIndex(), mtTest); Tree::RegionData rd2(NCS::StackIndex(), mtNMT); Tree tree; From 7dc00d39b4e184a59cbcd644d22db61b1abe8a4b Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Thu, 28 Nov 2024 13:58:14 +0000 Subject: [PATCH 013/171] 8345154: IGV: Show Parse and Assertion Predicate type as extra label Reviewed-by: rcastanedalo --- src/hotspot/share/opto/ifnode.cpp | 4 ++-- .../filters/customNodeInfo.filter | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index c0ed3f7e5d4..677cf5b7d60 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -2213,10 +2213,10 @@ void ParsePredicateNode::dump_spec(outputStream* st) const { st->print("Loop "); break; case Deoptimization::DeoptReason::Reason_profile_predicate: - st->print("Profiled_Loop "); + st->print("Profiled Loop "); break; case Deoptimization::DeoptReason::Reason_loop_limit_check: - st->print("Loop_Limit_Check "); + st->print("Loop Limit Check "); break; default: fatal("unknown kind"); diff --git a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter index bcdd86ba7d3..f94f06ab284 100644 --- a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter +++ b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/resources/com/sun/hotspot/igv/servercompiler/filters/customNodeInfo.filter @@ -34,3 +34,27 @@ editProperty(matches("name", "CallLeafDirect|CallLeafDirectVector|CallLeafNoFPDi // Show pre/main/post at CountedLoopNodes. editProperty(hasProperty("loop_kind"), ["loop_kind"], "extra_label", function(loop_kind) { return loop_kind[0]; }); + +// Show Parse Predicate type. +function parsePredicateInfo(dump_spec) { + // It's easier to match with ".*" because type "Loop" can also be found in type "Loop Limit Check" and "Profiled Loop". + // Matching with ".*" also requires us to exclude the optional "#useless" string at the end. + var predicateMatch = /#(.*)(#useless)?/.exec(dump_spec); + if (predicateMatch != null) { + return predicateMatch[1].trim(); + } + return null; +} +editProperty(matches("name", "ParsePredicate"), ["dump_spec"], "extra_label", + function(dump_spec) { return parsePredicateInfo(dump_spec[0]);}); + +// Show Assertion Predicate type. +function assertionPredicateInfo(dump_spec) { + var predicateMatch = /#((Init|Last) Value Assertion Predicate)/.exec(dump_spec); + if (predicateMatch != null) { + return predicateMatch[1]; + } + return null; +} +editProperty(matches("name", "If|RangeCheck"), ["dump_spec"], "extra_label", + function(dump_spec) { return assertionPredicateInfo(dump_spec[0]);}); From 3b21a298c29d88720f6bfb2dc1f3305b6a3db307 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 28 Nov 2024 14:17:15 +0000 Subject: [PATCH 014/171] 8345175: Further cleanup in java.logging and jdk.internal.logger after JEP 486 integration Reviewed-by: alanb, coffeys --- .../jdk/internal/logger/LoggerFinderLoader.java | 4 ---- .../share/classes/java/util/logging/LogManager.java | 13 ++++--------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java b/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java index 36edb918712..72fe24c25bb 100644 --- a/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java +++ b/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java @@ -148,8 +148,6 @@ public Logger getLogger(String name, Module module) { private static System.LoggerFinder loadLoggerFinder() { System.LoggerFinder result; try { - // Iterator iterates with the access control context stored - // at ServiceLoader creation time. final Iterator iterator = findLoggerFinderProviders(); if (iterator.hasNext()) { @@ -197,8 +195,6 @@ private static System.LoggerFinder loadDefaultImplementation() { DefaultLoggerFinder result = null; try { - // Iterator iterates with the access control context stored - // at ServiceLoader creation time. if (iterator.hasNext()) { result = iterator.next(); } diff --git a/src/java.logging/share/classes/java/util/logging/LogManager.java b/src/java.logging/share/classes/java/util/logging/LogManager.java index 0b7a854dddf..9c9c708a062 100644 --- a/src/java.logging/share/classes/java/util/logging/LogManager.java +++ b/src/java.logging/share/classes/java/util/logging/LogManager.java @@ -164,7 +164,7 @@ public class LogManager { // LoggerContext for system loggers and user loggers private final LoggerContext systemContext = new SystemLoggerContext(); private final LoggerContext userContext = new LoggerContext(); - // non final field - make it volatile to make sure that other threads + // non-final field - make it volatile to make sure that other threads // will see the new value once ensureLogManagerInitialized() has finished // executing. private volatile Logger rootLogger; @@ -312,7 +312,6 @@ protected LogManager() { */ private boolean initializedCalled = false; private volatile boolean initializationDone = false; - @SuppressWarnings("removal") final void ensureLogManagerInitialized() { final LogManager owner = this; if (initializationDone || owner != manager) { @@ -422,15 +421,11 @@ private void readPrimordialConfiguration() { // must be called while holding con } } - // LoggerContext maps from AppContext - private WeakHashMap contextsMap = null; - // Returns the LoggerContext for the user code (i.e. application or AppContext). // Loggers are isolated from each AppContext. private LoggerContext getUserContext() { - LoggerContext context = null; - // for standalone app, return userContext - return context != null ? context : userContext; + // return userContext + return userContext; } // The system context. @@ -447,7 +442,7 @@ private List contexts() { // Find or create a specified logger instance. If a logger has // already been created with the given name it is returned. - // Otherwise a new logger instance is created and registered + // Otherwise, a new logger instance is created and registered // in the LogManager global namespace. // This method will always return a non-null Logger object. // Synchronization is not required here. All synchronization for From fd742af0b76bdd7e6e14bd02ddc0d9bd921c90b2 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Thu, 28 Nov 2024 17:16:41 +0000 Subject: [PATCH 015/171] 8344394: Remove SecurityManager and related calls from java.management.rmi Reviewed-by: amenkov --- src/java.base/share/classes/module-info.java | 1 - .../remote/rmi/RMIConnectionImpl.java | 222 +++++------------- .../management/remote/rmi/RMIConnector.java | 127 +++++----- .../remote/rmi/RMIJRMPServerImpl.java | 4 +- 4 files changed, 114 insertions(+), 240 deletions(-) diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 5a44adeb294..c3bcbed4e3b 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -315,7 +315,6 @@ exports sun.reflect.misc to java.desktop, java.management, - java.management.rmi, java.rmi, java.sql.rowset; exports sun.security.internal.interfaces to diff --git a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java index cb318dc65b2..9c0b3d56214 100644 --- a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java +++ b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java @@ -29,24 +29,17 @@ import java.rmi.MarshalledObject; import java.rmi.UnmarshalException; import java.rmi.server.Unreferenced; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Permission; -import java.security.Permissions; -import java.security.PrivilegedAction; import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletionException; import javax.management.*; import javax.management.remote.JMXServerErrorException; import javax.management.remote.NotificationResult; import javax.security.auth.Subject; -import sun.reflect.misc.ReflectUtil; import static javax.management.remote.rmi.RMIConnector.Util.cast; import com.sun.jmx.remote.internal.ServerCommunicatorAdmin; @@ -94,7 +87,6 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { * RMIServerImpl. Can be null, equivalent to an * empty map. */ - @SuppressWarnings("removal") public RMIConnectionImpl(RMIServerImpl rmiServer, String connectionId, ClassLoader defaultClassLoader, @@ -111,54 +103,13 @@ public RMIConnectionImpl(RMIServerImpl rmiServer, this.mbeanServer = rmiServer.getMBeanServer(); final ClassLoader dcl = defaultClassLoader; - - ClassLoaderRepository repository = AccessController.doPrivileged( - new PrivilegedAction() { - public ClassLoaderRepository run() { - return mbeanServer.getClassLoaderRepository(); - } - }, - withPermissions(new MBeanPermission("*", "getClassLoaderRepository")) - ); - this.classLoaderWithRepository = AccessController.doPrivileged( - new PrivilegedAction() { - public ClassLoaderWithRepository run() { - return new ClassLoaderWithRepository( - repository, - dcl); - } - }, - withPermissions(new RuntimePermission("createClassLoader")) - ); - - this.defaultContextClassLoader = - AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public ClassLoader run() { - return new CombinedClassLoader(Thread.currentThread().getContextClassLoader(), - dcl); - } - }); - - serverCommunicatorAdmin = new - RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env)); - + ClassLoaderRepository repository = mbeanServer.getClassLoaderRepository(); + classLoaderWithRepository = new ClassLoaderWithRepository(repository, dcl); + defaultContextClassLoader = new CombinedClassLoader(Thread.currentThread().getContextClassLoader(), dcl); + serverCommunicatorAdmin = new RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env)); this.env = env; } - @SuppressWarnings("removal") - private static AccessControlContext withPermissions(Permission ... perms){ - Permissions col = new Permissions(); - - for (Permission thePerm : perms ) { - col.add(thePerm); - } - - final ProtectionDomain pd = new ProtectionDomain(null, col); - return new AccessControlContext( new ProtectionDomain[] { pd }); - } - private synchronized ServerNotifForwarder getServerNotifFwd() { // Lazily created when first use. Mainly when // addNotificationListener is first called. @@ -397,7 +348,7 @@ public ObjectInstance createMBean(String className, +", unwrapping params with MBean extended ClassLoader."); values = nullIsEmpty(unwrap(params, - getClassLoader(loaderName), + mbeanServer.getClassLoader(loaderName), defaultClassLoader, Object[].class,delegationSubject)); @@ -1249,7 +1200,6 @@ public void removeNotificationListener(ObjectName name, } } - @SuppressWarnings("removal") public NotificationResult fetchNotifications(long clientSequenceNumber, int maxNotifications, long timeout) @@ -1274,19 +1224,22 @@ public NotificationResult fetchNotifications(long clientSequenceNumber, + "returns null to force the client to stop fetching"); return null; } - final long csn = clientSequenceNumber; - final int mn = maxNotifications; - final long t = timeout; - PrivilegedAction action = - new PrivilegedAction() { - public NotificationResult run() { - return getServerNotifFwd().fetchNotifs(csn, t, mn); - } - }; + if (subject == null) { - return action.run(); + return getServerNotifFwd().fetchNotifs(clientSequenceNumber, timeout, maxNotifications); } else { - return Subject.doAs(subject, action); + try { + return Subject.callAs(subject, () -> getServerNotifFwd().fetchNotifs(clientSequenceNumber, timeout, maxNotifications)); + } catch (CompletionException ce) { + Throwable thr = ce.getCause(); + if (thr instanceof SecurityException se) { + throw se; + } else if (thr instanceof IOException ioe) { + throw ioe; + } else { + throw new RuntimeException(thr); + } + } } } finally { serverCommunicatorAdmin.rspOutgoing(); @@ -1311,25 +1264,6 @@ public String toString() { // private classes //------------------------------------------------------------------------ - private class PrivilegedOperation - implements PrivilegedExceptionAction { - - public PrivilegedOperation(int operation, Object[] params) { - this.operation = operation; - this.params = params; - } - - public Object run() throws Exception { - return doOperation(operation, params); - } - - private int operation; - private Object[] params; - } - - //------------------------------------------------------------------------ - // private classes - //------------------------------------------------------------------------ private class RMIServerCommunicatorAdmin extends ServerCommunicatorAdmin { public RMIServerCommunicatorAdmin(long timeout) { super(timeout); @@ -1352,44 +1286,13 @@ protected void doStop() { // private methods //------------------------------------------------------------------------ - @SuppressWarnings("removal") - private ClassLoader getClassLoader(final ObjectName name) - throws InstanceNotFoundException { - try { - return - AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public ClassLoader run() throws InstanceNotFoundException { - return mbeanServer.getClassLoader(name); - } - }, - withPermissions(new MBeanPermission("*", "getClassLoader")) - ); - } catch (PrivilegedActionException pe) { - throw (InstanceNotFoundException) extractException(pe); - } - } - - @SuppressWarnings("removal") private ClassLoader getClassLoaderFor(final ObjectName name) throws InstanceNotFoundException { - try { - return (ClassLoader) - AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public Object run() throws InstanceNotFoundException { - return mbeanServer.getClassLoaderFor(name); - } - }, - withPermissions(new MBeanPermission("*", "getClassLoaderFor")) - ); - } catch (PrivilegedActionException pe) { - throw (InstanceNotFoundException) extractException(pe); - } + + return mbeanServer.getClassLoaderFor(name); } /** @throws UnsupportedOperationException {@inheritDoc} */ - @SuppressWarnings("removal") private Object doPrivilegedOperation(final int operation, final Object[] params, final Subject delegationSubject) @@ -1402,10 +1305,9 @@ private Object doPrivilegedOperation(final int operation, } serverCommunicatorAdmin.reqIncoming(); try { - PrivilegedOperation op = new PrivilegedOperation(operation, params); if (subject == null) { try { - return op.run(); + return doOperation(operation, params); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; @@ -1414,7 +1316,20 @@ private Object doPrivilegedOperation(final int operation, } } } else { - return Subject.doAs(subject, op); + try { + return Subject.callAs(subject, () -> doOperation(operation, params)); + } catch (CompletionException ce) { + Throwable thr = ce.getCause(); + if (thr instanceof SecurityException se) { + throw se; + } else if (thr instanceof IOException ioe) { + throw ioe; + } else if (thr instanceof Exception e1) { + throw new PrivilegedActionException(e1); + } else { + throw new RuntimeException(thr); + } + } } } catch (Error e) { throw new JMXServerErrorException(e.toString(),e); @@ -1545,24 +1460,15 @@ private Object doOperation(int operation, Object[] params) } } - private static class SetCcl implements PrivilegedExceptionAction { - private final ClassLoader classLoader; - - SetCcl(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public ClassLoader run() { - Thread currentThread = Thread.currentThread(); - ClassLoader old = currentThread.getContextClassLoader(); - if (classLoader != old) { - currentThread.setContextClassLoader(classLoader); - } - return old; + private static ClassLoader setCcl(ClassLoader classLoader) { + Thread currentThread = Thread.currentThread(); + ClassLoader old = currentThread.getContextClassLoader(); + if (classLoader != old) { + currentThread.setContextClassLoader(classLoader); } + return old; } - @SuppressWarnings("removal") private T unwrap(final MarshalledObject mo, final ClassLoader cl, final Class wrappedClass, @@ -1578,32 +1484,33 @@ private T unwrap(final MarshalledObject mo, return null; } try { - final ClassLoader old = AccessController.doPrivileged(new SetCcl(cl)); + ClassLoader old = setCcl(cl); try { if (subject != null) { - return Subject.doAs(subject, (PrivilegedExceptionAction) () -> wrappedClass.cast(mo.get())); + try { + return Subject.callAs(subject, () -> wrappedClass.cast(mo.get())); + } catch (CompletionException ce) { + Throwable thr = ce.getCause(); + if (thr instanceof Exception e) { + throw e; + } else { + throw new RuntimeException(thr); + } + } } else { return wrappedClass.cast(mo.get()); } } finally { - AccessController.doPrivileged(new SetCcl(old)); + setCcl(old); } - } catch (PrivilegedActionException pe) { - Exception e = extractException(pe); + } catch (Exception e) { if (e instanceof IOException) { throw (IOException) e; } - if (e instanceof ClassNotFoundException) { - throw new UnmarshalException(e.toString(), e); - } logger.warning("unwrap", "Failed to unmarshall object: " + e); logger.debug("unwrap", e); - }catch (ClassNotFoundException ex) { - logger.warning("unwrap", "Failed to unmarshall object: " + ex); - logger.debug("unwrap", ex); - throw new UnmarshalException(ex.toString(), ex); + throw new UnmarshalException(e.toString(), e); } - return null; } private T unwrap(final MarshalledObject mo, @@ -1616,18 +1523,10 @@ private T unwrap(final MarshalledObject mo, return null; } try { - @SuppressWarnings("removal") - ClassLoader orderCL = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public ClassLoader run() throws Exception { - return new CombinedClassLoader(Thread.currentThread().getContextClassLoader(), - new OrderClassLoaders(cl1, cl2)); - } - } - ); + ClassLoader orderCL = new CombinedClassLoader(Thread.currentThread().getContextClassLoader(), + new OrderClassLoaders(cl1, cl2)); return unwrap(mo, orderCL, wrappedClass,delegationSubject); - } catch (PrivilegedActionException pe) { - Exception e = extractException(pe); + } catch (Exception e) { if (e instanceof IOException) { throw (IOException) e; } @@ -1815,7 +1714,6 @@ private CombinedClassLoader(ClassLoader parent, ClassLoader defaultCL) { @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - ReflectUtil.checkPackageAccess(name); try { super.loadClass(name, resolve); } catch(Exception e) { diff --git a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java index 24b9f1055b7..47089dcd252 100644 --- a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java +++ b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java @@ -54,9 +54,6 @@ import java.rmi.server.RemoteObject; import java.rmi.server.RemoteObjectInvocationHandler; import java.rmi.server.RemoteRef; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collections; @@ -102,7 +99,6 @@ import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.security.auth.Subject; import jdk.internal.module.Modules; -import sun.reflect.misc.ReflectUtil; import sun.rmi.server.UnicastRef2; import sun.rmi.transport.LiveRef; import java.io.NotSerializableException; @@ -1856,7 +1852,6 @@ private static final class ObjectInputStreamWithLoader protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { String name = classDesc.getName(); - ReflectUtil.checkPackageAccess(name); return Class.forName(name, false, Objects.requireNonNull(loader)); } @@ -1964,51 +1959,7 @@ public Object invoke(Remote obj, Method method, "\4\0\1\0\14\0\0"; final byte[] pRefByteCode = NoCallStackClassLoader.stringToBytes(pRefByteCodeString); - PrivilegedExceptionAction> action = - new PrivilegedExceptionAction>() { - public Constructor run() throws Exception { - Class thisClass = RMIConnector.class; - ClassLoader thisLoader = thisClass.getClassLoader(); - ProtectionDomain thisProtectionDomain = - thisClass.getProtectionDomain(); - - String proxyRefCName = ProxyRef.class.getName(); - ClassLoader cl = - new NoCallStackClassLoader(pRefClassName, - pRefByteCode, - new String[] { proxyRefCName }, - thisLoader, - thisProtectionDomain); - - Module jmxModule = ProxyRef.class.getModule(); - Module rmiModule = RemoteRef.class.getModule(); - - String pkg = packageOf(pRefClassName); - assert pkg != null && pkg.length() > 0 && - !pkg.equals(packageOf(proxyRefCName)); - - ModuleDescriptor descriptor = - ModuleDescriptor.newModule("jdk.remoteref", Set.of(SYNTHETIC)) - .packages(Set.of(pkg)) - .build(); - Module m = Modules.defineModule(cl, descriptor, null); - - // jdk.remoteref needs to read to java.base and jmxModule - Modules.addReads(m, Object.class.getModule()); - Modules.addReads(m, jmxModule); - Modules.addReads(m, rmiModule); - - // jdk.remoteref needs access to ProxyRef class - Modules.addExports(jmxModule, packageOf(proxyRefCName), m); - - // java.management needs to instantiate the fabricated RemoteRef class - Modules.addReads(jmxModule, m); - Modules.addExports(m, pkg, jmxModule); - - Class c = cl.loadClass(pRefClassName); - return c.getConstructor(RemoteRef.class); - } - }; + Class serverStubClass; try { @@ -2026,9 +1977,48 @@ public Constructor run() throws Exception { Constructor constr; try { stubClass = Class.forName(rmiConnectionImplStubClassName); - @SuppressWarnings("removal") - Constructor tmp = (Constructor) AccessController.doPrivileged(action); - constr = tmp; + + Class thisClass = RMIConnector.class; + ClassLoader thisLoader = thisClass.getClassLoader(); + ProtectionDomain thisProtectionDomain = + thisClass.getProtectionDomain(); + + String proxyRefCName = ProxyRef.class.getName(); + ClassLoader cl = + new NoCallStackClassLoader(pRefClassName, + pRefByteCode, + new String[] { proxyRefCName }, + thisLoader, + thisProtectionDomain); + + Module jmxModule = ProxyRef.class.getModule(); + Module rmiModule = RemoteRef.class.getModule(); + + String pkg = packageOf(pRefClassName); + assert pkg != null && pkg.length() > 0 && + !pkg.equals(packageOf(proxyRefCName)); + + ModuleDescriptor descriptor = + ModuleDescriptor.newModule("jdk.remoteref", Set.of(SYNTHETIC)) + .packages(Set.of(pkg)) + .build(); + Module m = Modules.defineModule(cl, descriptor, null); + + // jdk.remoteref needs to read to java.base and jmxModule + Modules.addReads(m, Object.class.getModule()); + Modules.addReads(m, jmxModule); + Modules.addReads(m, rmiModule); + + // jdk.remoteref needs access to ProxyRef class + Modules.addExports(jmxModule, packageOf(proxyRefCName), m); + + // java.management needs to instantiate the fabricated RemoteRef class + Modules.addReads(jmxModule, m); + Modules.addExports(m, pkg, jmxModule); + + Class c = cl.loadClass(pRefClassName); + + constr = c.getConstructor(RemoteRef.class); } catch (Exception e) { logger.error("", "Failed to initialize proxy reference constructor "+ @@ -2172,33 +2162,22 @@ private static int base64toInt(char c) { //-------------------------------------------------------------------- // Private stuff - Find / Set default class loader //-------------------------------------------------------------------- - @SuppressWarnings("removal") private ClassLoader pushDefaultClassLoader() { final Thread t = Thread.currentThread(); final ClassLoader old = t.getContextClassLoader(); - if (defaultClassLoader != null) - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - if (t.getContextClassLoader() != defaultClassLoader) { - t.setContextClassLoader(defaultClassLoader); - } - return null; - } - }); - return old; + if (defaultClassLoader != null) { + if (t.getContextClassLoader() != defaultClassLoader) { + t.setContextClassLoader(defaultClassLoader); + } + } + return old; } - @SuppressWarnings("removal") private void popDefaultClassLoader(final ClassLoader old) { - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - Thread t = Thread.currentThread(); - if (t.getContextClassLoader() != old) { - t.setContextClassLoader(old); - } - return null; - } - }); + Thread t = Thread.currentThread(); + if (t.getContextClassLoader() != old) { + t.setContextClassLoader(old); + } } //-------------------------------------------------------------------- diff --git a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java index d51ca2cd26e..44e85f1bde2 100644 --- a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java +++ b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIJRMPServerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -43,7 +43,6 @@ import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; -import sun.reflect.misc.ReflectUtil; import sun.rmi.server.UnicastServerRef; import sun.rmi.server.UnicastServerRef2; import sun.rmi.transport.LiveRef; @@ -119,7 +118,6 @@ else if(credentialsFilter != null){ else if (credentialsTypes != null) { allowedTypes = Arrays.stream(credentialsTypes).filter( s -> s!= null).collect(Collectors.toSet()); - allowedTypes.forEach(ReflectUtil::checkPackageAccess); cFilter = this::newClientCheckInput; } else { allowedTypes = null; From 43000a34d5ba6e2e997893a90dac0fe6693611cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Thu, 28 Nov 2024 17:38:07 +0000 Subject: [PATCH 016/171] 8345075: java.lang.module.ModuleDescriptor constructor could be made private Reviewed-by: alanb --- .../java/lang/module/ModuleDescriptor.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java index b38d1b0cc87..4b43a457b88 100644 --- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java +++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -1312,18 +1312,18 @@ private ModuleDescriptor(String name, * Creates a module descriptor from its components. * The arguments are pre-validated and sets are unmodifiable sets. */ - ModuleDescriptor(String name, - Version version, - Set modifiers, - Set requires, - Set exports, - Set opens, - Set uses, - Set provides, - Set packages, - String mainClass, - int hashCode, - boolean unused) { + private ModuleDescriptor(String name, + Version version, + Set modifiers, + Set requires, + Set exports, + Set opens, + Set uses, + Set provides, + Set packages, + String mainClass, + int hashCode, + boolean unused) { this.name = name; this.version = version; this.rawVersionString = null; From 959fa4a1a35a1bb650ec5888efaf3d0fc8cfb025 Mon Sep 17 00:00:00 2001 From: Aleksei Efimov Date: Thu, 28 Nov 2024 17:43:27 +0000 Subject: [PATCH 017/171] 8344299: SM cleanup in javax.naming modules Reviewed-by: alanb, dfuchs --- .../sun/security/util/SecurityConstants.java | 4 -- .../classes/com/sun/jndi/ldap/ClientId.java | 6 +- .../classes/com/sun/jndi/ldap/Connection.java | 11 ++- .../classes/com/sun/jndi/ldap/EventQueue.java | 2 +- .../sun/jndi/ldap/LdapBindingEnumeration.java | 17 +---- .../classes/com/sun/jndi/ldap/LdapCtx.java | 13 +--- .../sun/jndi/ldap/LdapDnsProviderService.java | 26 ++----- .../com/sun/jndi/ldap/LdapPoolManager.java | 60 +++++----------- .../sun/jndi/ldap/LdapSearchEnumeration.java | 17 +---- .../classes/com/sun/jndi/ldap/LdapURL.java | 9 +-- .../sun/jndi/ldap/NamingEventNotifier.java | 2 +- .../share/classes/com/sun/jndi/ldap/Obj.java | 21 ++++-- .../com/sun/jndi/ldap/VersionHelper.java | 72 ------------------- .../javax/naming/ldap/StartTlsRequest.java | 24 +------ .../naming/ldap/spi/LdapDnsProvider.java | 19 ----- .../javax/naming/spi/NamingManager.java | 21 +----- .../provider/certpath/ldap/JdkLDAP.java | 27 +++---- .../provider/certpath/ldap/LDAPCertStore.java | 8 +-- .../certpath/ldap/LDAPCertStoreImpl.java | 11 ++- .../jndi/dns/DNSDatagramChannelFactory.java | 8 +-- .../com/sun/jndi/dns/DnsContextFactory.java | 49 ++----------- .../classes/com/sun/jndi/dns/DnsUrl.java | 10 +-- .../jndi/rmi/registry/RegistryContext.java | 24 ------- .../com/sun/jndi/url/rmi/rmiURLContext.java | 9 +-- 24 files changed, 88 insertions(+), 382 deletions(-) delete mode 100644 src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java diff --git a/src/java.base/share/classes/sun/security/util/SecurityConstants.java b/src/java.base/share/classes/sun/security/util/SecurityConstants.java index 21ec592c766..9d49bbba0a1 100644 --- a/src/java.base/share/classes/sun/security/util/SecurityConstants.java +++ b/src/java.base/share/classes/sun/security/util/SecurityConstants.java @@ -96,10 +96,6 @@ private SecurityConstants () { public static final RuntimePermission GET_PD_PERMISSION = new RuntimePermission("getProtectionDomain"); - // java.lang.Class, java.lang.ClassLoader, java.lang.Thread - public static final RuntimePermission GET_CLASSLOADER_PERMISSION = - new RuntimePermission("getClassLoader"); - // java.lang.Thread public static final RuntimePermission GET_STACK_TRACE_PERMISSION = new RuntimePermission("getStackTrace"); diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/ClientId.java b/src/java.naming/share/classes/com/sun/jndi/ldap/ClientId.java index 7348692ad30..20a8cdcf148 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/ClientId.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/ClientId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -84,8 +84,8 @@ class ClientId { if ((socketFactory != null) && !socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) { try { - Class socketFactoryClass = - Obj.helper.loadClass(socketFactory); + Class socketFactoryClass = Class.forName(socketFactory, + true, Thread.currentThread().getContextClassLoader()); this.sockComparator = socketFactoryClass.getMethod( "compare", new Class[]{Object.class, Object.class}); Method getDefault = socketFactoryClass.getMethod( diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index f270a34d5b7..8166fe97a4a 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -44,8 +44,6 @@ import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -183,10 +181,8 @@ public final class Connection implements Runnable { = hostnameVerificationDisabledValue(); private static boolean hostnameVerificationDisabledValue() { - PrivilegedAction act = () -> System.getProperty( + String prop = System.getProperty( "com.sun.jndi.ldap.object.disableEndpointIdentification"); - @SuppressWarnings("removal") - String prop = AccessController.doPrivileged(act); if (prop == null) { return false; } @@ -259,7 +255,7 @@ void setBound() { throw ce; } - worker = Obj.helper.createThread(this); + worker = new Thread(this); worker.setDaemon(true); worker.start(); } @@ -313,7 +309,8 @@ private SocketFactory getSocketFactory(String socketFactoryName) throws Exceptio } @SuppressWarnings("unchecked") Class socketFactoryClass = - (Class) Obj.helper.loadClass(socketFactoryName); + (Class) Class.forName(socketFactoryName, + true, Thread.currentThread().getContextClassLoader()); Method getDefault = socketFactoryClass.getMethod("getDefault"); SocketFactory factory = (SocketFactory) getDefault.invoke(null, new Object[]{}); diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java index 3d88e179894..4f1cb9ec6a7 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java @@ -71,7 +71,7 @@ private static class QueueElement { // package private EventQueue() { - qThread = Obj.helper.createThread(this); + qThread = new Thread(this); qThread.setDaemon(true); // not a user thread qThread.start(); } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapBindingEnumeration.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapBindingEnumeration.java index 53a33a57097..5259cb63801 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapBindingEnumeration.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapBindingEnumeration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,10 +25,6 @@ package com.sun.jndi.ldap; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.Vector; import javax.naming.*; import javax.naming.directory.*; @@ -41,16 +37,12 @@ final class LdapBindingEnumeration extends AbstractLdapNamingEnumeration { - @SuppressWarnings("removal") - private final AccessControlContext acc = AccessController.getContext(); - LdapBindingEnumeration(LdapCtx homeCtx, LdapResult answer, Name remain, Continuation cont) throws NamingException { super(homeCtx, answer, remain, cont); } - @SuppressWarnings("removal") @Override protected Binding createItem(String dn, Attributes attrs, Vector respCtls) @@ -61,12 +53,7 @@ final class LdapBindingEnumeration if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) { // serialized object or object reference - try { - PrivilegedExceptionAction pa = () -> Obj.decodeObject(attrs); - obj = AccessController.doPrivileged(pa, acc); - } catch (PrivilegedActionException e) { - throw (NamingException)e.getException(); - } + obj = Obj.decodeObject(attrs); } if (obj == null) { // DirContext object diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java index 27ecb9e4ca1..0695894f300 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapCtx.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -32,8 +32,6 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collections; import java.util.Locale; @@ -220,7 +218,7 @@ static final class SearchArgs { // System property value private static final String ALLOWED_MECHS_SP_VALUE = - getMechsAllowedToSendCredentials(); + System.getProperty(ALLOWED_MECHS_SP); // Set of authentication mechanisms allowed by the system property private static final Set MECHS_ALLOWED_BY_SP = @@ -2706,13 +2704,6 @@ public void reconnect(Control[] connCtls) throws NamingException { ensureOpen(); // open or reauthenticated } - // Load 'mechsAllowedToSendCredentials' system property value - @SuppressWarnings("removal") - private static String getMechsAllowedToSendCredentials() { - PrivilegedAction pa = () -> System.getProperty(ALLOWED_MECHS_SP); - return System.getSecurityManager() == null ? pa.run() : AccessController.doPrivileged(pa); - } - // Get set of allowed authentication mechanism names from the property value private static Set getMechsFromPropertyValue(String propValue) { if (propValue == null || propValue.isBlank()) { diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java index 9330782f48c..da9c66489ad 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapDnsProviderService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -25,14 +25,11 @@ package com.sun.jndi.ldap; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.*; import java.util.concurrent.locks.ReentrantLock; import javax.naming.NamingException; import javax.naming.ldap.spi.LdapDnsProvider; import javax.naming.ldap.spi.LdapDnsProviderResult; -import sun.security.util.SecurityConstants; /** * The {@code LdapDnsProviderService} is responsible for creating and providing @@ -50,25 +47,10 @@ final class LdapDnsProviderService { /** * Creates a new instance of LdapDnsProviderService */ - @SuppressWarnings("removal") private LdapDnsProviderService() { - SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - providers = ServiceLoader.load( - LdapDnsProvider.class, - ClassLoader.getSystemClassLoader()); - } else { - final PrivilegedAction> pa = - () -> ServiceLoader.load( - LdapDnsProvider.class, - ClassLoader.getSystemClassLoader()); - - providers = AccessController.doPrivileged( - pa, - null, - new RuntimePermission("ldapDnsProvider"), - SecurityConstants.GET_CLASSLOADER_PERMISSION); - } + providers = ServiceLoader.load( + LdapDnsProvider.class, + ClassLoader.getSystemClassLoader()); } /** diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapPoolManager.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapPoolManager.java index ef122351437..384e1ace289 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapPoolManager.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapPoolManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -34,8 +34,6 @@ import javax.naming.ldap.Control; import javax.naming.NamingException; import javax.naming.CommunicationException; -import java.security.AccessController; -import java.security.PrivilegedAction; import com.sun.jndi.ldap.pool.PoolCleaner; import com.sun.jndi.ldap.pool.Pool; @@ -60,10 +58,10 @@ public final class LdapPoolManager { "com.sun.jndi.ldap.connect.pool.debug"; public static final boolean debug = - "all".equalsIgnoreCase(getProperty(DEBUG, null)); + "all".equalsIgnoreCase(System.getProperty(DEBUG)); public static final boolean trace = debug || - "fine".equalsIgnoreCase(getProperty(DEBUG, null)); + "fine".equalsIgnoreCase(System.getProperty(DEBUG)); // ---------- System properties for connection pooling @@ -120,16 +118,16 @@ public final class LdapPoolManager { private static final Pool[] pools = new Pool[3]; static { - maxSize = getInteger(MAX_POOL_SIZE, DEFAULT_MAX_POOL_SIZE); + maxSize = Integer.getInteger(MAX_POOL_SIZE, DEFAULT_MAX_POOL_SIZE); - prefSize = getInteger(PREF_POOL_SIZE, DEFAULT_PREF_POOL_SIZE); + prefSize = Integer.getInteger(PREF_POOL_SIZE, DEFAULT_PREF_POOL_SIZE); - initSize = getInteger(INIT_POOL_SIZE, DEFAULT_INIT_POOL_SIZE); + initSize = Integer.getInteger(INIT_POOL_SIZE, DEFAULT_INIT_POOL_SIZE); - idleTimeout = getLong(POOL_TIMEOUT, DEFAULT_TIMEOUT); + idleTimeout = Long.getLong(POOL_TIMEOUT, DEFAULT_TIMEOUT); // Determine supported authentication mechanisms - String str = getProperty(POOL_AUTH, DEFAULT_AUTH_MECHS); + String str = System.getProperty(POOL_AUTH, DEFAULT_AUTH_MECHS); StringTokenizer parser = new StringTokenizer(str); int count = parser.countTokens(); String mech; @@ -147,7 +145,7 @@ public final class LdapPoolManager { } // Determine supported protocols - str= getProperty(POOL_PROTOCOL, DEFAULT_PROTOCOLS); + str = System.getProperty(POOL_PROTOCOL, DEFAULT_PROTOCOLS); parser = new StringTokenizer(str); count = parser.countTokens(); String proto; @@ -171,20 +169,15 @@ public final class LdapPoolManager { } } - @SuppressWarnings("removal") private static void startCleanerThread() { // Create cleaner to expire idle connections - PrivilegedAction pa = new PrivilegedAction() { - public Void run() { - Thread t = InnocuousThread.newSystemThread( - "LDAP PoolCleaner", - new PoolCleaner(idleTimeout, pools)); - assert t.getContextClassLoader() == null; - t.setDaemon(true); - t.start(); - return null; - }}; - AccessController.doPrivileged(pa); + Thread t = InnocuousThread.newSystemThread( + "LDAP PoolCleaner", + new PoolCleaner(idleTimeout, pools)); + assert t.getContextClassLoader() == null; + t.setDaemon(true); + t.start(); + } // Cannot instantiate one of these @@ -252,7 +245,8 @@ static boolean isPoolingAllowed(String socketFactory, OutputStream trace, if ((socketFactory != null) && !socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) { try { - Class socketFactoryClass = Obj.helper.loadClass(socketFactory); + Class socketFactoryClass = Class.forName(socketFactory, true, + Thread.currentThread().getContextClassLoader()); Class[] interfaces = socketFactoryClass.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].getCanonicalName().equals(COMPARATOR)) { @@ -399,22 +393,4 @@ private static void d(String msg, String o) { System.err.println("LdapPoolManager: " + msg + o); } } - - @SuppressWarnings("removal") - private static final String getProperty(final String propName, final String defVal) { - PrivilegedAction pa = () -> System.getProperty(propName, defVal); - return AccessController.doPrivileged(pa); - } - - @SuppressWarnings("removal") - private static final int getInteger(final String propName, final int defVal) { - PrivilegedAction pa = () -> Integer.getInteger(propName, defVal); - return AccessController.doPrivileged(pa); - } - - @SuppressWarnings("removal") - private static final long getLong(final String propName, final long defVal) { - PrivilegedAction pa = () -> Long.getLong(propName, defVal); - return AccessController.doPrivileged(pa); - } } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSearchEnumeration.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSearchEnumeration.java index 50f26851892..6f1981df102 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSearchEnumeration.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapSearchEnumeration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,10 +25,6 @@ package com.sun.jndi.ldap; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.Vector; import javax.naming.*; import javax.naming.directory.*; @@ -45,9 +41,6 @@ final class LdapSearchEnumeration private Name startName; // prefix of names of search results private LdapCtx.SearchArgs searchArgs = null; - @SuppressWarnings("removal") - private final AccessControlContext acc = AccessController.getContext(); - LdapSearchEnumeration(LdapCtx homeCtx, LdapResult search_results, String starter, LdapCtx.SearchArgs args, Continuation cont) throws NamingException { @@ -61,7 +54,6 @@ final class LdapSearchEnumeration searchArgs = args; } - @SuppressWarnings("removal") @Override protected SearchResult createItem(String dn, Attributes attrs, Vector respCtls) @@ -121,12 +113,7 @@ protected SearchResult createItem(String dn, Attributes attrs, if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) { // Entry contains Java-object attributes (ser/ref object) // serialized object or object reference - try { - PrivilegedExceptionAction pea = () -> Obj.decodeObject(attrs); - obj = AccessController.doPrivileged(pea, acc); - } catch (PrivilegedActionException e) { - throw (NamingException)e.getException(); - } + obj = Obj.decodeObject(attrs); } if (obj == null) { obj = new LdapCtx(homeCtx, dn); diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java index f26e086d6a7..140f025b779 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -29,8 +29,6 @@ import java.net.MalformedURLException; import java.io.UnsupportedEncodingException; import java.net.URI; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Locale; import java.util.StringTokenizer; import com.sun.jndi.toolkit.url.Uri; @@ -73,12 +71,9 @@ public final class LdapURL extends Uri { public static final ParseMode PARSE_MODE; static { - PrivilegedAction action = () -> - System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); ParseMode parseMode = DEFAULT_PARSE_MODE; try { - @SuppressWarnings("removal") - String mode = AccessController.doPrivileged(action); + String mode = System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); } catch (Throwable t) { parseMode = DEFAULT_PARSE_MODE; diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java b/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java index 27b83b91515..40a8173b768 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java @@ -86,7 +86,7 @@ final class NamingEventNotifier implements Runnable { namingListeners = new Vector<>(); namingListeners.addElement(firstListener); - worker = Obj.helper.createThread(this); + worker = new Thread(this); worker.setDaemon(true); // not a user thread worker.start(); } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java index 0d28928559f..5e34f954302 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Obj.java @@ -57,8 +57,19 @@ final class Obj { private Obj () {}; // Make sure no one can create one - // package private; used by Connection - static VersionHelper helper = VersionHelper.getVersionHelper(); + /** + * Determines whether objects may be deserialized or reconstructed from a content of + * 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes. + */ + private static final boolean trustSerialData; + + static { + // System property to control whether classes are allowed to be loaded from + // 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes. + String trustSerialDataSp = System.getProperty( + "com.sun.jndi.ldap.object.trustSerialData", "false"); + trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp); + } // LDAP attributes used to support Java objects. static final String[] JAVA_ATTRIBUTES = { @@ -233,14 +244,14 @@ static Object decodeObject(Attributes attrs) String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE])); try { if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) { - if (!VersionHelper.isSerialDataAllowed()) { + if (!trustSerialData) { throw new NamingException("Object deserialization is not allowed"); } ClassLoader cl = Thread.currentThread().getContextClassLoader(); return deserializeObject((byte[])attr.get(), cl); } else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) { // javaRemoteLocation attribute (RMI stub will be created) - if (!VersionHelper.isSerialDataAllowed()) { + if (!trustSerialData) { throw new NamingException("Object deserialization is not allowed"); } // For backward compatibility only @@ -471,7 +482,7 @@ private static Reference decodeReference(Attributes attrs, } else if (val.charAt(start) == separator) { // Check if deserialization of binary RefAddr is allowed from // 'javaReferenceAddress' LDAP attribute. - if (!VersionHelper.isSerialDataAllowed()) { + if (!trustSerialData) { throw new NamingException("Object deserialization is not allowed"); } diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java b/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java deleted file mode 100644 index bb888a1457a..00000000000 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/VersionHelper.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 1999, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package com.sun.jndi.ldap; - -public final class VersionHelper { - - private static final VersionHelper helper = new VersionHelper(); - - /** - * Determines whether objects may be deserialized or reconstructed from a content of - * 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes. - */ - private static final boolean trustSerialData; - - static { - // System property to control whether classes are allowed to be loaded from - // 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes. - String trustSerialDataSp = System.getProperty( - "com.sun.jndi.ldap.object.trustSerialData", "false"); - trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp); - } - - private VersionHelper() { - } - - static VersionHelper getVersionHelper() { - return helper; - } - - /** - * Returns true if deserialization or reconstruction of objects from - * 'javaSerializedData', 'javaRemoteLocation' and 'javaReferenceAddress' - * LDAP attributes is allowed. - * - * @return true if deserialization is allowed; false - otherwise - */ - public static boolean isSerialDataAllowed() { - return trustSerialData; - } - - Class loadClass(String className) throws ClassNotFoundException { - return Class.forName(className, true, - Thread.currentThread().getContextClassLoader()); - } - - Thread createThread(Runnable r) { - return new Thread(r); - } -} diff --git a/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java b/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java index 1e519609484..1e45ff511c5 100644 --- a/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java +++ b/src/java.naming/share/classes/javax/naming/ldap/StartTlsRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -26,13 +26,10 @@ package javax.naming.ldap; import java.util.Iterator; -import java.security.AccessController; -import java.security.PrivilegedAction; import javax.naming.ConfigurationException; import javax.naming.NamingException; import com.sun.naming.internal.VersionHelper; import java.util.ServiceLoader; -import java.util.ServiceConfigurationError; /** * This class implements the LDAPv3 Extended Request for StartTLS as @@ -181,10 +178,10 @@ public ExtendedResponse createExtendedResponse(String id, byte[] berValue, StartTlsResponse resp = null; ServiceLoader sl = ServiceLoader.load( - StartTlsResponse.class, getContextClassLoader()); + StartTlsResponse.class, Thread.currentThread().getContextClassLoader()); Iterator iter = sl.iterator(); - while (resp == null && privilegedHasNext(iter)) { + while (resp == null && iter.hasNext()) { resp = iter.next(); } if (resp != null) { @@ -216,20 +213,5 @@ private ConfigurationException wrapException(Exception e) { return ce; } - /* - * Acquire the class loader associated with this thread. - */ - @SuppressWarnings("removal") - private final ClassLoader getContextClassLoader() { - PrivilegedAction pa = Thread.currentThread()::getContextClassLoader; - return AccessController.doPrivileged(pa); - } - - @SuppressWarnings("removal") - private static final boolean privilegedHasNext(final Iterator iter) { - PrivilegedAction pa = iter::hasNext; - return AccessController.doPrivileged(pa); - } - private static final long serialVersionUID = 4441679576360753397L; } diff --git a/src/java.naming/share/classes/javax/naming/ldap/spi/LdapDnsProvider.java b/src/java.naming/share/classes/javax/naming/ldap/spi/LdapDnsProvider.java index 0cb240a891c..21fbca5d1e7 100644 --- a/src/java.naming/share/classes/javax/naming/ldap/spi/LdapDnsProvider.java +++ b/src/java.naming/share/classes/javax/naming/ldap/spi/LdapDnsProvider.java @@ -53,29 +53,10 @@ */ public abstract class LdapDnsProvider { - // The {@code RuntimePermission("ldapDnsProvider")} is - // necessary to subclass and instantiate the {@code LdapDnsProvider} class. - private static final RuntimePermission DNSPROVIDER_PERMISSION = - new RuntimePermission("ldapDnsProvider"); - /** * Creates a new instance of {@code LdapDnsProvider}. */ protected LdapDnsProvider() { - this(checkPermission()); - } - - private LdapDnsProvider(Void unused) { - // nothing to do. - } - - private static Void checkPermission() { - @SuppressWarnings("removal") - final SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(DNSPROVIDER_PERMISSION); - } - return null; } /** diff --git a/src/java.naming/share/classes/javax/naming/spi/NamingManager.java b/src/java.naming/share/classes/javax/naming/spi/NamingManager.java index 4d57b541ec1..83f0ec2dd78 100644 --- a/src/java.naming/share/classes/javax/naming/spi/NamingManager.java +++ b/src/java.naming/share/classes/javax/naming/spi/NamingManager.java @@ -25,8 +25,6 @@ package javax.naming.spi; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.*; import javax.naming.*; @@ -471,7 +469,6 @@ private static Object getURLObject(String scheme, Object urlInfo, * @see javax.naming.InitialContext * @see javax.naming.directory.InitialDirContext */ - @SuppressWarnings("removal") public static Context getInitialContext(Hashtable env) throws NamingException { ClassLoader loader; @@ -492,16 +489,8 @@ public static Context getInitialContext(Hashtable env) throw ne; } - if (System.getSecurityManager() == null) { - loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) loader = ClassLoader.getSystemClassLoader(); - } else { - PrivilegedAction pa = () -> { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return (cl == null) ? ClassLoader.getSystemClassLoader() : cl; - }; - loader = AccessController.doPrivileged(pa); - } + loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) loader = ClassLoader.getSystemClassLoader(); var key = FACTORIES_CACHE.sub(className); try { @@ -570,12 +559,6 @@ public static synchronized void setInitialContextFactoryBuilder( if (initctx_factory_builder != null) throw new IllegalStateException( "InitialContextFactoryBuilder already set"); - - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkSetFactory(); - } initctx_factory_builder = builder; } diff --git a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/JdkLDAP.java b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/JdkLDAP.java index d03a4f06221..b42c6971126 100644 --- a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/JdkLDAP.java +++ b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/JdkLDAP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -69,25 +69,20 @@ public Object newInstance(Object ctrParamObj) } } - @SuppressWarnings("removal") public JdkLDAP() { super("JdkLDAP", PROVIDER_VER, "JdkLDAP Provider (implements LDAP CertStore)"); final Provider p = this; - PrivilegedAction pa = () -> { - HashMap attrs = new HashMap<>(2); - attrs.put("LDAPSchema", "RFC2587"); - attrs.put("ImplementedIn", "Software"); + HashMap attrs = new HashMap<>(2); + attrs.put("LDAPSchema", "RFC2587"); + attrs.put("ImplementedIn", "Software"); - /* - * CertStore - * attrs: LDAPSchema, ImplementedIn - */ - putService(new ProviderService(p, "CertStore", - "LDAP", "sun.security.provider.certpath.ldap.LDAPCertStore", - null, attrs)); - return null; - }; - AccessController.doPrivileged(pa); + /* + * CertStore + * attrs: LDAPSchema, ImplementedIn + */ + putService(new ProviderService(p, "CertStore", + "LDAP", "sun.security.provider.certpath.ldap.LDAPCertStore", + null, attrs)); } } diff --git a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java index 7ff6dc8d925..b12e53cb7ec 100644 --- a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java +++ b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -136,12 +136,6 @@ public LDAPCertStore(CertStoreParameters params) + params.getClass().getName() + " passed"); } - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkConnect(serverName, port); - } - Key k = new Key(serverName, port); LDAPCertStoreImpl lci = certStoreCache.get(k); if (lci == null) { diff --git a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java index 39a787bf4fb..8f18e04760a 100644 --- a/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java +++ b/src/java.naming/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -97,9 +97,7 @@ final class LDAPCertStoreImpl { "sun.security.certpath.ldap.disable.app.resource.files"; static { - @SuppressWarnings("removal") - String s = AccessController.doPrivileged( - (PrivilegedAction) () -> System.getProperty(PROP_LIFETIME)); + String s = System.getProperty(PROP_LIFETIME); if (s != null) { LIFETIME = Integer.parseInt(s); // throws NumberFormatException } else { @@ -172,9 +170,8 @@ private void createInitialDirContext(String server, int port) env.put(Context.PROVIDER_URL, url); // If property is set to true, disable application resource file lookup. - @SuppressWarnings("removal") - boolean disableAppResourceFiles = AccessController.doPrivileged( - (PrivilegedAction) () -> Boolean.getBoolean(PROP_DISABLE_APP_RESOURCE_FILES)); + boolean disableAppResourceFiles = + Boolean.getBoolean(PROP_DISABLE_APP_RESOURCE_FILES); if (disableAppResourceFiles) { if (debug != null) { debug.println("LDAPCertStore disabling app resource files"); diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramChannelFactory.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramChannelFactory.java index 73585c23f86..a278987ad3b 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramChannelFactory.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DNSDatagramChannelFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -29,8 +29,6 @@ import java.net.ProtocolFamily; import java.net.InetSocketAddress; import java.nio.channels.DatagramChannel; -import java.security.AccessController; -import java.security.PrivilegedExceptionAction; import java.util.Objects; import java.util.Random; @@ -52,11 +50,9 @@ private EphemeralPortRange() {} } private static int findFirstFreePort() { - PrivilegedExceptionAction action = () -> new DatagramSocket(0); int port; try { - @SuppressWarnings({"deprecated", "removal"}) - DatagramSocket ds = AccessController.doPrivileged(action); + DatagramSocket ds = new DatagramSocket(0); try (DatagramSocket ds1 = ds) { port = ds1.getLocalPort(); } diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContextFactory.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContextFactory.java index 7b8a30a52d0..645fac074e5 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContextFactory.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContextFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -90,9 +90,8 @@ public static DnsContext getContext(String domain, * Public for use by product test suite. */ public static boolean platformServersAvailable() { - return !filterNameServers( - ResolverConfiguration.open().nameservers(), true - ).isEmpty(); + return !ResolverConfiguration + .open().nameservers().isEmpty(); } private static Context urlToContext(String url, Hashtable env) @@ -145,8 +144,8 @@ private static String[] serversForUrls(DnsUrl[] urls) // No server or port given, so look to underlying platform. // ResolverConfiguration does some limited caching, so the // following is reasonably efficient even if called rapid-fire. - List platformServers = filterNameServers( - ResolverConfiguration.open().nameservers(), false); + List platformServers = + ResolverConfiguration.open().nameservers(); if (!platformServers.isEmpty()) { servers.addAll(platformServers); continue; // on to next URL (if any, which is unlikely) @@ -216,42 +215,4 @@ private static String getInitCtxUrl(Hashtable env) { String url = (String) env.get(Context.PROVIDER_URL); return ((url != null) ? url : DEFAULT_URL); } - - /** - * Removes any DNS server that's not permitted to access - * @param input the input server[:port] list, must not be null - * @param oneIsEnough return output once there exists one ok - * @return the filtered list, all non-permitted input removed - */ - private static List filterNameServers(List input, boolean oneIsEnough) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security == null || input == null || input.isEmpty()) { - return input; - } else { - List output = new ArrayList<>(); - for (String platformServer: input) { - int colon = platformServer.indexOf(':', - platformServer.indexOf(']') + 1); - - int p = (colon < 0) - ? DEFAULT_PORT - : Integer.parseInt( - platformServer.substring(colon + 1)); - String s = (colon < 0) - ? platformServer - : platformServer.substring(0, colon); - try { - security.checkConnect(s, p); - output.add(platformServer); - if (oneIsEnough) { - return output; - } - } catch (SecurityException se) { - continue; - } - } - return output; - } - } } diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java index 5d5703c85eb..6c6b42021c9 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -29,8 +29,6 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Locale; import java.util.StringTokenizer; @@ -65,12 +63,10 @@ public class DnsUrl extends Uri { public static final ParseMode PARSE_MODE; static { - PrivilegedAction action = () -> - System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); ParseMode parseMode = DEFAULT_PARSE_MODE; try { - @SuppressWarnings("removal") - String mode = AccessController.doPrivileged(action); + String mode = System.getProperty( + PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); } catch (Throwable t) { parseMode = DEFAULT_PARSE_MODE; diff --git a/src/jdk.naming.rmi/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java b/src/jdk.naming.rmi/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java index 5becceb8294..e5090cbaae1 100644 --- a/src/jdk.naming.rmi/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java +++ b/src/jdk.naming.rmi/share/classes/com/sun/jndi/rmi/registry/RegistryContext.java @@ -58,11 +58,6 @@ public class RegistryContext implements Context, Referenceable { Reference reference = null; // ref used to create this context, if any - // Environment property that, if set, indicates that a security - // manager should be installed (if none is already in place). - public static final String SECURITY_MGR = - "java.naming.rmi.security.manager"; - /** * Returns a context for the registry at a given host and port. * If "host" is null, uses default host. @@ -77,9 +72,6 @@ public RegistryContext(String host, int port, Hashtable env) environment = (env == null) ? new Hashtable(5) : (Hashtable) env; - if (environment.get(SECURITY_MGR) != null) { - installSecurityMgr(); - } // chop off '[' and ']' in an IPv6 literal address if ((host != null) && (host.charAt(0) == '[')) { @@ -295,9 +287,6 @@ public Object removeFromEnvironment(String propName) public Object addToEnvironment(String propName, Object propVal) throws NamingException { - if (propName.equals(SECURITY_MGR)) { - installSecurityMgr(); - } return environment.put(propName, propVal); } @@ -412,19 +401,6 @@ private static Registry getRegistry(String host, int port, } } - /** - * Attempts to install a security manager if none is currently in - * place. - */ - @SuppressWarnings("removal") - private static void installSecurityMgr() { - - try { - System.setSecurityManager(new SecurityManager()); - } catch (Exception e) { - } - } - /** * Encodes an object prior to binding it in the registry. First, * NamingManager.getStateToBind() is invoked. If the resulting diff --git a/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java b/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java index cfa66ed9174..c2972a87092 100644 --- a/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java +++ b/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -26,8 +26,6 @@ package com.sun.jndi.url.rmi; import java.net.URI; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Hashtable; import java.util.Locale; @@ -57,12 +55,9 @@ public class rmiURLContext extends GenericURLContext { public static final ParseMode PARSE_MODE; static { - PrivilegedAction action = () -> - System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); ParseMode parseMode = DEFAULT_PARSE_MODE; try { - @SuppressWarnings("removal") - String mode = AccessController.doPrivileged(action); + String mode = System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); } catch (Throwable t) { parseMode = DEFAULT_PARSE_MODE; From 08d563ba15047020fd5f5fea80547e18898bbab2 Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Fri, 29 Nov 2024 07:50:14 +0000 Subject: [PATCH 018/171] 8345110: RISC-V: Optimize and and clean up byte reverse assembler routines Reviewed-by: mli, rehn --- .../cpu/riscv/macroAssembler_riscv.cpp | 93 +++---------------- .../cpu/riscv/macroAssembler_riscv.hpp | 6 +- src/hotspot/cpu/riscv/riscv_b.ad | 24 +++-- src/hotspot/cpu/riscv/templateTable_riscv.cpp | 28 +++--- 4 files changed, 51 insertions(+), 100 deletions(-) diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index c39a086838d..46e6bc1b534 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -2461,41 +2461,6 @@ void MacroAssembler::load_long_misaligned(Register dst, Address src, Register tm } } - -// reverse bytes in halfword in lower 16 bits and sign-extend -// Rd[15:0] = Rs[7:0] Rs[15:8] (sign-extend to 64 bits) -void MacroAssembler::revb_h_h(Register Rd, Register Rs, Register tmp) { - if (UseZbb) { - rev8(Rd, Rs); - srai(Rd, Rd, 48); - return; - } - assert_different_registers(Rs, tmp); - assert_different_registers(Rd, tmp); - srli(tmp, Rs, 8); - andi(tmp, tmp, 0xFF); - slli(Rd, Rs, 56); - srai(Rd, Rd, 48); // sign-extend - orr(Rd, Rd, tmp); -} - -// reverse bytes in lower word and sign-extend -// Rd[31:0] = Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24] (sign-extend to 64 bits) -void MacroAssembler::revb_w_w(Register Rd, Register Rs, Register tmp1, Register tmp2) { - if (UseZbb) { - rev8(Rd, Rs); - srai(Rd, Rd, 32); - return; - } - assert_different_registers(Rs, tmp1, tmp2); - assert_different_registers(Rd, tmp1, tmp2); - revb_h_w_u(Rd, Rs, tmp1, tmp2); - slli(tmp2, Rd, 48); - srai(tmp2, tmp2, 32); // sign-extend - srli(Rd, Rd, 16); - orr(Rd, Rd, tmp2); -} - // reverse bytes in halfword in lower 16 bits and zero-extend // Rd[15:0] = Rs[7:0] Rs[15:8] (zero-extend to 64 bits) void MacroAssembler::revb_h_h_u(Register Rd, Register Rs, Register tmp) { @@ -2532,56 +2497,28 @@ void MacroAssembler::revb_h_w_u(Register Rd, Register Rs, Register tmp1, Registe orr(Rd, Rd, tmp2); } -// This method is only used for revb_h -// Rd = Rs[47:0] Rs[55:48] Rs[63:56] -void MacroAssembler::revb_h_helper(Register Rd, Register Rs, Register tmp1, Register tmp2) { - assert_different_registers(Rs, tmp1, tmp2); - assert_different_registers(Rd, tmp1); - srli(tmp1, Rs, 48); - andi(tmp2, tmp1, 0xFF); - slli(tmp2, tmp2, 8); - srli(tmp1, tmp1, 8); - orr(tmp1, tmp1, tmp2); - slli(Rd, Rs, 16); - orr(Rd, Rd, tmp1); -} - -// reverse bytes in each halfword -// Rd[63:0] = Rs[55:48] Rs[63:56] Rs[39:32] Rs[47:40] Rs[23:16] Rs[31:24] Rs[7:0] Rs[15:8] -void MacroAssembler::revb_h(Register Rd, Register Rs, Register tmp1, Register tmp2) { - if (UseZbb) { - assert_different_registers(Rs, tmp1); - assert_different_registers(Rd, tmp1); - rev8(Rd, Rs); - zero_extend(tmp1, Rd, 32); - roriw(tmp1, tmp1, 16); - slli(tmp1, tmp1, 32); - srli(Rd, Rd, 32); - roriw(Rd, Rd, 16); - zero_extend(Rd, Rd, 32); - orr(Rd, Rd, tmp1); - return; - } - assert_different_registers(Rs, tmp1, tmp2); - assert_different_registers(Rd, tmp1, tmp2); - revb_h_helper(Rd, Rs, tmp1, tmp2); - for (int i = 0; i < 3; ++i) { - revb_h_helper(Rd, Rd, tmp1, tmp2); - } -} - -// reverse bytes in each word -// Rd[63:0] = Rs[39:32] Rs[47:40] Rs[55:48] Rs[63:56] Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24] +// reverse bytes in lower word, sign-extend +// Rd[32:0] = Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24] void MacroAssembler::revb_w(Register Rd, Register Rs, Register tmp1, Register tmp2) { if (UseZbb) { rev8(Rd, Rs); - rori(Rd, Rd, 32); + srai(Rd, Rd, 32); return; } assert_different_registers(Rs, tmp1, tmp2); assert_different_registers(Rd, tmp1, tmp2); - revb(Rd, Rs, tmp1, tmp2); - ror_imm(Rd, Rd, 32); + andi(tmp1, Rs, 0xFF); + slli(tmp1, tmp1, 8); + for (int step = 8; step < 24; step += 8) { + srli(tmp2, Rs, step); + andi(tmp2, tmp2, 0xFF); + orr(tmp1, tmp1, tmp2); + slli(tmp1, tmp1, 8); + } + srli(Rd, Rs, 24); + andi(Rd, Rd, 0xFF); + orr(Rd, tmp1, Rd); + sign_extend(Rd, Rd, 32); } // reverse bytes in doubleword diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index cbf69e93c5c..9c38e8424d7 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -913,13 +913,9 @@ class MacroAssembler: public Assembler { void orn(Register Rd, Register Rs1, Register Rs2); // revb - void revb_h_h(Register Rd, Register Rs, Register tmp = t0); // reverse bytes in halfword in lower 16 bits, sign-extend - void revb_w_w(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); // reverse bytes in lower word, sign-extend void revb_h_h_u(Register Rd, Register Rs, Register tmp = t0); // reverse bytes in halfword in lower 16 bits, zero-extend void revb_h_w_u(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); // reverse bytes in halfwords in lower 32 bits, zero-extend - void revb_h_helper(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2= t1); // reverse bytes in upper 16 bits (48:63) and move to lower - void revb_h(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2= t1); // reverse bytes in each halfword - void revb_w(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2= t1); // reverse bytes in each word + void revb_w(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2= t1); // reverse bytes in lower word, sign-extend void revb(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); // reverse bytes in doubleword void ror_imm(Register dst, Register src, uint32_t shift, Register tmp = t0); diff --git a/src/hotspot/cpu/riscv/riscv_b.ad b/src/hotspot/cpu/riscv/riscv_b.ad index 92e616a3063..6b7645858d8 100644 --- a/src/hotspot/cpu/riscv/riscv_b.ad +++ b/src/hotspot/cpu/riscv/riscv_b.ad @@ -181,11 +181,15 @@ instruct bytes_reverse_int_b(iRegINoSp dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesI src)); ins_cost(ALU_COST * 2); - format %{ "revb_w_w $dst, $src\t#@bytes_reverse_int_b" %} + format %{ + "rev8 $dst, $src\t#@bytes_reverse_int_b\t\n" + "srai $dst, $dst, 32\t\n" + %} ins_encode %{ assert(UseZbb, "must be"); - __ revb_w_w(as_Register($dst$$reg), as_Register($src$$reg)); + __ rev8(as_Register($dst$$reg), as_Register($src$$reg)); + __ srai(as_Register($dst$$reg), as_Register($dst$$reg), 32); %} ins_pipe(ialu_reg); @@ -209,11 +213,15 @@ instruct bytes_reverse_unsigned_short_b(iRegINoSp dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesUS src)); ins_cost(ALU_COST * 2); - format %{ "revb_h_h_u $dst, $src\t#@bytes_reverse_unsigned_short_b" %} + format %{ + "rev8 $dst, $src\t#@bytes_reverse_unsigned_short_b\t\n" + "srli $dst, $dst, 48\t\n" + %} ins_encode %{ assert(UseZbb, "must be"); - __ revb_h_h_u(as_Register($dst$$reg), as_Register($src$$reg)); + __ rev8(as_Register($dst$$reg), as_Register($src$$reg)); + __ srli(as_Register($dst$$reg), as_Register($dst$$reg), 48); %} ins_pipe(ialu_reg); @@ -223,11 +231,15 @@ instruct bytes_reverse_short_b(iRegINoSp dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesS src)); ins_cost(ALU_COST * 2); - format %{ "revb_h_h $dst, $src\t#@bytes_reverse_short_b" %} + format %{ + "rev8 $dst, $src\t#@bytes_reverse_short_b\t\n" + "srai $dst, $dst, 48\t\n" + %} ins_encode %{ assert(UseZbb, "must be"); - __ revb_h_h(as_Register($dst$$reg), as_Register($src$$reg)); + __ rev8(as_Register($dst$$reg), as_Register($src$$reg)); + __ srai(as_Register($dst$$reg), as_Register($dst$$reg), 48); %} ins_pipe(ialu_reg); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 62dc952bde0..5ae6805872a 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -1621,13 +1621,14 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) { // load branch displacement if (!is_wide) { + // Convert the 16-bit value into native byte-ordering and sign-extend __ lb(x12, at_bcp(1)); __ lbu(t1, at_bcp(2)); __ slli(x12, x12, 8); __ add(x12, x12, t1); } else { __ lwu(x12, at_bcp(1)); - __ revb_w_w(x12, x12); // reverse bytes in word and sign-extend + __ revb_w(x12, x12); } // Handle all the JSR stuff here, then exit. @@ -1892,8 +1893,8 @@ void TemplateTable::tableswitch() { // load lo & hi __ lwu(x12, Address(x11, BytesPerInt)); __ lwu(x13, Address(x11, 2 * BytesPerInt)); - __ revb_w_w(x12, x12); // reverse bytes in word (32bit) and sign-extend - __ revb_w_w(x13, x13); // reverse bytes in word (32bit) and sign-extend + __ revb_w(x12, x12); + __ revb_w(x13, x13); // check against lo & hi __ blt(x10, x12, default_case); __ bgt(x10, x13, default_case); @@ -1904,7 +1905,7 @@ void TemplateTable::tableswitch() { __ profile_switch_case(x10, x11, x12); // continue execution __ bind(continue_execution); - __ revb_w_w(x13, x13); // reverse bytes in word (32bit) and sign-extend + __ revb_w(x13, x13); __ add(xbcp, xbcp, x13); __ load_unsigned_byte(t0, Address(xbcp)); __ dispatch_only(vtos, /*generate_poll*/true); @@ -1924,7 +1925,7 @@ void TemplateTable::fast_linearswitch() { transition(itos, vtos); Label loop_entry, loop, found, continue_execution; // bswap x10 so we can avoid bswapping the table entries - __ revb_w_w(x10, x10); // reverse bytes in word (32bit) and sign-extend + __ revb_w(x10, x10); // align xbcp __ la(x9, at_bcp(BytesPerInt)); // btw: should be able to get rid of // this instruction (change offsets @@ -1932,6 +1933,9 @@ void TemplateTable::fast_linearswitch() { __ andi(x9, x9, -BytesPerInt); // set counter __ lwu(x11, Address(x9, BytesPerInt)); + // Convert the 32-bit npairs (number of pairs) into native byte-ordering + // We can use sign-extension here because npairs must be greater than or + // equal to 0 per JVM spec on 'lookupswitch' bytecode. __ revb_w(x11, x11); __ j(loop_entry); // table search @@ -1953,7 +1957,7 @@ void TemplateTable::fast_linearswitch() { __ profile_switch_case(x11, x10, x9); // continue execution __ bind(continue_execution); - __ revb_w_w(x13, x13); // reverse bytes in word (32bit) and sign-extend + __ revb_w(x13, x13); __ add(xbcp, xbcp, x13); __ lbu(t0, Address(xbcp, 0)); __ dispatch_only(vtos, /*generate_poll*/true); @@ -2005,7 +2009,9 @@ void TemplateTable::fast_binaryswitch() { __ mv(i, zr); // i = 0 __ lwu(j, Address(array, -BytesPerInt)); // j = length(array) - // Convert j into native byteordering + // Convert the 32-bit npairs (number of pairs) into native byte-ordering + // We can use sign-extension here because npairs must be greater than or + // equal to 0 per JVM spec on 'lookupswitch' bytecode. __ revb_w(j, j); // And start @@ -2024,7 +2030,7 @@ void TemplateTable::fast_binaryswitch() { // Convert array[h].match to native byte-ordering before compare __ shadd(temp, h, array, temp, 3); __ lwu(temp, Address(temp, 0)); - __ revb_w_w(temp, temp); // reverse bytes in word (32bit) and sign-extend + __ revb_w(temp, temp); Label L_done, L_greater; __ bge(key, temp, L_greater); @@ -2047,14 +2053,14 @@ void TemplateTable::fast_binaryswitch() { // Convert array[i].match to native byte-ordering before compare __ shadd(temp, i, array, temp, 3); __ lwu(temp, Address(temp, 0)); - __ revb_w_w(temp, temp); // reverse bytes in word (32bit) and sign-extend + __ revb_w(temp, temp); __ bne(key, temp, default_case); // entry found -> j = offset __ shadd(temp, i, array, temp, 3); __ lwu(j, Address(temp, BytesPerInt)); __ profile_switch_case(i, key, array); - __ revb_w_w(j, j); // reverse bytes in word (32bit) and sign-extend + __ revb_w(j, j); __ add(temp, xbcp, j); __ load_unsigned_byte(t0, Address(temp, 0)); @@ -2067,7 +2073,7 @@ void TemplateTable::fast_binaryswitch() { __ bind(default_case); __ profile_switch_default(i); __ lwu(j, Address(array, -2 * BytesPerInt)); - __ revb_w_w(j, j); // reverse bytes in word (32bit) and sign-extend + __ revb_w(j, j); __ add(temp, xbcp, j); __ load_unsigned_byte(t0, Address(temp, 0)); From f2c0d186ff8345a3a018f7d88758e5b214f07c10 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Fri, 29 Nov 2024 09:24:29 +0000 Subject: [PATCH 019/171] 8344989: Test java/foreign/TestLinker.java failed with zero: IllegalStateException: libffi call failed with status: FFI_BAD_TYPEDEF Reviewed-by: jvernee --- .../foreign/abi/fallback/FallbackLinker.java | 34 +++++++++++++++++++ test/jdk/java/foreign/TestLinker.java | 11 ++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java index 607a29e2ddf..ad5e4b97175 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java @@ -31,6 +31,7 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.SequenceLayout; import java.lang.foreign.ValueLayout; import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN; @@ -85,6 +86,7 @@ public static boolean isSupported() { @Override protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + assertNotEmpty(function); MemorySegment cif = makeCif(inferredMethodType, function, options, Arena.ofAuto()); int capturedStateMask = options.capturedCallState() @@ -111,6 +113,7 @@ protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDe @Override protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + assertNotEmpty(function); MemorySegment cif = makeCif(targetType, function, options, Arena.ofAuto()); UpcallData invData = new UpcallData(function.returnLayout().orElse(null), function.argumentLayouts(), cif); @@ -325,4 +328,35 @@ class Holder { return Holder.CANONICAL_LAYOUTS; } + + private static void assertNotEmpty(FunctionDescriptor fd) { + fd.returnLayout().ifPresent(FallbackLinker::assertNotEmpty); + fd.argumentLayouts().forEach(FallbackLinker::assertNotEmpty); + } + + // Recursively tests for emptiness + private static void assertNotEmpty(MemoryLayout layout) { + switch (layout) { + case GroupLayout gl -> { + if (gl.memberLayouts().isEmpty()) { + throw empty(gl); + } else { + gl.memberLayouts().forEach(FallbackLinker::assertNotEmpty); + } + } + case SequenceLayout sl -> { + if (sl.elementCount() == 0) { + throw empty(sl); + } else { + assertNotEmpty(sl.elementLayout()); + } + } + default -> { /* do nothing */ } + } + } + + private static IllegalArgumentException empty(MemoryLayout layout) { + return new IllegalArgumentException("The layout " + layout + " is empty"); + } + } diff --git a/test/jdk/java/foreign/TestLinker.java b/test/jdk/java/foreign/TestLinker.java index 798e5dd04db..902b938ac62 100644 --- a/test/jdk/java/foreign/TestLinker.java +++ b/test/jdk/java/foreign/TestLinker.java @@ -23,12 +23,13 @@ /* * @test - * @modules java.base/jdk.internal.foreign + * @modules java.base/jdk.internal.foreign java.base/jdk.internal.foreign.abi.fallback * @run testng TestLinker * @run testng/othervm TestLinker */ import jdk.internal.foreign.CABI; +import jdk.internal.foreign.abi.fallback.FallbackLinker; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -250,7 +251,13 @@ public void sequenceOfZeroElements() { var padding5a1 = MemoryLayout.paddingLayout(5); var struct8a8 = MemoryLayout.structLayout(sequence0a8, sequence3a1, padding5a1); var fd = FunctionDescriptor.of(struct8a8, struct8a8, struct8a8); - linker.downcallHandle(fd); + if (linker.getClass().equals(FallbackLinker.class)) { + // The fallback linker does not support empty layouts (FFI_BAD_TYPEDEF) + var iae = expectThrows(IllegalArgumentException.class, () -> linker.downcallHandle(fd)); + assertTrue(iae.getMessage().contains("is empty")); + } else { + linker.downcallHandle(fd); + } } @DataProvider From 1a7a184e4124939e1c0c29be227f619e838fbc87 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Fri, 29 Nov 2024 09:54:40 +0000 Subject: [PATCH 020/171] 8344293: Remove empty Test files left over from JDK-8305895 Reviewed-by: jsjolen, rkennke, shade --- test/hotspot/jtreg/gtest/MetaspaceUtilsGtests.java | 0 test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/hotspot/jtreg/gtest/MetaspaceUtilsGtests.java delete mode 100644 test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java diff --git a/test/hotspot/jtreg/gtest/MetaspaceUtilsGtests.java b/test/hotspot/jtreg/gtest/MetaspaceUtilsGtests.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java b/test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java deleted file mode 100644 index e69de29bb2d..00000000000 From 095e769f47682efe4e34bb46c0da2c5a2e283a7f Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 29 Nov 2024 09:55:29 +0000 Subject: [PATCH 021/171] 8345237: 32-bit Zero builds fail with assert(has_klass_gap()) failed: precondition Reviewed-by: mli, jwaters, rkennke --- src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index fa572537c2f..3f3632c0eea 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -2013,7 +2013,9 @@ void BytecodeInterpreter::run(interpreterState istate) { oopDesc::release_set_mark(result, ik->prototype_header()); } else { oopDesc::set_mark(result, markWord::prototype()); - oopDesc::set_klass_gap(result, 0); + if (oopDesc::has_klass_gap()) { + oopDesc::set_klass_gap(result, 0); + } oopDesc::release_set_klass(result, ik); } oop obj = cast_to_oop(result); From ece0401054b40db43f5239e7841418c5efc814d0 Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Fri, 29 Nov 2024 11:38:23 +0000 Subject: [PATCH 022/171] 8345052: Harden StampedLock Reviewed-by: alanb, vklang --- .../util/concurrent/locks/StampedLock.java | 158 ++++++++++++------ .../locks/StampedLock/OOMEInStampedLock.java | 142 ++++++++++++++++ 2 files changed, 250 insertions(+), 50 deletions(-) create mode 100644 test/jdk/java/util/concurrent/locks/StampedLock/OOMEInStampedLock.java diff --git a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java index b9f0e9340b7..3fbfad875d6 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java @@ -289,7 +289,8 @@ public class StampedLock implements java.io.Serializable { * Nearly all of these mechanics are carried out in methods * acquireWrite and acquireRead, that, as typical of such code, * sprawl out because actions and retries rely on consistent sets - * of locally cached reads. + * of locally cached reads, and include fall-backs for exceptional + * cases including OutOfMemoryErrors and JVM exceptions. * * For an explanation of the use of acquireFence, see * http://gee.cs.oswego.edu/dl/html/j9mm.html as well as Boehm's @@ -1182,8 +1183,7 @@ private boolean casTail(Node c, Node v) { } /** tries once to CAS a new dummy node for head */ - private void tryInitializeHead() { - Node h = new WriterNode(); + private void tryInitializeHead(Node h) { if (U.compareAndSetReference(this, HEAD, null, h)) tail = h; } @@ -1203,6 +1203,7 @@ private long acquireWrite(boolean interruptible, boolean timed, long time) { boolean interrupted = false, first = false; WriterNode node = null; Node pred = null; + long nanos = 0L; for (long s, nextState;;) { if (!first && (pred = (node == null) ? null : node.prev) != null && !(first = (head == pred))) { @@ -1227,12 +1228,23 @@ private long acquireWrite(boolean interruptible, boolean timed, long time) { } return nextState; } else if (node == null) { // retry before enqueuing - node = new WriterNode(); + try { + node = new WriterNode(); + } catch (OutOfMemoryError oome) { + return spinLockOnOOME(true, interruptible, timed, time); + } } else if (pred == null) { // try to enqueue Node t = tail; node.setPrevRelaxed(t); - if (t == null) - tryInitializeHead(); + if (t == null) { // try to initialize + Node h; + try { + h = new WriterNode(); + } catch (OutOfMemoryError oome) { + return spinLockOnOOME(true, interruptible, timed, time); + } + tryInitializeHead(h); + } else if (!casTail(t, node)) node.setPrevRelaxed(null); // back out else @@ -1244,21 +1256,25 @@ else if (!casTail(t, node)) if (node.waiter == null) node.waiter = Thread.currentThread(); node.status = WAITING; - } else { - long nanos; - spins = postSpins = (byte)((postSpins << 1) | 1); - if (!timed) - LockSupport.park(this); - else if ((nanos = time - System.nanoTime()) > 0L) - LockSupport.parkNanos(this, nanos); - else - break; + } else if (!timed || (nanos = time - System.nanoTime()) > 0L) { + try { + if (!timed) + LockSupport.park(this); + else + LockSupport.parkNanos(this, nanos); + } catch (Error | RuntimeException ex) { + cancelAcquire(node); + throw ex; + } node.clearStatus(); if ((interrupted |= Thread.interrupted()) && interruptible) break; - } + spins = postSpins = (byte)((postSpins << 1) | 1); + } else + break; } - return cancelAcquire(node, interrupted); + cancelAcquire(node); + return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; } /** @@ -1285,11 +1301,23 @@ private long acquireRead(boolean interruptible, boolean timed, long time) { if ((t == null || (tailPred = t.prev) == null) && (nextState = tryAcquireRead()) != 0L) // try now if empty return nextState; - else if (t == null) - tryInitializeHead(); + else if (t == null) { + Node h; + try { + h = new WriterNode(); + } catch (OutOfMemoryError oome) { + return spinLockOnOOME(false, interruptible, timed, time); + } + tryInitializeHead(h); + } else if (tailPred == null || !(t instanceof ReaderNode)) { - if (node == null) - node = new ReaderNode(); + if (node == null) { + try { + node = new ReaderNode(); + } catch (OutOfMemoryError oome) { + return spinLockOnOOME(false, interruptible, timed, time); + } + } if (tail == t) { node.setPrevRelaxed(t); if (casTail(t, node)) { @@ -1302,8 +1330,13 @@ else if (tailPred == null || !(t instanceof ReaderNode)) { for (boolean attached = false;;) { if (leader.status < 0 || leader.prev == null) break; - else if (node == null) - node = new ReaderNode(); + else if (node == null) { + try { + node = new ReaderNode(); + } catch (OutOfMemoryError oome) { + return spinLockOnOOME(false, interruptible, timed, time); + } + } else if (node.waiter == null) node.waiter = Thread.currentThread(); else if (!attached) { @@ -1313,15 +1346,22 @@ else if (!attached) { if (!attached) node.setCowaitersRelaxed(null); } else { - long nanos = 0L; - if (!timed) - LockSupport.park(this); - else if ((nanos = time - System.nanoTime()) > 0L) - LockSupport.parkNanos(this, nanos); + long nanos = (timed) ? time - System.nanoTime(): 0L; + try { + if (!timed) + LockSupport.park(this); + else if (nanos > 0L) + LockSupport.parkNanos(this, nanos); + } catch (Error | RuntimeException ex) { + cancelCowaiter(node, leader); + throw ex; + } interrupted |= Thread.interrupted(); if ((interrupted && interruptible) || - (timed && nanos <= 0L)) - return cancelCowaiter(node, leader, interrupted); + (timed && nanos <= 0L)) { + cancelCowaiter(node, leader); + return (interrupted) ? INTERRUPTED : 0L; + } } } if (node != null) @@ -1341,6 +1381,7 @@ else if ((nanos = time - System.nanoTime()) > 0L) byte spins = 0, postSpins = 0; // retries upon unpark of first thread boolean first = false; Node pred = null; + long nanos = 0L; for (long nextState;;) { if (!first && (pred = node.prev) != null && !(first = (head == pred))) { @@ -1371,21 +1412,25 @@ else if ((nanos = time - System.nanoTime()) > 0L) if (node.waiter == null) node.waiter = Thread.currentThread(); node.status = WAITING; - } else { - long nanos; - spins = postSpins = (byte)((postSpins << 1) | 1); - if (!timed) - LockSupport.park(this); - else if ((nanos = time - System.nanoTime()) > 0L) - LockSupport.parkNanos(this, nanos); - else - break; + } else if (!timed || (nanos = time - System.nanoTime()) > 0) { + try { + if (!timed) + LockSupport.park(this); + else + LockSupport.parkNanos(this, nanos); + } catch (Error | RuntimeException ex) { + cancelAcquire(node); + throw ex; + } node.clearStatus(); if ((interrupted |= Thread.interrupted()) && interruptible) break; - } + spins = postSpins = (byte)((postSpins << 1) | 1); + } else + break; } - return cancelAcquire(node, interrupted); + cancelAcquire(node); + return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; } // Cancellation support @@ -1450,10 +1495,8 @@ private void unlinkCowaiter(ReaderNode node, ReaderNode leader) { * to recheck status. * * @param node the waiter (may be null if not yet enqueued) - * @param interrupted if already interrupted - * @return INTERRUPTED if interrupted or Thread.interrupted, else zero */ - private long cancelAcquire(Node node, boolean interrupted) { + private void cancelAcquire(Node node) { if (node != null) { node.waiter = null; node.status = CANCELLED; @@ -1461,7 +1504,6 @@ private long cancelAcquire(Node node, boolean interrupted) { if (node instanceof ReaderNode) signalCowaiters((ReaderNode)node); } - return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; } /** @@ -1470,17 +1512,33 @@ private long cancelAcquire(Node node, boolean interrupted) { * * @param node if non-null, the waiter * @param leader if non-null, the node heading cowaiters list - * @param interrupted if already interrupted - * @return INTERRUPTED if interrupted or Thread.interrupted, else zero */ - private long cancelCowaiter(ReaderNode node, ReaderNode leader, - boolean interrupted) { + private void cancelCowaiter(ReaderNode node, ReaderNode leader) { if (node != null) { node.waiter = null; node.status = CANCELLED; unlinkCowaiter(node, leader); } - return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; + } + + /** + * Fallback upon encountering OutOfMemoryErrors + */ + private long spinLockOnOOME(boolean write, boolean interruptible, + boolean timed, long time) { + long startTime = (timed) ? System.nanoTime() : 0L; + for (int spins = 0;;) { + long s = (write) ? tryAcquireWrite() : tryAcquireRead(); + if (s != 0L) + return s; + Thread.onSpinWait(); + if ((++spins & (1 << 8)) == 0) { // occasionally check + if (interruptible && Thread.interrupted()) + return INTERRUPTED; + if (timed && System.nanoTime() - startTime > time) + return 0L; + } + } } // Unsafe diff --git a/test/jdk/java/util/concurrent/locks/StampedLock/OOMEInStampedLock.java b/test/jdk/java/util/concurrent/locks/StampedLock/OOMEInStampedLock.java new file mode 100644 index 00000000000..2346ad39fc4 --- /dev/null +++ b/test/jdk/java/util/concurrent/locks/StampedLock/OOMEInStampedLock.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013, 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. + */ + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Phaser; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.StampedLock; +import java.util.function.Consumer; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * @test + * @bug 8066859 + * @summary An adaptation of OOMEInAQS test for StampedLocks + * @requires vm.gc.G1 + * @run main/othervm -XX:+UseG1GC -XX:-UseGCOverheadLimit -Xmx48M -XX:-UseTLAB OOMEInStampedLock + */ + +public class OOMEInStampedLock extends Thread { + static final int NTHREADS = 3; + static final int NREPS = 100; + // statically allocate + static final StampedLock stampedLock = new StampedLock(); + static final Lock wLock = stampedLock.asWriteLock(); + static final Lock rLock = stampedLock.asReadLock(); + static final CountDownLatch started = new CountDownLatch(1); + static final CountDownLatch filled = new CountDownLatch(1); + static final CountDownLatch canFill = new CountDownLatch(NTHREADS); + static volatile Object data; + static volatile Throwable exception; + static int turn; + + /** + * For each of NTHREADS threads, REPS times: Take turns + * executing. Introduce OOM using fillHeap during runs. In + * addition to testing AQS, the CountDownLatches ensure that + * methods execute at least once before OutOfMemory occurs, to + * avoid uncontrollable impact of OOME during class-loading. + */ + public static void main(String[] args) throws Throwable { + OOMEInStampedLock[] threads = new OOMEInStampedLock[NTHREADS]; + for (int i = 0; i < NTHREADS; ++i) + (threads[i] = new OOMEInStampedLock(i)).start(); + started.countDown(); + canFill.await(); + long t0 = System.nanoTime(); + data = fillHeap(); + filled.countDown(); + long t1 = System.nanoTime(); + for (int i = 0; i < NTHREADS; ++i) + threads[i].join(); + data = null; // free heap before reporting and terminating + System.gc(); + Throwable ex = exception; + if (ex != null) + throw ex; + System.out.println( + "fillHeap time: " + (t1 - t0) / 1000_000 + + " millis, whole test time: " + (System.nanoTime() - t0) / 1000_000 + + " millis" + ); + } + + final int tid; + OOMEInStampedLock(int tid) { + this.tid = tid; + } + + @Override + public void run() { + int id = tid, nextId = (id + 1) % NTHREADS; + final Lock wl = wLock, rl = rLock; + try { + started.await(); + for (int i = 0; i < NREPS; i++) { + int t; + rl.lock(); t = turn; rl.unlock(); + wl.lock(); + try { + if (turn == t && turn == id) + turn = nextId; + } finally { + wl.unlock(); + } + if (i == 2) { // Subsequent AQS methods encounter OOME + canFill.countDown(); + filled.await(); + } + } + data = null; + System.gc(); // avoid getting stuck while exiting + } catch (Throwable ex) { + data = null; + System.gc(); // avoid nested OOME + exception = ex; + } + } + + static Object[] fillHeap() { + Object[] first = null, last = null; + int size = 1 << 20; + while (size > 0) { + try { + Object[] array = new Object[size]; + if (first == null) { + first = array; + } else { + last[0] = array; + } + last = array; + } catch (OutOfMemoryError oome) { + size = size >>> 1; + } + } + return first; + } +} From 8858de393a5cb4f9f1150a6cdf388266362a9559 Mon Sep 17 00:00:00 2001 From: Sharath TN Date: Fri, 29 Nov 2024 12:27:06 +0000 Subject: [PATCH 023/171] 8338571: [TestBug] DefaultCloseOperation.java test not working as expected wrt instruction after JDK-8325851 fix Reviewed-by: aivanov, dnguyen --- test/jdk/javax/swing/JFrame/DefaultCloseOperation.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java b/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java index e08efdb8854..a20c32b83bc 100644 --- a/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java +++ b/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java @@ -99,7 +99,6 @@ public void init() { CloseOpFrame testFrame = new CloseOpFrame(); testFrame.setLocationRelativeTo(null); - PassFailJFrame.addTestWindow(testFrame); add(new JLabel("JFrame Default Close Operation:")); frameCloseOp = new JComboBox<>(); @@ -127,7 +126,6 @@ public void init() { testDialog = new CloseOpDialog(testFrame); testDialog.setLocationRelativeTo(null); - PassFailJFrame.addTestWindow(testDialog); add(new JLabel("JDialog Default Close Operation:")); dialogCloseOp = new JComboBox<>(); From 6bea1b6cf1f64ce06c2028fe4dbc44f70778168f Mon Sep 17 00:00:00 2001 From: Volker Simonis Date: Fri, 29 Nov 2024 12:30:43 +0000 Subject: [PATCH 024/171] 8344727: [JVMCI] Export the CompileBroker compilation activity mode for Truffle compiler control Reviewed-by: dnsimon --- src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 5 +++++ .../share/classes/jdk/vm/ci/hotspot/CompilerToVM.java | 6 ++++++ .../classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index aa8ce28b7c5..9052e622894 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -3223,6 +3223,10 @@ C2V_VMENTRY(void, getOopMapAt, (JNIEnv* env, jobject, ARGUMENT_PAIR(method), JVMCIENV->copy_longs_from((jlong*)oop_map_buf, oop_map, 0, nwords); C2V_END +C2V_VMENTRY_0(jint, getCompilationActivityMode, (JNIEnv* env, jobject)) + return CompileBroker::get_compilation_activity_mode(); +} + #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(c2v_ ## f)) @@ -3385,6 +3389,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "notifyCompilerInliningEvent", CC "(I" HS_METHOD2 HS_METHOD2 "ZLjava/lang/String;I)V", FN_PTR(notifyCompilerInliningEvent)}, {CC "getOopMapAt", CC "(" HS_METHOD2 "I[J)V", FN_PTR(getOopMapAt)}, {CC "updateCompilerThreadCanCallJava", CC "(Z)Z", FN_PTR(updateCompilerThreadCanCallJava)}, + {CC "getCompilationActivityMode", CC "()I", FN_PTR(getCompilationActivityMode)}, }; int CompilerToVM::methods_count() { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java index 12c6dfd3f9d..894fca94a77 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java @@ -1522,4 +1522,10 @@ void getOopMapAt(HotSpotResolvedJavaMethodImpl method, int bci, long[] oopMap) { * @returns false if no change was made, otherwise true */ native boolean updateCompilerThreadCanCallJava(boolean newState); + + /** + * Returns the current {@code CompileBroker} compilation activity mode which is one of: + * {@code stop_compilation = 0}, {@code run_compilation = 1} or {@code shutdown_compilation = 2} + */ + native int getCompilationActivityMode(); } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index 616b5a23f64..bc221c46717 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -1488,4 +1488,12 @@ JVMCIError exitHotSpotWithMessage(int status, String format, Object... args) { exitHotSpot(status); throw JVMCIError.shouldNotReachHere(); } + + /** + * Returns HotSpot's {@code CompileBroker} compilation activity mode which is one of: + * {@code stop_compilation = 0}, {@code run_compilation = 1} or {@code shutdown_compilation = 2} + */ + public int getCompilationActivityMode() { + return compilerToVm.getCompilationActivityMode(); + } } From 4da7c3548436ffffb009828891df0d13d47370e3 Mon Sep 17 00:00:00 2001 From: Tobias Holenstein Date: Fri, 29 Nov 2024 13:53:18 +0000 Subject: [PATCH 025/171] 8314512: IGV: clean up hierarchical layout code Reviewed-by: chagedorn, rcastanedalo --- .../com/sun/hotspot/igv/data/InputNode.java | 5 - .../java/com/sun/hotspot/igv/graph/Block.java | 49 +- .../hotspot/igv/graph/BlockConnection.java | 23 +- .../com/sun/hotspot/igv/graph/Diagram.java | 46 +- .../com/sun/hotspot/igv/graph/Figure.java | 76 +- .../hotspot/igv/graph/FigureConnection.java | 34 +- .../com/sun/hotspot/igv/graph/InputSlot.java | 5 +- .../com/sun/hotspot/igv/graph/OutputSlot.java | 5 +- .../java/com/sun/hotspot/igv/graph/Slot.java | 31 +- .../igv/hierarchicallayout/ClusterEdge.java | 6 +- .../ClusterIngoingConnection.java | 6 +- .../ClusterInputSlotNode.java | 20 +- .../igv/hierarchicallayout/ClusterNode.java | 46 +- .../ClusterOutgoingConnection.java | 6 +- .../ClusterOutputSlotNode.java | 20 +- .../HierarchicalCFGLayoutManager.java | 163 +- .../HierarchicalClusterLayoutManager.java | 126 +- .../HierarchicalLayoutManager.java | 1963 +++++------------ .../HierarchicalStableLayoutManager.java | 877 +++----- .../InterClusterConnection.java | 6 +- .../igv/hierarchicallayout/LayoutEdge.java | 202 +- .../igv/hierarchicallayout/LayoutGraph.java | 433 ++++ .../igv/hierarchicallayout/LayoutLayer.java | 206 ++ .../hierarchicallayout}/LayoutManager.java | 19 +- .../igv/hierarchicallayout/LayoutNode.java | 525 ++++- .../LinearLayoutManager.java | 70 - .../igv/hierarchicallayout/Timing.java | 67 - .../com/sun/hotspot/igv/layout/Cluster.java | 15 +- .../sun/hotspot/igv/layout/LayoutGraph.java | 205 -- .../java/com/sun/hotspot/igv/layout/Link.java | 4 +- .../com/sun/hotspot/igv/util/Statistics.java | 40 - .../sun/hotspot/igv/view/DiagramScene.java | 467 ++-- .../hotspot/igv/view/EditorTopComponent.java | 12 +- .../hotspot/igv/view/widgets/BlockWidget.java | 21 +- .../igv/view/widgets/FigureWidget.java | 118 +- .../igv/view/widgets/InputSlotWidget.java | 16 +- .../hotspot/igv/view/widgets/LineWidget.java | 75 +- .../igv/view/widgets/OutputSlotWidget.java | 21 +- .../hotspot/igv/view/widgets/SlotWidget.java | 35 +- src/utils/IdealGraphVisualizer/igv.sh | 4 +- 40 files changed, 2952 insertions(+), 3116 deletions(-) create mode 100644 src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java create mode 100644 src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java rename src/utils/IdealGraphVisualizer/{Layout/src/main/java/com/sun/hotspot/igv/layout => HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout}/LayoutManager.java (63%) delete mode 100644 src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LinearLayoutManager.java delete mode 100644 src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/Timing.java delete mode 100644 src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutGraph.java delete mode 100644 src/utils/IdealGraphVisualizer/Util/src/main/java/com/sun/hotspot/igv/util/Statistics.java diff --git a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java index f2e1ad16ea6..62b7aa4c034 100644 --- a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java +++ b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java @@ -63,11 +63,6 @@ public boolean equals(Object obj) { Objects.equals(getProperties(), other.getProperties()); } - @Override - public int hashCode() { - return Objects.hash(id, getProperties()); - } - @Override public String toString() { return "Node " + id + " " + getProperties().toString(); diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Block.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Block.java index 483ca9d40f0..319f41a9249 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Block.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Block.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -24,11 +24,12 @@ package com.sun.hotspot.igv.graph; import com.sun.hotspot.igv.data.InputBlock; +import com.sun.hotspot.igv.data.InputNode; import com.sun.hotspot.igv.layout.Cluster; -import java.awt.Dimension; +import com.sun.hotspot.igv.layout.Vertex; +import java.awt.Point; import java.awt.Rectangle; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @@ -36,9 +37,9 @@ */ public class Block implements Cluster { - private InputBlock inputBlock; + protected final InputBlock inputBlock; private Rectangle bounds; - private Diagram diagram; + private final Diagram diagram; public Block(InputBlock inputBlock, Diagram diagram) { this.inputBlock = inputBlock; @@ -59,14 +60,33 @@ public Set getSuccessors() { return succs; } - public Dimension getNodeOffset() { - return new Dimension(0, -Figure.getVerticalOffset()); + public List getVertices() { + List vertices = new ArrayList<>(); + for (InputNode inputNode : inputBlock.getNodes()) { + if (diagram.hasFigure(inputNode)) { + vertices.add(diagram.getFigure(inputNode)); + } + } + return vertices; } public void setBounds(Rectangle r) { this.bounds = r; } + @Override + public void setPosition(Point p) { + if (bounds != null) { + bounds.setLocation(p); + } + } + + @Override + public Point getPosition() { + return bounds.getLocation(); + } + + @Override public Rectangle getBounds() { return bounds; } @@ -79,5 +99,18 @@ public int compareTo(Cluster o) { public String toString() { return inputBlock.getName(); } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Block other = (Block) obj; + return inputBlock.equals(other.inputBlock); + } + + @Override + public int hashCode() { + return inputBlock.hashCode(); + } } diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/BlockConnection.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/BlockConnection.java index 84b7a4ec09f..dc39a702cdb 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/BlockConnection.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/BlockConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -27,6 +27,7 @@ import java.awt.Color; import java.awt.Point; import java.util.List; +import java.util.Objects; public class BlockConnection implements Connection { @@ -85,11 +86,6 @@ public Block getToCluster() { return destinationBlock; } - @Override - public boolean isVIP() { - return true; - } - @Override public List getControlPoints() { return controlPoints; @@ -104,4 +100,19 @@ public void setControlPoints(List list) { public boolean hasSlots() { return false; } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BlockConnection that)) return false; + return Objects.equals(this.sourceBlock, that.sourceBlock) && + Objects.equals(this.destinationBlock, that.destinationBlock) && + Objects.equals(this.label, that.label); + } + + @Override + public int hashCode() { + return Objects.hash(sourceBlock, destinationBlock, label); + } } diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java index 191996f2ee9..bb5e3358a30 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -34,7 +34,7 @@ */ public class Diagram { - private List
figures; + private final Map figures; private final Map blocks; private final String nodeText; private final String shortNodeText; @@ -63,7 +63,7 @@ public Diagram(InputGraph graph, String nodeText, String shortNodeText, this.nodeText = nodeText; this.shortNodeText = shortNodeText; this.tinyNodeText = tinyNodeText; - this.figures = new ArrayList<>(); + this.figures = new LinkedHashMap<>(); this.blocks = new LinkedHashMap<>(8); this.blockConnections = new HashSet<>(); this.cfg = false; @@ -81,7 +81,7 @@ public Diagram(InputGraph graph, String nodeText, String shortNodeText, f.getProperties().add(n.getProperties()); f.setBlock(blocks.get(graph.getBlock(n))); figureHash.put(n.getId(), f); - this.figures.add(f); + this.figures.put(n, f); } for (InputEdge e : graph.getEdges()) { @@ -113,7 +113,7 @@ public Diagram(InputGraph graph, String nodeText, String shortNodeText, } } - for (Figure f : figures) { + for (Figure f : figures.values()) { int i = 0; for (InputSlot inputSlot : f.getInputSlots()) { inputSlot.setOriginalIndex(i); @@ -133,6 +133,15 @@ public Block getBlock(InputBlock b) { return blocks.get(b); } + public boolean hasFigure(InputNode n) { + return figures.containsKey(n); + } + + public Figure getFigure(InputNode n) { + assert figures.containsKey(n); + return figures.get(n); + } + public boolean hasBlock(InputBlock b) { return blocks.containsKey(b); } @@ -158,7 +167,7 @@ public Collection getInputBlocks() { } public List
getFigures() { - return Collections.unmodifiableList(figures); + return Collections.unmodifiableList(new ArrayList<>(figures.values())); } public FigureConnection createConnection(InputSlot inputSlot, OutputSlot outputSlot, String label) { @@ -167,7 +176,7 @@ public FigureConnection createConnection(InputSlot inputSlot, OutputSlot outputS return new FigureConnection(inputSlot, outputSlot, label); } - public void removeAllBlocks(Set blocksToRemove) { + public void removeAllBlocks(Collection blocksToRemove) { Set
figuresToRemove = new HashSet<>(); for (Block b : blocksToRemove) { for (Figure f : getFigures()) { @@ -182,18 +191,12 @@ public void removeAllBlocks(Set blocksToRemove) { } } - public void removeAllFigures(Set
figuresToRemove) { + public void removeAllFigures(Collection
figuresToRemove) { for (Figure f : figuresToRemove) { freeFigure(f); - } + figures.remove(f.getInputNode()); - ArrayList
newFigures = new ArrayList<>(); - for (Figure f : this.figures) { - if (!figuresToRemove.contains(f)) { - newFigures.add(f); - } } - figures = newFigures; } private void freeFigure(Figure succ) { @@ -215,15 +218,14 @@ private void freeFigure(Figure succ) { } - public void removeFigure(Figure succ) { - assert this.figures.contains(succ); - freeFigure(succ); - this.figures.remove(succ); + public void removeFigure(Figure figure) { + freeFigure(figure); + this.figures.remove(figure.getInputNode()); } public Set getConnections() { Set connections = new HashSet<>(); - for (Figure f : figures) { + for (Figure f : figures.values()) { for (InputSlot s : f.getInputSlots()) { connections.addAll(s.getConnections()); } @@ -246,7 +248,7 @@ public void printStatistics() { System.out.println("============================================================="); System.out.println("Diagram statistics"); - List
tmpFigures = getFigures(); + Collection
tmpFigures = getFigures(); Set connections = getConnections(); System.out.println("Number of figures: " + tmpFigures.size()); @@ -278,7 +280,7 @@ public int compare(Figure a, Figure b) { } public Figure getRootFigure() { - Properties.PropertySelector
selector = new Properties.PropertySelector<>(figures); + Properties.PropertySelector
selector = new Properties.PropertySelector<>(figures.values()); Figure root = selector.selectSingle(new Properties.StringPropertyMatcher("name", "Root")); if (root == null) { root = selector.selectSingle(new Properties.StringPropertyMatcher("name", "Start")); diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java index f02b4657cea..23c7d136d5b 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -33,13 +33,11 @@ public class Figure extends Properties.Entity implements Vertex { - public static final int INSET = 8; public static final int SLOT_WIDTH = 10; - public static final int OVERLAPPING = 6; - public static final int SLOT_START = 4; - public static final int SLOT_OFFSET = 8; - public static final int TOP_CFG_HEIGHT = 7; - public static final int BOTTOM_CFG_HEIGHT = 6; + public static final int SLOT_HEIGHT = 10; + public static final int BORDER = 1; + public static final int PADDING = 4; + public static final int SLOT_OFFSET = 16; public static final int WARNING_WIDTH = 16; public static final double BOLD_LINE_FACTOR = 1.06; protected List inputSlots; @@ -67,23 +65,21 @@ public int getHeight() { } private void updateHeight() { - String nodeText = diagram.getNodeText(); - int lines = nodeText.split("\n").length; - if (hasInputList() && lines > 1) { - lines++; - } - if (getProperties().get("extra_label") != null) { - lines++; - } - heightCash = lines * metrics.getHeight() + INSET; + heightCash = getLines().length * metrics.getHeight() + 2 * PADDING; + heightCash += getSlotsHeight(); + } + + public int getSlotsHeight() { + int slotHeight = 0; if (diagram.isCFG()) { if (hasNamedInputSlot()) { - heightCash += TOP_CFG_HEIGHT; + slotHeight += Figure.SLOT_HEIGHT; } if (hasNamedOutputSlot()) { - heightCash += BOTTOM_CFG_HEIGHT; + slotHeight += Figure.SLOT_HEIGHT; } } + return slotHeight; } public static List getAllBefore(List inputList, T tIn) { @@ -117,19 +113,20 @@ public void setWidth(int width) { } private void updateWidth() { - int max = 0; - for (String s : getLines()) { - int cur = metrics.stringWidth(s); - if (cur > max) { - max = cur; - } - } - widthCash = (int)(max * BOLD_LINE_FACTOR) + INSET; - if (getWarning() != null) { - widthCash += WARNING_WIDTH; + widthCash = 0; + for (String s : getLines()) { + int cur = metrics.stringWidth(s); + if (cur > widthCash) { + widthCash = cur; } - widthCash = Math.max(widthCash, Figure.getSlotsWidth(inputSlots)); - widthCash = Math.max(widthCash, Figure.getSlotsWidth(outputSlots)); + } + widthCash += 2 * PADDING; + if (getWarning() != null) { + widthCash += WARNING_WIDTH + PADDING; + } + widthCash = Math.max(widthCash, Figure.getSlotsWidth(inputSlots)); + widthCash = Math.max(widthCash, Figure.getSlotsWidth(outputSlots)); + widthCash = (int)(widthCash * BOLD_LINE_FACTOR); } protected Figure(Diagram diagram, int id, InputNode node) { @@ -231,10 +228,9 @@ public InputNode getInputNode() { return inputNode; } - public InputSlot createInputSlot() { + public void createInputSlot() { InputSlot slot = new InputSlot(this, -1); inputSlots.add(slot); - return slot; } public void removeSlot(Slot s) { @@ -341,7 +337,6 @@ public void updateLines() { } inputLabel = nodeTinyLabel; } - assert(inputLabel != null); int gapSize = is.gapSize(); if (gapSize == 1) { inputs.add("_"); @@ -379,22 +374,23 @@ public void updateLines() { @Override public Dimension getSize() { - int width = Math.max(getWidth(), Figure.SLOT_WIDTH * (Math.max(inputSlots.size(), outputSlots.size()) + 1)); - int height = getHeight() + (diagram.isCFG() ? 0 : 2 * Figure.SLOT_WIDTH - 2 * Figure.OVERLAPPING); + int width = getWidth(); + int height = getHeight(); return new Dimension(width, height); } @Override public boolean equals(Object o) { - if (!(o instanceof Figure)) { + if (this == o) return true; + if (!(o instanceof Figure other)) { return false; } - return getInputNode().equals(((Figure) o).getInputNode()); + return Objects.equals(this.getInputNode(), other.getInputNode()); } @Override public int hashCode() { - return getInputNode().hashCode(); + return Objects.hash(getInputNode()); } @Override @@ -402,10 +398,6 @@ public String toString() { return idString; } - public static int getVerticalOffset() { - return Figure.SLOT_WIDTH - Figure.OVERLAPPING; - } - public Cluster getCluster() { return block; } diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/FigureConnection.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/FigureConnection.java index 186a6b63f17..6970666b655 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/FigureConnection.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/FigureConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -29,6 +29,7 @@ import java.awt.Point; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @@ -41,7 +42,7 @@ public class FigureConnection implements Connection { private Color color; private ConnectionStyle style; private List controlPoints; - private String label; + private final String label; protected FigureConnection(InputSlot inputSlot, OutputSlot outputSlot, String label) { this.inputSlot = inputSlot; @@ -121,6 +122,14 @@ public Port getFrom() { return outputSlot; } + public Figure getFromFigure() { + return outputSlot.getFigure(); + } + + public Figure getToFigure() { + return inputSlot.getFigure(); + } + @Override public Cluster getFromCluster() { return getFrom().getVertex().getCluster(); @@ -136,11 +145,6 @@ public Cluster getToCluster() { return getTo().getVertex().getCluster(); } - @Override - public boolean isVIP() { - return style == ConnectionStyle.BOLD; - } - @Override public List getControlPoints() { return controlPoints; @@ -158,15 +162,15 @@ public boolean hasSlots() { @Override public boolean equals(Object o) { - if (!(o instanceof FigureConnection)) { - return false; - } - - return getInputSlot().getFigure().equals(((FigureConnection)o).getInputSlot().getFigure()) - && getOutputSlot().getFigure().equals(((FigureConnection)o).getOutputSlot().getFigure()) - && getInputSlot().getPosition() == ((FigureConnection)o).getInputSlot().getPosition() - && getOutputSlot().getPosition() == ((FigureConnection) o).getOutputSlot().getPosition(); + if (this == o) return true; + if (!(o instanceof FigureConnection that)) return false; + return Objects.equals(this.outputSlot, that.outputSlot) && + Objects.equals(this.inputSlot, that.inputSlot); } + @Override + public int hashCode() { + return Objects.hash(outputSlot, inputSlot); + } } diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/InputSlot.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/InputSlot.java index 71c29aa8db1..192f24f2f85 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/InputSlot.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/InputSlot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -25,6 +25,7 @@ import java.awt.Point; import java.util.List; +import java.util.Objects; /** * @@ -72,7 +73,7 @@ public Point getRelativePosition() { int gap = getFigure().getWidth() - Figure.getSlotsWidth(getFigure().getInputSlots()); double gapRatio = (double)gap / (double)(getFigure().getInputSlots().size() + 1); int gapAmount = (int)((getPosition() + 1)*gapRatio); - return new Point(gapAmount + Figure.getSlotsWidth(Figure.getAllBefore(getFigure().getInputSlots(), this)) + getWidth()/2, -Figure.SLOT_START); + return new Point(gapAmount + Figure.getSlotsWidth(Figure.getAllBefore(getFigure().getInputSlots(), this)) + getWidth()/2, 0); } @Override diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/OutputSlot.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/OutputSlot.java index 7b1f9cd1a64..a9e17a55827 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/OutputSlot.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/OutputSlot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -24,6 +24,7 @@ package com.sun.hotspot.igv.graph; import java.awt.Point; +import java.util.Objects; /** * @@ -54,7 +55,7 @@ public Point getRelativePosition() { } double gapRatio = (double)gap / (double)(getFigure().getOutputSlots().size() + 1); int gapAmount = (int)((getPosition() + 1)*gapRatio); - return new Point(gapAmount + Figure.getSlotsWidth(Figure.getAllBefore(getFigure().getOutputSlots(), this)) + getWidth()/2, Figure.SLOT_START); + return new Point(gapAmount + Figure.getSlotsWidth(Figure.getAllBefore(getFigure().getOutputSlots(), this)) + getWidth()/2, 0); } @Override diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Slot.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Slot.java index f5c0b2c2ff4..eb86d3dda88 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Slot.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Slot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -34,10 +34,7 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; /** * @@ -45,14 +42,13 @@ */ public abstract class Slot implements Port, Source.Provider, Properties.Provider { - private int wantedIndex; - private Source source; + private final int wantedIndex; + private final Source source; protected List connections; - private InputNode associatedNode; private Color color; private String text; private String shortName; - private Figure figure; + private final Figure figure; protected Slot(Figure figure, int wantedIndex) { this.figure = figure; @@ -80,13 +76,10 @@ public Properties getProperties() { } public static final Comparator slotIndexComparator = Comparator.comparingInt(o -> o.wantedIndex); - public void setAssociatedNode(InputNode node) { - associatedNode = node; - } - public int getWidth() { - if (shortName == null || shortName.length() <= 1) { - return Figure.SLOT_WIDTH; + assert shortName != null; + if (shortName.isEmpty()) { + return 0; } else { BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); @@ -96,8 +89,8 @@ public int getWidth() { } } - public int getWantedIndex() { - return wantedIndex; + public int getHeight() { + return Figure.SLOT_HEIGHT; } @Override @@ -111,7 +104,6 @@ public String getText() { public void setShortName(String s) { assert s != null; -// assert s.length() <= 2; this.shortName = s; } @@ -138,7 +130,7 @@ public String getToolTipText() { } public boolean shouldShowName() { - return getShortName() != null && getShortName().length() > 0; + return getShortName() != null && !getShortName().isEmpty(); } public boolean hasSourceNodes() { @@ -153,7 +145,6 @@ public void setText(String s) { } public Figure getFigure() { - assert figure != null; return figure; } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterEdge.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterEdge.java index a7266ebd5fa..84c02ce1fc0 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterEdge.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterEdge.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -70,10 +70,6 @@ public List getControlPoints() { return points; } - public boolean isVIP() { - return false; - } - @Override public String toString() { return from + "->" + to; diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterIngoingConnection.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterIngoingConnection.java index 4d97b06d3c3..0353855d07c 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterIngoingConnection.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterIngoingConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -70,8 +70,4 @@ public void setControlPoints(List p) { public List getControlPoints() { return controlPoints; } - - public boolean isVIP() { - return false; - } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterInputSlotNode.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterInputSlotNode.java index 22c5abda75c..b3ed5196ea8 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterInputSlotNode.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterInputSlotNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -28,6 +28,7 @@ import com.sun.hotspot.igv.layout.Vertex; import java.awt.Dimension; import java.awt.Point; +import java.util.Objects; /** * @@ -50,11 +51,11 @@ public String toString() { public ClusterInputSlotNode(ClusterNode n, String id) { this.blockNode = n; this.id = id; + this.position = new Point(0, 0); n.addSubNode(this); final Vertex thisNode = this; - final ClusterNode thisBlockNode = blockNode; outputSlot = new Port() { @@ -76,13 +77,13 @@ public String toString() { public Point getRelativePosition() { Point p = new Point(thisNode.getPosition()); - p.x += blockNode.getBorder(); + p.x += ClusterNode.PADDING; p.y = 0; return p; } public Vertex getVertex() { - return thisBlockNode; + return blockNode; } @Override @@ -124,4 +125,15 @@ public int compareTo(Vertex o) { return toString().compareTo(o.toString()); } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ClusterInputSlotNode other)) return false; + return Objects.equals(this.id, other.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterNode.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterNode.java index 80151e69d06..0de4dfc47eb 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterNode.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -29,6 +29,7 @@ import com.sun.hotspot.igv.layout.Vertex; import java.awt.Dimension; import java.awt.Point; +import java.awt.Rectangle; import java.util.*; /** @@ -37,6 +38,7 @@ */ public class ClusterNode implements Vertex { + public static final int PADDING = 8; private Cluster cluster; private Port inputSlot; private final Set subNodes; @@ -45,21 +47,15 @@ public class ClusterNode implements Vertex { private final Set subEdges; private boolean root; private final String name; - private final int border; - private final Dimension nodeOffset; private final int headerVerticalSpace; private final Dimension emptySize; - public ClusterNode(Cluster cluster, String name, int border, - Dimension nodeOffset, int headerVerticalSpace, - Dimension emptySize) { + public ClusterNode(Cluster cluster, String name, int headerVerticalSpace, Dimension emptySize) { this.subNodes = new HashSet<>(); this.subEdges = new HashSet<>(); this.cluster = cluster; this.position = new Point(0, 0); this.name = name; - this.border = border; - this.nodeOffset = nodeOffset; this.headerVerticalSpace = headerVerticalSpace; this.emptySize = emptySize; if (emptySize.width > 0 || emptySize.height > 0) { @@ -67,8 +63,8 @@ public ClusterNode(Cluster cluster, String name, int border, } } - public ClusterNode(Cluster cluster, String name) { - this(cluster, name, 20, new Dimension(0, 0), 0, new Dimension(0, 0)); + public void updateClusterBounds() { + cluster.setBounds(new Rectangle(position, size)); } public String getName() { @@ -145,22 +141,22 @@ private void calculateSize() { // Normalize coordinates for (Vertex n : subNodes) { - n.setPosition(new Point(n.getPosition().x - minX + nodeOffset.width, - n.getPosition().y - minY + nodeOffset.height + headerVerticalSpace)); + n.setPosition(new Point(n.getPosition().x - minX, + n.getPosition().y - minY + headerVerticalSpace)); } for (Link l : subEdges) { List points = new ArrayList<>(l.getControlPoints()); for (Point p : points) { p.x -= minX; - p.y -= minY; + p.y = p.y - minY + headerVerticalSpace; } l.setControlPoints(points); } - size.width += 2 * border; - size.height += 2 * border; + size.width += 2 * PADDING; + size.height += 2 * PADDING; } public Port getInputSlot() { @@ -181,7 +177,7 @@ public void setPosition(Point pos) { this.position = pos; for (Vertex n : subNodes) { Point cur = new Point(n.getPosition()); - cur.translate(pos.x + border, pos.y + border); + cur.translate(pos.x + PADDING, pos.y + PADDING); n.setPosition(cur); } @@ -191,7 +187,7 @@ public void setPosition(Point pos) { for (Point p : arr) { if (p != null) { Point p2 = new Point(p); - p2.translate(pos.x + border, pos.y + border); + p2.translate(pos.x + PADDING, pos.y + PADDING); newArr.add(p2); } else { newArr.add(null); @@ -218,10 +214,6 @@ public boolean isRoot() { return root; } - public int getBorder() { - return border; - } - public int compareTo(Vertex o) { return toString().compareTo(o.toString()); } @@ -234,4 +226,16 @@ public String toString() { public Set getSubNodes() { return subNodes; } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ClusterNode other)) return false; + return Objects.equals(this.name, other.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutgoingConnection.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutgoingConnection.java index b63ef4bb1ee..d39de8982d0 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutgoingConnection.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutgoingConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -69,8 +69,4 @@ public void setControlPoints(List p) { public List getControlPoints() { return intermediatePoints; } - - public boolean isVIP() { - return false; - } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutputSlotNode.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutputSlotNode.java index 2073efc9fcf..d4ae5a3b7b2 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutputSlotNode.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/ClusterOutputSlotNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -28,6 +28,7 @@ import com.sun.hotspot.igv.layout.Vertex; import java.awt.Dimension; import java.awt.Point; +import java.util.Objects; /** * @@ -59,11 +60,11 @@ public String toString() { public ClusterOutputSlotNode(ClusterNode n, String id) { this.blockNode = n; this.id = id; + this.position = new Point(0, 0); n.addSubNode(this); final Vertex thisNode = this; - final ClusterNode thisBlockNode = blockNode; inputSlot = new Port() { @@ -85,13 +86,13 @@ public String toString() { public Point getRelativePosition() { Point p = new Point(thisNode.getPosition()); - p.x += blockNode.getBorder(); + p.x += ClusterNode.PADDING; p.y = 0; return p; } public Vertex getVertex() { - return thisBlockNode; + return blockNode; } @Override @@ -137,4 +138,15 @@ public int compareTo(Vertex o) { return toString().compareTo(o.toString()); } + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ClusterOutputSlotNode other)) return false; + return Objects.equals(this.id, other.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalCFGLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalCFGLayoutManager.java index 3d4e8f87b27..74a7b074414 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalCFGLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalCFGLayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -23,131 +23,124 @@ */ package com.sun.hotspot.igv.hierarchicallayout; -import com.sun.hotspot.igv.layout.LayoutManager; -import com.sun.hotspot.igv.layout.*; +import com.sun.hotspot.igv.layout.Cluster; +import com.sun.hotspot.igv.layout.Link; +import com.sun.hotspot.igv.layout.Vertex; import java.awt.*; import java.util.*; -public class HierarchicalCFGLayoutManager implements LayoutManager { +public class HierarchicalCFGLayoutManager extends LayoutManager { - private static final int BLOCK_BORDER = 5; private final FontMetrics fontMetrics; - // Lays out nodes within a single cluster (basic block). - private LayoutManager subManager; - // Lays out clusters in the CFG. - private LayoutManager manager; - private Set clusters; - - public HierarchicalCFGLayoutManager() { + private final HierarchicalLayoutManager manager; + private final Set clusters; + private final Set clusterLinks; + Map clusterNodesMap; + Map clusterEdgesMap; + + public HierarchicalCFGLayoutManager(Set clusterLinks, Set clusters) { + this.clusterLinks = clusterLinks; + this.clusters = clusters; // Anticipate block label sizes to dimension blocks appropriately. Canvas canvas = new Canvas(); Font font = new Font("Arial", Font.BOLD, 14); fontMetrics = canvas.getFontMetrics(font); + manager = new HierarchicalLayoutManager(); + manager.setLayoutSelfEdges(true); } @Override public void setCutEdges(boolean enable) { - subManager.setCutEdges(enable); manager.setCutEdges(enable); } - public void setSubManager(LayoutManager manager) { - this.subManager = manager; - } - - public void setManager(LayoutManager manager) { - this.manager = manager; - } - - public void setClusters(Set clusters) { - this.clusters = clusters; - } - - public void doLayout(LayoutGraph graph, Set importantLinks) { - doLayout(graph); + private static void doLinearLayout(ClusterNode clusterNode) { + Cluster cluster = clusterNode.getCluster(); + LayoutGraph graph = new LayoutGraph(clusterNode.getSubEdges(), clusterNode.getSubNodes()); + int curY = 0; + for (Vertex vertex : cluster.getVertices()) { + if (graph.containsVertex(vertex)) { + vertex.setPosition(new Point(0, curY)); + curY += vertex.getSize().height; + } + } + clusterNode.updateSize(); } public void doLayout(LayoutGraph graph) { - // Create cluster-level nodes and edges. - Map clusterNode = createClusterNodes(graph); - Set clusterEdges = createClusterEdges(clusterNode); - markRootClusters(clusterEdges); + clusterNodesMap = createClusterNodes(graph.getVertices()); + assert clusterNodesMap.size() == clusters.size(); + clusterEdgesMap = createClusterEdges(clusterNodesMap); + assert clusterEdgesMap.size() == clusterLinks.size(); // Compute layout for each cluster. - for (Cluster c : clusters) { - ClusterNode n = clusterNode.get(c); - subManager.doLayout(new LayoutGraph(n.getSubEdges(), n.getSubNodes()), new HashSet<>()); - n.updateSize(); + for (ClusterNode clusterNode : clusterNodesMap.values()) { + doLinearLayout(clusterNode); + } + + // mark root nodes + LayoutGraph clusterGraph = new LayoutGraph(clusterEdgesMap.values(), clusterNodesMap.values()); + for (Vertex rootVertex : clusterGraph.findRootVertices()) { + assert rootVertex instanceof ClusterNode; + ((ClusterNode) rootVertex).setRoot(true); } // Compute inter-cluster layout. - manager.doLayout(new LayoutGraph(clusterEdges, new HashSet<>(clusterNode.values())), new HashSet<>()); + manager.doLayout(clusterGraph); // Write back results. - writeBackClusterBounds(clusterNode); - writeBackClusterEdgePoints(graph, clusterEdges); + writeBackClusterBounds(); + writeBackClusterEdgePoints(); } - private Map createClusterNodes(LayoutGraph graph) { - Map clusterNode = new HashMap<>(); - for (Cluster c : clusters) { - String blockLabel = "B" + c; - Dimension emptySize = new Dimension(fontMetrics.stringWidth(blockLabel) + BLOCK_BORDER * 2, - fontMetrics.getHeight() + BLOCK_BORDER); - ClusterNode cn = new ClusterNode(c, c.toString(), BLOCK_BORDER, c.getNodeOffset(), - fontMetrics.getHeight(), emptySize); - clusterNode.put(c, cn); + private Map createClusterNodes(SortedSet vertices) { + Map clusterNodes = new HashMap<>(); + for (Cluster cluster : clusters) { + String blockLabel = "B" + cluster; + Dimension emptySize = new Dimension(fontMetrics.stringWidth(blockLabel) + ClusterNode.PADDING, + fontMetrics.getHeight() + ClusterNode.PADDING); + ClusterNode clusterNode = new ClusterNode(cluster, cluster.toString(), fontMetrics.getHeight(), emptySize); + clusterNodes.put(cluster, clusterNode); } - for (Vertex v : graph.getVertices()) { - Cluster c = v.getCluster(); - assert c != null : "Cluster of vertex " + v + " is null!"; - clusterNode.get(c).addSubNode(v); + for (Vertex vertex : vertices) { + Cluster cluster = vertex.getCluster(); + clusterNodes.get(cluster).addSubNode(vertex); } - return clusterNode; + return clusterNodes; } - private Set createClusterEdges(Map clusterNode) { - Set clusterEdges = new HashSet<>(); - for (Cluster c : clusters) { - ClusterNode start = clusterNode.get(c); - for (Cluster succ : c.getSuccessors()) { - ClusterNode end = clusterNode.get(succ); - if (end != null) { - ClusterEdge e = new ClusterEdge(start, end); - clusterEdges.add(e); - } - } - } - return clusterEdges; - } + private Map createClusterEdges(Map clusterNodes) { + Map clusterEdges = new HashMap<>(); - private void markRootClusters(Set clusterEdges) { - Set roots = new LayoutGraph(clusterEdges).findRootVertices(); - for (Vertex v : roots) { - assert v instanceof ClusterNode; - ((ClusterNode) v).setRoot(true); + for (Link clusterLink : clusterLinks) { + ClusterNode fromClusterNode = clusterNodes.get(clusterLink.getFromCluster()); + ClusterNode toClusterNode = clusterNodes.get(clusterLink.getToCluster()); + assert fromClusterNode != null; + assert toClusterNode != null; + clusterEdges.put(clusterLink, new ClusterEdge(fromClusterNode, toClusterNode)); } + + return clusterEdges; } - private void writeBackClusterBounds(Map clusterNode) { - for (Cluster c : clusters) { - ClusterNode n = clusterNode.get(c); - c.setBounds(new Rectangle(n.getPosition(), n.getSize())); + private void writeBackClusterBounds() { + assert clusterNodesMap.size() == clusters.size(); + for (ClusterNode clusterNode : clusterNodesMap.values()) { + clusterNode.updateClusterBounds(); } } - private void writeBackClusterEdgePoints(LayoutGraph graph, Set clusterEdges) { - // Map from "primitive" cluster edges to their input links. - Map, Link> inputLink = new HashMap<>(); - for (Link l : graph.getLinks()) { - inputLink.put(new AbstractMap.SimpleEntry<>(l.getFromCluster(), l.getToCluster()), l); - } - for (ClusterEdge ce : clusterEdges) { - assert (ce.getControlPoints() != null); - Link l = inputLink.get(new AbstractMap.SimpleEntry<>(ce.getFromCluster(), ce.getToCluster())); - l.setControlPoints(ce.getControlPoints()); + private void writeBackClusterEdgePoints() { + assert clusterEdgesMap.size() == clusterLinks.size(); + for (Link clusterLink : clusterLinks) { + ClusterEdge clusterEdge = clusterEdgesMap.get(clusterLink); + if (clusterEdge.getControlPoints() != null) { + clusterLink.setControlPoints(clusterEdge.getControlPoints()); + } else { + clusterLink.setControlPoints(new ArrayList<>()); + } } } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalClusterLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalClusterLayoutManager.java index 1d103585e58..ac3d1846d3c 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalClusterLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalClusterLayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -24,52 +24,37 @@ package com.sun.hotspot.igv.hierarchicallayout; import com.sun.hotspot.igv.layout.*; -import java.awt.Point; -import java.awt.Rectangle; +import java.awt.*; import java.util.*; +import java.util.List; /** * * @author Thomas Wuerthinger */ -public class HierarchicalClusterLayoutManager implements LayoutManager { +public class HierarchicalClusterLayoutManager extends LayoutManager { - private HierarchicalLayoutManager.Combine combine; - private LayoutManager subManager = new HierarchicalLayoutManager(combine); - private LayoutManager manager = new HierarchicalLayoutManager(combine); - private static final boolean TRACE = false; + private final LayoutManager manager; + private final HashMap clusterNodes; - public HierarchicalClusterLayoutManager(HierarchicalLayoutManager.Combine combine) { - this.combine = combine; + + public HierarchicalClusterLayoutManager() { + this.manager = new HierarchicalLayoutManager(); + this.clusterNodes = new HashMap<>(); } @Override public void setCutEdges(boolean enable) { - subManager.setCutEdges(enable); manager.setCutEdges(enable); } - public void doLayout(LayoutGraph graph, Set importantLinks) { - doLayout(graph); - } - - public void setSubManager(LayoutManager manager) { - this.subManager = manager; - } - - public void setManager(LayoutManager manager) { - this.manager = manager; - } public void doLayout(LayoutGraph graph) { - - assert graph.verify(); - + clusterNodes.clear(); HashMap> listsConnection = new HashMap<>(); HashMap> clusterInputSlotHash = new HashMap<>(); HashMap> clusterOutputSlotHash = new HashMap<>(); - HashMap clusterNodes = new HashMap<>(); HashMap> clusterInputSlotSet = new HashMap<>(); HashMap> clusterOutputSlotSet = new HashMap<>(); Set clusterEdges = new HashSet<>(); @@ -79,35 +64,33 @@ public void doLayout(LayoutGraph graph) { HashMap linkClusterIngoingConnection = new HashMap<>(); Set clusterNodeSet = new HashSet<>(); - Set cluster = graph.getClusters(); + Set clusters = new TreeSet<>(); + for (Vertex v : graph.getVertices()) { + if (v.getCluster() != null) { + clusters.add(v.getCluster()); + } + } + int z = 0; - for (Cluster c : cluster) { + for (Cluster c : clusters) { listsConnection.put(c, new ArrayList<>()); clusterInputSlotHash.put(c, new HashMap<>()); clusterOutputSlotHash.put(c, new HashMap<>()); clusterOutputSlotSet.put(c, new TreeSet<>()); clusterInputSlotSet.put(c, new TreeSet<>()); - ClusterNode cn = new ClusterNode(c, "" + z); + + String blockLabel = "B" + c; + Canvas canvas = new Canvas(); + FontMetrics fontMetrics = canvas.getFontMetrics(TITLE_FONT); + Dimension emptySize = new Dimension(fontMetrics.stringWidth(blockLabel) + ClusterNode.PADDING * 2, + fontMetrics.getHeight() + ClusterNode.PADDING * 2); + ClusterNode cn = new ClusterNode(c, "" + z, fontMetrics.getHeight(), emptySize); + clusterNodes.put(c, cn); clusterNodeSet.add(cn); z++; } - // Add cluster edges - for (Cluster c : cluster) { - - ClusterNode start = clusterNodes.get(c); - - for (Cluster succ : c.getSuccessors()) { - ClusterNode end = clusterNodes.get(succ); - if (end != null && start != end) { - ClusterEdge e = new ClusterEdge(start, end); - clusterEdges.add(e); - interClusterEdges.add(e); - } - } - } - for (Vertex v : graph.getVertices()) { Cluster c = v.getCluster(); assert c != null : "Cluster of vertex " + v + " is null!"; @@ -123,13 +106,6 @@ public void doLayout(LayoutGraph graph) { Cluster fromCluster = fromVertex.getCluster(); Cluster toCluster = toVertex.getCluster(); - Port samePort = null; - if (combine == HierarchicalLayoutManager.Combine.SAME_INPUTS) { - samePort = toPort; - } else if (combine == HierarchicalLayoutManager.Combine.SAME_OUTPUTS) { - samePort = fromPort; - } - assert listsConnection.containsKey(fromCluster); assert listsConnection.containsKey(toCluster); @@ -137,23 +113,19 @@ public void doLayout(LayoutGraph graph) { listsConnection.get(fromCluster).add(l); clusterNodes.get(fromCluster).addSubEdge(l); } else { - ClusterInputSlotNode inputSlotNode = null; - ClusterOutputSlotNode outputSlotNode = null; + ClusterInputSlotNode inputSlotNode; + ClusterOutputSlotNode outputSlotNode; - if (samePort != null) { - outputSlotNode = clusterOutputSlotHash.get(fromCluster).get(samePort); - inputSlotNode = clusterInputSlotHash.get(toCluster).get(samePort); - } + outputSlotNode = clusterOutputSlotHash.get(fromCluster).get(fromPort); + inputSlotNode = clusterInputSlotHash.get(toCluster).get(toPort); if (outputSlotNode == null) { - outputSlotNode = new ClusterOutputSlotNode(clusterNodes.get(fromCluster), "Out " + fromCluster.toString() + " " + samePort); + outputSlotNode = new ClusterOutputSlotNode(clusterNodes.get(fromCluster), "Out " + fromCluster.toString() + " " + fromPort); clusterOutputSlotSet.get(fromCluster).add(outputSlotNode); ClusterOutgoingConnection conn = new ClusterOutgoingConnection(outputSlotNode, l); outputSlotNode.setOutgoingConnection(conn); clusterNodes.get(fromCluster).addSubEdge(conn); - if (samePort != null) { - clusterOutputSlotHash.get(fromCluster).put(samePort, outputSlotNode); - } + clusterOutputSlotHash.get(fromCluster).put(fromPort, outputSlotNode); linkClusterOutgoingConnection.put(l, conn); } else { @@ -161,13 +133,16 @@ public void doLayout(LayoutGraph graph) { } if (inputSlotNode == null) { - inputSlotNode = new ClusterInputSlotNode(clusterNodes.get(toCluster), "In " + toCluster.toString() + " " + samePort); + inputSlotNode = new ClusterInputSlotNode( + clusterNodes.get(toCluster), + "In " + toCluster.toString() + " " + toPort // Use toPort here + ); clusterInputSlotSet.get(toCluster).add(inputSlotNode); } ClusterIngoingConnection conn = new ClusterIngoingConnection(inputSlotNode, l); clusterNodes.get(toCluster).addSubEdge(conn); - clusterInputSlotHash.get(toCluster).put(samePort, inputSlotNode); + clusterInputSlotHash.get(toCluster).put(fromPort, inputSlotNode); linkClusterIngoingConnection.put(l, conn); @@ -178,39 +153,26 @@ public void doLayout(LayoutGraph graph) { } } - Timing t = null; - - if (TRACE) { - t = new Timing("Child timing"); - t.start(); - } - - for (Cluster c : cluster) { + for (Cluster c : clusters) { ClusterNode n = clusterNodes.get(c); - subManager.doLayout(new LayoutGraph(n.getSubEdges(), n.getSubNodes()), new HashSet<>()); + HierarchicalLayoutManager subManager = new HierarchicalLayoutManager(); + subManager.doLayout(new LayoutGraph(n.getSubEdges(), n.getSubNodes())); n.updateSize(); } - Set roots = new LayoutGraph(interClusterEdges).findRootVertices(); + Set roots = new LayoutGraph(interClusterEdges, new HashSet<>()).findRootVertices(); for (Vertex v : roots) { assert v instanceof ClusterNode; ((ClusterNode) v).setRoot(true); } - manager.doLayout(new LayoutGraph(clusterEdges, clusterNodeSet), interClusterEdges); + manager.doLayout(new LayoutGraph(clusterEdges, clusterNodeSet)); - for (Cluster c : cluster) { + for (Cluster c : clusters) { ClusterNode n = clusterNodes.get(c); c.setBounds(new Rectangle(n.getPosition(), n.getSize())); } - // TODO: handle case where blocks are not fully connected - - if (TRACE) { - t.stop(); - t.print(); - } - for (Link l : graph.getLinks()) { if (linkInterClusterConnection.containsKey(l)) { diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java index 996fd895577..477cdce0c5b 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -23,12 +23,9 @@ */ package com.sun.hotspot.igv.hierarchicallayout; -import com.sun.hotspot.igv.layout.LayoutGraph; -import com.sun.hotspot.igv.layout.LayoutManager; +import static com.sun.hotspot.igv.hierarchicallayout.LayoutNode.*; import com.sun.hotspot.igv.layout.Link; import com.sun.hotspot.igv.layout.Vertex; -import com.sun.hotspot.igv.util.Statistics; -import java.awt.Dimension; import java.awt.Point; import java.util.*; @@ -36,104 +33,19 @@ * * @author Thomas Wuerthinger */ -public class HierarchicalLayoutManager implements LayoutManager { - - public static final boolean TRACE = false; - public static final boolean CHECK = false; - public static final int SWEEP_ITERATIONS = 1; - public static final int CROSSING_ITERATIONS = 2; - public static final int DUMMY_HEIGHT = 1; - public static final int DUMMY_WIDTH = 1; - public static final int X_OFFSET = 8; - public static final int LAYER_OFFSET = 8; - public static final int MAX_LAYER_LENGTH = -1; - public static final int MIN_LAYER_DIFFERENCE = 1; - public static final int VIP_BONUS = 10; - - public enum Combine { - - NONE, - SAME_INPUTS, - SAME_OUTPUTS - } - // Options - private final Combine combine; - private final int dummyWidth; - private final int dummyHeight; - private int xOffset; - private int layerOffset; - private int maxLayerLength; - private int minLayerDifference; - private boolean layoutSelfEdges; - // Algorithm global datastructures - private Set reversedLinks; - private Set selfEdges; - private List nodes; - private HashMap vertexToLayoutNode; - private HashMap> reversedLinkStartPoints; - private HashMap> reversedLinkEndPoints; - private HashMap> splitStartPoints; - private HashMap> splitEndPoints; - private LayoutGraph graph; - private List[] layers; - private int layerCount; - private Set importantLinks; - private final Set linksToFollow; - - private abstract static class AlgorithmPart { - - public void start() { - if (CHECK) { - preCheck(); - } - - long start = 0; - if (TRACE) { - System.out.println("##################################################"); - System.out.println("Starting part " + this.getClass().getName()); - start = System.currentTimeMillis(); - } - run(); - if (TRACE) { - System.out.println("Timing for " + this.getClass().getName() + " is " + (System.currentTimeMillis() - start)); - printStatistics(); - } - - if (CHECK) { - postCheck(); - } - } - - protected abstract void run(); - - protected void printStatistics() { - } - - protected void postCheck() { - } - - protected void preCheck() { - } - } +public class HierarchicalLayoutManager extends LayoutManager { - public HierarchicalLayoutManager(Combine b) { - this.combine = b; - this.dummyWidth = DUMMY_WIDTH; - this.dummyHeight = DUMMY_HEIGHT; - this.xOffset = X_OFFSET; - this.layerOffset = LAYER_OFFSET; - this.maxLayerLength = MAX_LAYER_LENGTH; - this.minLayerDifference = MIN_LAYER_DIFFERENCE; - this.layoutSelfEdges = false; - this.linksToFollow = new HashSet<>(); - } + int maxLayerLength; + private LayoutGraph graph; + private boolean layoutSelfEdges = false; - public void setXOffset(int xOffset) { - this.xOffset = xOffset; + public HierarchicalLayoutManager() { + setCutEdges(false); + setLayoutSelfEdges(false); } - public void setLayerOffset(int layerOffset) { - this.layerOffset = layerOffset; + public void setLayoutSelfEdges(boolean layoutSelfEdges) { + this.layoutSelfEdges = layoutSelfEdges; } @Override @@ -141,1582 +53,731 @@ public void setCutEdges(boolean enable) { maxLayerLength = enable ? 10 : -1; } - public void setMinLayerDifference(int v) { - minLayerDifference = v; - } + @Override + public void doLayout(LayoutGraph layoutGraph) { + layoutGraph.initializeLayout(); - public void setLayoutSelfEdges(boolean layoutSelfEdges) { - this.layoutSelfEdges = layoutSelfEdges; - } + removeSelfEdges(layoutGraph); - public List getNodes() { - return nodes; - } + // STEP 1: Reverse edges + ReverseEdges.apply(layoutGraph); - // Remove self-edges, possibly saving them into the selfEdges set. - private void removeSelfEdges(boolean save) { - for (LayoutNode node : nodes) { - for (LayoutEdge e : new ArrayList<>(node.succs)) { - if (e.to == node) { - if (save) { - selfEdges.add(e); - } - node.succs.remove(e); - node.preds.remove(e); - } - } - } - } + // STEP 2: Assign layers and create dummy nodes + LayerManager.apply(layoutGraph, maxLayerLength); - @Override - public void doLayout(LayoutGraph graph) { - doLayout(graph, new HashSet<>()); + // STEP 3: Crossing Reduction + CrossingReduction.apply(layoutGraph); - } + // STEP 4: Assign X coordinates + AssignXCoordinates.apply(layoutGraph); - @Override - public void doLayout(LayoutGraph graph, Set importantLinks) { - - this.importantLinks = importantLinks; - this.graph = graph; - - vertexToLayoutNode = new HashMap<>(); - reversedLinks = new HashSet<>(); - selfEdges = new HashSet<>(); - reversedLinkStartPoints = new HashMap<>(); - reversedLinkEndPoints = new HashMap<>(); - nodes = new ArrayList<>(); - splitStartPoints = new HashMap<>(); - splitEndPoints = new HashMap<>(); - - // ############################################################# - // Step 1: Build up data structure - new BuildDatastructure().start(); - - if (!layoutSelfEdges) { - // Remove self-edges from the beginning. - removeSelfEdges(false); - } + // STEP 5: Write back to interface + WriteResult.apply(layoutGraph); - // ############################################################# - // STEP 2: Reverse edges, handle backedges - new ReverseEdges().start(); + graph = layoutGraph; + } - for (LayoutNode n : nodes) { - ArrayList tmpArr = new ArrayList<>(); - for (LayoutEdge e : n.succs) { - if (importantLinks.contains(e.link)) { - tmpArr.add(e); + /** + * Removes self-edges from nodes in the graph. If self-edges are to be included in the layout + * (`layoutSelfEdges` is true), it stores them in the node for later processing and marks the graph + * to display self-edges + */ + private void removeSelfEdges(LayoutGraph graph) { + for (LayoutNode node : graph.getLayoutNodes()) { + // Collect self-edges first to avoid concurrent modification + List selfEdges = new ArrayList<>(); + for (LayoutEdge edge : node.getSuccessors()) { + if (edge.getTo() == node) { + selfEdges.add(edge); } } - for (LayoutEdge e : tmpArr) { - e.from.succs.remove(e); - e.to.preds.remove(e); + // Remove each self-edge + for (LayoutEdge edge : selfEdges) { + node.removeSuccessor(edge); + node.removePredecessor(edge); + } + if (layoutSelfEdges) { + for (LayoutEdge selfEdge : selfEdges) { + node.setSelfEdge(selfEdge); + } + graph.setShowSelfEdges(true); } } - - // Hide self-edges from the layout algorithm and save them for later. - removeSelfEdges(true); - - // ############################################################# - // STEP 3: Assign layers - new AssignLayers().start(); - - // ############################################################# - // STEP 4: Create dummy nodes - new CreateDummyNodes().start(); - - // ############################################################# - // STEP 5: Crossing Reduction - new CrossingReduction().start(); - - // ############################################################# - // STEP 7: Assign X coordinates - new AssignXCoordinates().start(); - - // ############################################################# - // STEP 6: Assign Y coordinates - new AssignYCoordinates().start(); - - // Put saved self-edges back so that they are assigned points. - for (LayoutEdge e : selfEdges) { - e.from.succs.add(e); - e.to.preds.add(e); + if (layoutSelfEdges) { + graph.setShowSelfEdges(true); } - - // ############################################################# - // STEP 8: Write back to interface - new WriteResult().start(); } - private class WriteResult extends AlgorithmPart { + public List getNodes() { + return graph.getAllNodes(); + } - private int pointCount; + public static class ReverseEdges { - @Override - protected void run() { + static public void apply(LayoutGraph graph) { + reverseRootInputs(graph); + depthFirstSearch(graph); - HashMap vertexPositions = new HashMap<>(); - HashMap> linkPositions = new HashMap<>(); - for (Vertex v : graph.getVertices()) { - LayoutNode n = vertexToLayoutNode.get(v); - assert !vertexPositions.containsKey(v); - vertexPositions.put(v, new Point(n.x + n.xOffset, n.y + n.yOffset)); + for (LayoutNode node : graph.getLayoutNodes()) { + node.computeReversedLinkPoints(false); } + } - for (LayoutNode n : nodes) { - - for (LayoutEdge e : n.preds) { - if (e.link != null && !linkPositions.containsKey(e.link)) { - ArrayList points = new ArrayList<>(); - - Point p = new Point(e.to.x + e.relativeTo, e.to.y + e.to.yOffset + e.link.getTo().getRelativePosition().y); - points.add(p); - if (e.to.inOffsets.containsKey(e.relativeTo)) { - points.add(new Point(p.x, p.y + e.to.inOffsets.get(e.relativeTo) + e.link.getTo().getRelativePosition().y)); - } - - LayoutNode cur = e.from; - LayoutNode other = e.to; - LayoutEdge curEdge = e; - while (cur.vertex == null && cur.preds.size() != 0) { - if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { - points.remove(points.size() - 1); - } - points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height)); - if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { - points.remove(points.size() - 1); - } - points.add(new Point(cur.x + cur.width / 2, cur.y)); - assert cur.preds.size() == 1; - curEdge = cur.preds.get(0); - cur = curEdge.from; - } - - p = new Point(cur.x + curEdge.relativeFrom, cur.y + cur.height - cur.bottomYOffset + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y)); - if (curEdge.from.outOffsets.containsKey(curEdge.relativeFrom)) { - points.add(new Point(p.x, p.y + curEdge.from.outOffsets.get(curEdge.relativeFrom) + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y))); - } - points.add(p); - - Collections.reverse(points); - - if (cur.vertex == null && cur.preds.size() == 0) { - - if (reversedLinkEndPoints.containsKey(e.link)) { - for (Point p1 : reversedLinkEndPoints.get(e.link)) { - points.add(new Point(p1.x + e.to.x, p1.y + e.to.y)); - } - } - - if (splitStartPoints.containsKey(e.link)) { - points.add(0, null); - points.addAll(0, splitStartPoints.get(e.link)); - - //checkPoints(points); - if (reversedLinks.contains(e.link)) { - Collections.reverse(points); - } - assert !linkPositions.containsKey(e.link); - linkPositions.put(e.link, points); - } else { - splitEndPoints.put(e.link, points); - } - - } else { - if (reversedLinks.contains(e.link)) { - Collections.reverse(points); - if (selfEdges.contains(e)) { - // For self edges, it is enough with the - // start and end points computed by ReverseEdges. - points.clear(); - } - } - if (reversedLinkStartPoints.containsKey(e.link)) { - for (Point p1 : reversedLinkStartPoints.get(e.link)) { - points.add(new Point(p1.x + cur.x, p1.y + cur.y)); - } - } - - if (reversedLinkEndPoints.containsKey(e.link)) { - for (Point p1 : reversedLinkEndPoints.get(e.link)) { - points.add(0, new Point(p1.x + other.x, p1.y + other.y)); - } - } - - assert !linkPositions.containsKey(e.link); - linkPositions.put(e.link, points); - } - pointCount += points.size(); + private static void reverseRootInputs(LayoutGraph graph) { + for (LayoutNode node : graph.getLayoutNodes()) { + if (node.getVertex().isRoot()) { + for (LayoutEdge predEdge : new ArrayList<>(node.getPredecessors())) { + reverseEdge(predEdge); } } + } + } - for (LayoutEdge e : n.succs) { - if (e.link != null && !linkPositions.containsKey(e.link)) { - ArrayList points = new ArrayList<>(); - Point p = new Point(e.from.x + e.relativeFrom, e.from.y + e.from.height - e.from.bottomYOffset + e.link.getFrom().getRelativePosition().y); - points.add(p); - if (e.from.outOffsets.containsKey(e.relativeFrom)) { - Point pOffset = new Point(p.x, p.y + e.from.outOffsets.get(e.relativeFrom) + - e.link.getFrom().getRelativePosition().y + e.from.yOffset); - if (!pOffset.equals(p)) { - points.add(pOffset); - } - } - - LayoutNode cur = e.to; - LayoutNode other = e.from; - LayoutEdge curEdge = e; - while (cur.vertex == null && !cur.succs.isEmpty()) { - if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { - points.remove(points.size() - 1); - } - points.add(new Point(cur.x + cur.width / 2, cur.y)); - if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { - points.remove(points.size() - 1); - } - points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height)); - if (cur.succs.isEmpty()) { - break; - } - assert cur.succs.size() == 1; - curEdge = cur.succs.get(0); - cur = curEdge.to; - } - - p = new Point(cur.x + curEdge.relativeTo, cur.y + cur.yOffset + ((curEdge.link == null) ? 0 : curEdge.link.getTo().getRelativePosition().y)); - points.add(p); - if (curEdge.to.inOffsets.containsKey(curEdge.relativeTo)) { - points.add(new Point(p.x, p.y + curEdge.to.inOffsets.get(curEdge.relativeTo) + ((curEdge.link == null) ? 0 : curEdge.link.getTo().getRelativePosition().y))); - } + public static void reverseEdge(LayoutEdge edge) { + edge.reverse(); - if (cur.succs.isEmpty() && cur.vertex == null) { - if (reversedLinkStartPoints.containsKey(e.link)) { - for (Point p1 : reversedLinkStartPoints.get(e.link)) { - points.add(0, new Point(p1.x + other.x, p1.y + other.y)); - } - } + LayoutNode fromNode = edge.getFrom(); + LayoutNode toNode = edge.getTo(); + int relativeFrom = edge.getRelativeFromX(); + int relativeTo = edge.getRelativeToX(); - if (splitEndPoints.containsKey(e.link)) { - points.add(null); - points.addAll(splitEndPoints.get(e.link)); + edge.setFrom(toNode); + edge.setTo(fromNode); + edge.setRelativeFromX(relativeTo); + edge.setRelativeToX(relativeFrom); - if (reversedLinks.contains(e.link)) { - Collections.reverse(points); - } - assert !linkPositions.containsKey(e.link); - linkPositions.put(e.link, points); - } else { - splitStartPoints.put(e.link, points); - } - } else { + fromNode.removeSuccessor(edge); + fromNode.addPredecessor(edge); + toNode.removePredecessor(edge); + toNode.addSuccessor(edge); + } - if (reversedLinkStartPoints.containsKey(e.link)) { - for (Point p1 : reversedLinkStartPoints.get(e.link)) { - points.add(0, new Point(p1.x + other.x + other.xOffset, p1.y + other.y)); - } - } - if (reversedLinkEndPoints.containsKey(e.link)) { - for (Point p1 : reversedLinkEndPoints.get(e.link)) { - points.add(new Point(p1.x + cur.x + cur.xOffset, p1.y + cur.y)); - } - } - if (reversedLinks.contains(e.link)) { - Collections.reverse(points); - } - assert !linkPositions.containsKey(e.link); - linkPositions.put(e.link, points); - } + private static void depthFirstSearch(LayoutGraph graph) { + Set visited = new HashSet<>(); + Set active = new HashSet<>(); - pointCount += points.size(); - } - } - } + for (LayoutNode startNode : graph.getLayoutNodes()) { + Deque stack = new ArrayDeque<>(); + stack.push(startNode); - int minX = Integer.MAX_VALUE; - int minY = Integer.MAX_VALUE; - for (Vertex v : vertexPositions.keySet()) { - Point p = vertexPositions.get(v); - minX = Math.min(minX, p.x); - minY = Math.min(minY, p.y); - } + while (!stack.isEmpty()) { + LayoutNode node = stack.pop(); - for (Link l : linkPositions.keySet()) { - List points = linkPositions.get(l); - for (Point p : points) { - if (p != null) { - minX = Math.min(minX, p.x); - minY = Math.min(minY, p.y); + if (visited.contains(node)) { + active.remove(node); + continue; } - } - - } - for (Vertex v : vertexPositions.keySet()) { - Point p = vertexPositions.get(v); - p.x -= minX; - p.y -= minY; - v.setPosition(p); - } + stack.push(node); + visited.add(node); + active.add(node); - for (Link l : linkPositions.keySet()) { - List points = linkPositions.get(l); - for (Point p : points) { - if (p != null) { - p.x -= minX; - p.y -= minY; + for (LayoutEdge edge : new ArrayList<>(node.getSuccessors())) { + LayoutNode successor = edge.getTo(); + if (active.contains(successor)) { + reverseEdge(edge); + } else if (!visited.contains(successor)) { + stack.push(successor); + } } } - l.setControlPoints(points); - - } - } - - @Override - protected void printStatistics() { - System.out.println("Number of nodes: " + nodes.size()); - int edgeCount = 0; - for (LayoutNode n : nodes) { - edgeCount += n.succs.size(); } - System.out.println("Number of edges: " + edgeCount); - System.out.println("Number of points: " + pointCount); } } - public static final Comparator nodePositionComparator = Comparator.comparingInt(n -> n.pos); - public static final Comparator nodeProcessingDownComparator = (n1, n2) -> { - int n1VIP = 0; - for (LayoutEdge e : n1.preds) { - if (e.vip) { - n1VIP++; - } - } - int n2VIP = 0; - for (LayoutEdge e : n2.preds) { - if (e.vip) { - n2VIP++; - } - } - if (n1VIP != n2VIP) { - return n2VIP - n1VIP; - } - if (n1.vertex == null) { - if (n2.vertex == null) { - return 0; - } - return -1; - } - if (n2.vertex == null) { - return 1; - } - return n1.preds.size() - n2.preds.size(); - }; - public static final Comparator nodeProcessingUpComparator = (n1, n2) -> { - int n1VIP = 0; - for (LayoutEdge e : n1.succs) { - if (e.vip) { - n1VIP++; - } - } - int n2VIP = 0; - for (LayoutEdge e : n2.succs) { - if (e.vip) { - n2VIP++; - } - } - if (n1VIP != n2VIP) { - return n2VIP - n1VIP; - } - if (n1.vertex == null) { - if (n2.vertex == null) { - return 0; - } - return -1; - } - if (n2.vertex == null) { - return 1; - } - return n1.succs.size() - n2.succs.size(); - }; - - private class AssignXCoordinates extends AlgorithmPart { - - private ArrayList[] space; - private ArrayList[] downProcessingOrder; - private ArrayList[] upProcessingOrder; - - private void initialPositions() { - for (LayoutNode n : nodes) { - n.x = space[n.layer].get(n.pos); - } - } - - @SuppressWarnings("unchecked") - private void createArrays() { - space = new ArrayList[layers.length]; - downProcessingOrder = new ArrayList[layers.length]; - upProcessingOrder = new ArrayList[layers.length]; - } - - @Override - protected void run() { - createArrays(); + public static class LayerManager { - for (int i = 0; i < layers.length; i++) { - space[i] = new ArrayList<>(); - downProcessingOrder[i] = new ArrayList<>(); - upProcessingOrder[i] = new ArrayList<>(); + private static void assignLayerDownwards(LayoutGraph graph) { + ArrayList workingList = new ArrayList<>(); - int curX = 0; - for (LayoutNode n : layers[i]) { - space[i].add(curX); - curX += n.width + xOffset; - downProcessingOrder[i].add(n); - upProcessingOrder[i].add(n); - } - - downProcessingOrder[i].sort(nodeProcessingDownComparator); - upProcessingOrder[i].sort(nodeProcessingUpComparator); - } - - initialPositions(); - for (int i = 0; i < SWEEP_ITERATIONS; i++) { - sweepDown(); - adjustSpace(); - sweepUp(); - adjustSpace(); - } - - sweepDown(); - adjustSpace(); - sweepUp(); - } - - private void adjustSpace() { - for (int i = 0; i < layers.length; i++) { - for (LayoutNode n : layers[i]) { - space[i].add(n.x); - } - } - } - - private int calculateOptimalDown(LayoutNode n) { - int size = n.preds.size(); - if (size == 0) { - return n.x; - } - int vipCount = 0; - for (LayoutEdge e : n.preds) { - if (e.vip) { - vipCount++; + // add all root nodes to layer 0 + for (LayoutNode node : graph.getLayoutNodes()) { + if (!node.hasPredecessors()) { + workingList.add(node); + node.setLayer(0); } } - if (vipCount == 0) { - int[] values = new int[size]; - for (int i = 0; i < size; i++) { - LayoutEdge e = n.preds.get(i); - values[i] = e.from.x + e.relativeFrom - e.relativeTo; - } - return Statistics.median(values); - } else { - int z = 0; - int[] values = new int[vipCount]; - for (int i = 0; i < size; i++) { - LayoutEdge e = n.preds.get(i); - if (e.vip) { - values[z++] = e.from.x + e.relativeFrom - e.relativeTo; + // assign layers downwards starting from roots + int layer = 1; + while (!workingList.isEmpty()) { + ArrayList newWorkingList = new ArrayList<>(); + for (LayoutNode node : workingList) { + for (LayoutEdge succEdge : node.getSuccessors()) { + LayoutNode succNode = succEdge.getTo(); + if (succNode.getLayer() == -1) { + // This node was not assigned before. + boolean assignedPred = true; + for (LayoutEdge predEdge : succNode.getPredecessors()) { + LayoutNode predNode = predEdge.getFrom(); + if (predNode.getLayer() == -1 || predNode.getLayer() >= layer) { + // This now has an unscheduled successor or a successor that was scheduled only in this round. + assignedPred = false; + break; + } + } + if (assignedPred) { + // This successor node can be assigned. + succNode.setLayer(layer); + newWorkingList.add(succNode); + } + } } } - return Statistics.median(values); - } - } - - private int calculateOptimalBoth(LayoutNode n) { - if (n.preds.size() == n.succs.size()) { - return n.x; - } - - int[] values = new int[n.preds.size() + n.succs.size()]; - int i = 0; - - for (LayoutEdge e : n.preds) { - values[i] = e.from.x + e.relativeFrom - e.relativeTo; - i++; + workingList = newWorkingList; + layer++; } - for (LayoutEdge e : n.succs) { - values[i] = e.to.x + e.relativeTo - e.relativeFrom; - i++; + int layerCount = layer - 1; + for (LayoutNode n : graph.getLayoutNodes()) { + n.setLayer((layerCount - 1 - n.getLayer())); } - - return Statistics.median(values); } - private int calculateOptimalUp(LayoutNode n) { - int size = n.succs.size(); - if (size == 0) { - return n.x; - } - int[] values = new int[size]; - for (int i = 0; i < size; i++) { - LayoutEdge e = n.succs.get(i); - values[i] = e.to.x + e.relativeTo - e.relativeFrom; - if (e.vip) { - return values[i]; - } - } - return Statistics.median(values); - } + private static void assignLayerUpwards(LayoutGraph graph) { + ArrayList workingList = new ArrayList<>(); + // add all leaves to working list, reset layer of non-leave nodes + for (LayoutNode node : graph.getLayoutNodes()) { + if (!node.hasSuccessors()) { + workingList.add(node); + } else { + node.setLayer(-1); + } + } + + // assign layer upwards starting from leaves + // sinks non-leave nodes down as much as possible + int layer = 1; + while (!workingList.isEmpty()) { + ArrayList newWorkingList = new ArrayList<>(); + for (LayoutNode node : workingList) { + if (node.getLayer() < layer) { + for (LayoutEdge predEdge : node.getPredecessors()) { + LayoutNode predNode = predEdge.getFrom(); + if (predNode.getLayer() == -1) { + // This node was not assigned before. + boolean assignedSucc = true; + for (LayoutEdge succEdge : predNode.getSuccessors()) { + LayoutNode succNode = succEdge.getTo(); + if (succNode.getLayer() == -1 || succNode.getLayer() >= layer) { + // This now has an unscheduled successor or a successor that was scheduled only in this round. + assignedSucc = false; + break; + } + } - private void sweepUp() { - for (int i = layers.length - 1; i >= 0; i--) { - NodeRow r = new NodeRow(space[i]); - for (LayoutNode n : upProcessingOrder[i]) { - int optimal = calculateOptimalUp(n); - r.insert(n, optimal); + if (assignedSucc) { + // This predecessor node can be assigned. + predNode.setLayer(layer); + newWorkingList.add(predNode); + } + } + } + } else { + newWorkingList.add(node); + } } - } - } - private void sweepDown() { - for (int i = 1; i < layers.length; i++) { - NodeRow r = new NodeRow(space[i]); - for (LayoutNode n : downProcessingOrder[i]) { - int optimal = calculateOptimalDown(n); - r.insert(n, optimal); - } + workingList = newWorkingList; + layer++; } - } - } - public static class NodeRow { - - private final TreeSet treeSet; - private final ArrayList space; - - public NodeRow(ArrayList space) { - treeSet = new TreeSet<>(nodePositionComparator); - this.space = space; - } - - public int offset(LayoutNode n1, LayoutNode n2) { - int v1 = space.get(n1.pos) + n1.width; - int v2 = space.get(n2.pos); - return v2 - v1; - } - - public void insert(LayoutNode n, int pos) { - - SortedSet headSet = treeSet.headSet(n); - - LayoutNode leftNeighbor; - int minX = Integer.MIN_VALUE; - if (!headSet.isEmpty()) { - leftNeighbor = headSet.last(); - minX = leftNeighbor.x + leftNeighbor.width + offset(leftNeighbor, n); + int layerCount = layer - 1; + for (LayoutNode n : graph.getLayoutNodes()) { + n.setLayer((layerCount - 1 - n.getLayer())); } - if (pos < minX) { - n.x = minX; - } else { - - LayoutNode rightNeighbor; - SortedSet tailSet = treeSet.tailSet(n); - int maxX = Integer.MAX_VALUE; - if (!tailSet.isEmpty()) { - rightNeighbor = tailSet.first(); - maxX = rightNeighbor.x - offset(n, rightNeighbor) - n.width; - } - - n.x = Math.min(pos, maxX); - - assert minX <= maxX : minX + " vs " + maxX; - } - - treeSet.add(n); - } - } - private static final Comparator crossingNodeComparator = Comparator.comparingInt(n -> n.crossingNumber); - - private class CrossingReduction extends AlgorithmPart { - - @Override - public void preCheck() { - for (LayoutNode n : nodes) { - assert n.layer < layerCount; - } + graph.initLayers(layerCount); } - @SuppressWarnings("unchecked") - private void createLayers() { - layers = new List[layerCount]; - for (int i = 0; i < layerCount; i++) { - layers[i] = new ArrayList<>(); - } + static private void assignLayers(LayoutGraph graph) { + assignLayerDownwards(graph); + assignLayerUpwards(graph); } - @Override - protected void run() { - createLayers(); + static private void createDummyNodes(LayoutGraph graph, int maxLayerLength) { + List layoutNodes = new ArrayList<>(graph.getLayoutNodes()); + layoutNodes.sort(LAYOUT_NODE_DEGREE_COMPARATOR); // Generate initial ordering HashSet visited = new HashSet<>(); - for (LayoutNode n : nodes) { - if (n.layer == 0) { - layers[0].add(n); - visited.add(n); - } else if (n.preds.isEmpty()) { - layers[n.layer].add(n); - visited.add(n); + for (LayoutNode layoutNode : layoutNodes) { + if (layoutNode.getLayer() == 0) { + graph.getLayer(0).add(layoutNode); + visited.add(layoutNode); + } else if (!layoutNode.hasPredecessors()) { + graph.getLayer(layoutNode.getLayer()).add(layoutNode); + visited.add(layoutNode); } } - for (int i = 0; i < layers.length - 1; i++) { - for (LayoutNode n : layers[i]) { - for (LayoutEdge e : n.succs) { - if (!visited.contains(e.to)) { - visited.add(e.to); - layers[i + 1].add(e.to); - if (!nodes.contains(e.to)) { - nodes.add(e.to); - } + for (LayoutNode layoutNode : layoutNodes) { + graph.createDummiesForNodeSuccessor(layoutNode, maxLayerLength); + } + + for (int i = 0; i < graph.getLayerCount() - 1; i++) { + for (LayoutNode n : graph.getLayer(i)) { + for (LayoutEdge e : n.getSuccessors()) { + if (e.getTo().isDummy()) continue; + if (!visited.contains(e.getTo())) { + visited.add(e.getTo()); + graph.getLayer(i + 1).add(e.getTo()); + e.getTo().setLayer(i + 1); } } } } - - updatePositions(); - - initX(); - - // Optimize - for (int i = 0; i < CROSSING_ITERATIONS; i++) { - downSweep(); - upSweep(); - } - downSweep(); } - private void initX() { - - for (int i = 0; i < layers.length; i++) { - updateXOfLayer(i); - } + static public void apply(LayoutGraph graph, int maxLayerLength) { + assignLayers(graph); + createDummyNodes(graph, maxLayerLength); + graph.updatePositions(); } + } - private void updateXOfLayer(int index) { - int x = 0; + static class CrossingReduction { - for (LayoutNode n : layers[index]) { - n.x = x; - x += n.width + xOffset; + public static void apply(LayoutGraph graph) { + for (int i = 0; i < graph.getLayerCount(); i++) { + graph.getLayer(i).updateNodeIndices(); + graph.getLayer(i).initXPositions(); } - } - private void updatePositions() { - for (List layer : layers) { - int z = 0; - for (LayoutNode n : layer) { - n.pos = z; - z++; - } + for (int i = 0; i < CROSSING_ITERATIONS; i++) { + downSweep(graph); + upSweep(graph); } + downSweep(graph); } - private void downSweep() { - - // Downsweep - for (int i = 1; i < layerCount; i++) { + private static void downSweep(LayoutGraph graph) { - for (LayoutNode n : layers[i]) { - n.crossingNumber = 0; + for (int i = 1; i < graph.getLayerCount(); i++) { + for (LayoutNode n : graph.getLayer(i)) { + n.setCrossingNumber(0); } - - for (LayoutNode n : layers[i]) { - + for (LayoutNode n : graph.getLayer(i)) { int sum = 0; int count = 0; - for (LayoutEdge e : n.preds) { - int cur = e.from.x + e.relativeFrom; - int factor = 1; - if (e.vip) { - factor = VIP_BONUS; - } - sum += cur * factor; - count += factor; + for (LayoutEdge e : n.getPredecessors()) { + sum += e.getStartX(); + count++; } if (count > 0) { sum /= count; - n.crossingNumber = sum; + n.setCrossingNumber(sum); } } - - updateCrossingNumbers(i, true); - layers[i].sort(crossingNodeComparator); - updateXOfLayer(i); - - int z = 0; - for (LayoutNode n : layers[i]) { - n.pos = z; - z++; - } + updateCrossingNumbers(graph.getLayer(i), true); + graph.getLayer(i).sort(NODE_CROSSING_COMPARATOR); + graph.getLayer(i).initXPositions(); + graph.getLayer(i).updateNodeIndices(); } } - private void updateCrossingNumbers(int index, boolean down) { - for (int i = 0; i < layers[index].size(); i++) { - LayoutNode n = layers[index].get(i); + private static void updateCrossingNumbers(LayoutLayer layer, boolean down) { + for (int i = 0; i < layer.size(); i++) { + LayoutNode n = layer.get(i); LayoutNode prev = null; if (i > 0) { - prev = layers[index].get(i - 1); + prev = layer.get(i - 1); } LayoutNode next = null; - if (i < layers[index].size() - 1) { - next = layers[index].get(i + 1); + if (i < layer.size() - 1) { + next = layer.get(i + 1); } - - boolean cond = n.succs.isEmpty(); + boolean cond = !n.hasSuccessors(); if (down) { - cond = n.preds.isEmpty(); + cond = !n.hasPredecessors(); } - if (cond) { - if (prev != null && next != null) { - n.crossingNumber = (prev.crossingNumber + next.crossingNumber) / 2; + n.setCrossingNumber((prev.getCrossingNumber() + next.getCrossingNumber()) / 2); } else if (prev != null) { - n.crossingNumber = prev.crossingNumber; + n.setCrossingNumber(prev.getCrossingNumber()); } else if (next != null) { - n.crossingNumber = next.crossingNumber; + n.setCrossingNumber(next.getCrossingNumber()); } } } } - private void upSweep() { - // Upsweep - for (int i = layerCount - 2; i >= 0; i--) { - - for (LayoutNode n : layers[i]) { - n.crossingNumber = 0; + private static void upSweep(LayoutGraph graph) { + for (int i = graph.getLayerCount() - 2; i >= 0; i--) { + for (LayoutNode n : graph.getLayer(i)) { + n.setCrossingNumber(0); } - - for (LayoutNode n : layers[i]) { - + for (LayoutNode n : graph.getLayer(i)) { int count = 0; int sum = 0; - for (LayoutEdge e : n.succs) { - int cur = e.to.x + e.relativeTo; - int factor = 1; - if (e.vip) { - factor = VIP_BONUS; - } - sum += cur * factor; - count += factor; + for (LayoutEdge e : n.getSuccessors()) { + sum += e.getEndX(); + count++; } - if (count > 0) { sum /= count; - n.crossingNumber = sum; + n.setCrossingNumber(sum); } } - - updateCrossingNumbers(i, false); - layers[i].sort(crossingNodeComparator); - updateXOfLayer(i); - - int z = 0; - for (LayoutNode n : layers[i]) { - n.pos = z; - z++; - } + updateCrossingNumbers(graph.getLayer(i), false); + graph.getLayer(i).sort(NODE_CROSSING_COMPARATOR); + graph.getLayer(i).initXPositions(); + graph.getLayer(i).updateNodeIndices(); } } - - @Override - public void postCheck() { - - HashSet visited = new HashSet<>(); - for (int i = 0; i < layers.length; i++) { - for (LayoutNode n : layers[i]) { - assert !visited.contains(n); - assert n.layer == i; - visited.add(n); - } - } - - } } - private class AssignYCoordinates extends AlgorithmPart { - - @Override - protected void run() { - int curY = 0; - - for (List layer : layers) { - int maxHeight = 0; - int baseLine = 0; - int bottomBaseLine = 0; - for (LayoutNode n : layer) { - maxHeight = Math.max(maxHeight, n.height - n.yOffset - n.bottomYOffset); - baseLine = Math.max(baseLine, n.yOffset); - bottomBaseLine = Math.max(bottomBaseLine, n.bottomYOffset); - } - - int maxXOffset = 0; - for (LayoutNode n : layer) { - if (n.vertex == null) { - // Dummy node - n.y = curY; - n.height = maxHeight + baseLine + bottomBaseLine; + static class AssignXCoordinates { - } else { - n.y = curY + baseLine + (maxHeight - (n.height - n.yOffset - n.bottomYOffset)) / 2 - n.yOffset; - } + private static List> space; + private static List> downProcessingOrder; + private static List> upProcessingOrder; - for (LayoutEdge e : n.succs) { - int curXOffset = Math.abs(n.x - e.to.x); - maxXOffset = Math.max(curXOffset, maxXOffset); - } + private static final Comparator nodeProcessingDownComparator = (n1, n2) -> { + if (n1.isDummy()) { + if (n2.isDummy()) { + return 0; } - - curY += maxHeight + baseLine + bottomBaseLine; - curY += layerOffset + ((int) (Math.sqrt(maxXOffset) * 1.5)); + return -1; } - } - } - - private class CreateDummyNodes extends AlgorithmPart { - - private int oldNodeCount; - - @Override - protected void preCheck() { - for (LayoutNode n : nodes) { - for (LayoutEdge e : n.succs) { - assert e.from != null; - assert e.from == n; - assert e.from.layer < e.to.layer; - } + if (n2.isDummy()) { + return 1; + } + return n1.getInDegree() - n2.getInDegree(); + }; - for (LayoutEdge e : n.preds) { - assert e.to != null; - assert e.to == n; + private static final Comparator nodeProcessingUpComparator = (n1, n2) -> { + if (n1.isDummy()) { + if (n2.isDummy()) { + return 0; } + return -1; } - } - - @Override - protected void run() { - oldNodeCount = nodes.size(); - - if (combine == Combine.SAME_OUTPUTS) { - - Comparator comparator = Comparator.comparingInt(e -> e.to.layer); - HashMap> portHash = new HashMap<>(); - ArrayList currentNodes = new ArrayList<>(nodes); - for (LayoutNode n : currentNodes) { - portHash.clear(); - - ArrayList succs = new ArrayList<>(n.succs); - HashMap topNodeHash = new HashMap<>(); - HashMap> bottomNodeHash = new HashMap<>(); - for (LayoutEdge e : succs) { - assert e.from.layer < e.to.layer; - if (e.from.layer != e.to.layer - 1) { - if (maxLayerLength != -1 && e.to.layer - e.from.layer > maxLayerLength) { - assert maxLayerLength > 2; - e.to.preds.remove(e); - e.from.succs.remove(e); - - LayoutEdge topEdge; - - if (combine == Combine.SAME_OUTPUTS && topNodeHash.containsKey(e.relativeFrom)) { - LayoutNode topNode = topNodeHash.get(e.relativeFrom); - topEdge = new LayoutEdge(); - topEdge.relativeFrom = e.relativeFrom; - topEdge.from = e.from; - topEdge.relativeTo = topNode.width / 2; - topEdge.to = topNode; - topEdge.link = e.link; - topEdge.vip = e.vip; - e.from.succs.add(topEdge); - topNode.preds.add(topEdge); - } else { - - LayoutNode topNode = new LayoutNode(); - topNode.layer = e.from.layer + 1; - topNode.width = DUMMY_WIDTH; - topNode.height = DUMMY_HEIGHT; - nodes.add(topNode); - topEdge = new LayoutEdge(); - topEdge.relativeFrom = e.relativeFrom; - topEdge.from = e.from; - topEdge.relativeTo = 0; - topEdge.to = topNode; - topEdge.link = e.link; - topEdge.vip = e.vip; - e.from.succs.add(topEdge); - topNode.preds.add(topEdge); - topNodeHash.put(e.relativeFrom, topNode); - bottomNodeHash.put(e.relativeFrom, new HashMap<>()); - } - - HashMap hash = bottomNodeHash.get(e.relativeFrom); - - LayoutNode bottomNode; - if (hash.containsKey(e.to.layer)) { - bottomNode = hash.get(e.to.layer); - } else { - - bottomNode = new LayoutNode(); - bottomNode.layer = e.to.layer - 1; - bottomNode.width = DUMMY_WIDTH; - bottomNode.height = DUMMY_HEIGHT; - nodes.add(bottomNode); - hash.put(e.to.layer, bottomNode); - } - - LayoutEdge bottomEdge = new LayoutEdge(); - bottomEdge.relativeTo = e.relativeTo; - bottomEdge.to = e.to; - bottomEdge.relativeFrom = bottomNode.width / 2; - bottomEdge.from = bottomNode; - bottomEdge.link = e.link; - bottomEdge.vip = e.vip; - e.to.preds.add(bottomEdge); - bottomNode.succs.add(bottomEdge); - - } else { - Integer i = e.relativeFrom; - if (!portHash.containsKey(i)) { - portHash.put(i, new ArrayList<>()); - } - portHash.get(i).add(e); - } - } - } - - succs = new ArrayList<>(n.succs); - for (LayoutEdge e : succs) { - - Integer i = e.relativeFrom; - if (portHash.containsKey(i)) { + if (n2.isDummy()) { + return 1; + } + return n1.getOutDegree() - n2.getOutDegree(); + }; - List list = portHash.get(i); - list.sort(comparator); + public static void apply(LayoutGraph graph) { + space = new ArrayList<>(graph.getLayerCount()); + downProcessingOrder = new ArrayList<>(graph.getLayerCount()); + upProcessingOrder = new ArrayList<>(graph.getLayerCount()); - if (list.size() == 1) { - processSingleEdge(list.get(0)); - } else { + for (int i = 0; i < graph.getLayerCount(); i++) { + // Add a new empty list for each layer + space.add(new ArrayList<>()); + downProcessingOrder.add(new ArrayList<>()); + upProcessingOrder.add(new ArrayList<>()); - int maxLayer = list.get(0).to.layer; - for (LayoutEdge curEdge : list) { - maxLayer = Math.max(maxLayer, curEdge.to.layer); - } + int curX = 0; + for (LayoutNode n : graph.getLayer(i)) { + // Add the current position to space and increment curX + space.get(i).add(curX); + curX += n.getOuterWidth() + NODE_OFFSET; - int cnt = maxLayer - n.layer - 1; - LayoutEdge[] edges = new LayoutEdge[cnt]; - LayoutNode[] nodes = new LayoutNode[cnt]; - edges[0] = new LayoutEdge(); - edges[0].from = n; - edges[0].relativeFrom = i; - edges[0].vip = e.vip; - n.succs.add(edges[0]); - - nodes[0] = new LayoutNode(); - nodes[0].width = dummyWidth; - nodes[0].height = dummyHeight; - nodes[0].layer = n.layer + 1; - nodes[0].preds.add(edges[0]); - edges[0].to = nodes[0]; - edges[0].relativeTo = nodes[0].width / 2; - for (int j = 1; j < cnt; j++) { - edges[j] = new LayoutEdge(); - edges[j].vip = e.vip; - edges[j].from = nodes[j - 1]; - edges[j].relativeFrom = nodes[j - 1].width / 2; - nodes[j - 1].succs.add(edges[j]); - nodes[j] = new LayoutNode(); - nodes[j].width = dummyWidth; - nodes[j].height = dummyHeight; - nodes[j].layer = n.layer + j + 1; - nodes[j].preds.add(edges[j]); - edges[j].to = nodes[j]; - edges[j].relativeTo = nodes[j].width / 2; - } + // Add the current node to processing orders + downProcessingOrder.get(i).add(n); + upProcessingOrder.get(i).add(n); + } - for (LayoutEdge curEdge : list) { - assert curEdge.to.layer - n.layer - 2 >= 0; - assert curEdge.to.layer - n.layer - 2 < cnt; - LayoutNode anchor = nodes[curEdge.to.layer - n.layer - 2]; - anchor.succs.add(curEdge); - curEdge.from = anchor; - curEdge.relativeFrom = anchor.width / 2; - n.succs.remove(curEdge); - } + // Sort the processing orders + downProcessingOrder.get(i).sort(nodeProcessingDownComparator); + upProcessingOrder.get(i).sort(nodeProcessingUpComparator); + } - } + for (LayoutNode n : graph.getLayoutNodes()) { + n.setX(space.get(n.getLayer()).get(n.getPos())); + } - portHash.remove(i); - } - } - } - } else if (combine == Combine.SAME_INPUTS) { - throw new UnsupportedOperationException("Currently not supported"); - } else { - ArrayList currentNodes = new ArrayList<>(nodes); - for (LayoutNode n : currentNodes) { - for (LayoutEdge e : List.copyOf(n.succs)) { - processSingleEdge(e); - } - } + for (LayoutNode n : graph.getDummyNodes()) { + n.setX(space.get(n.getLayer()).get(n.getPos())); } - } - private void processSingleEdge(LayoutEdge e) { - LayoutNode n = e.to; - if (e.to.layer - 1 > e.from.layer) { - LayoutEdge last = e; - for (int i = n.layer - 1; i > last.from.layer; i--) { - last = addBetween(last, i); - } + for (int i = 0; i < SWEEP_ITERATIONS; i++) { + sweepDown(graph); + adjustSpace(graph); + sweepUp(graph); + adjustSpace(graph); } - } - private LayoutEdge addBetween(LayoutEdge e, int layer) { - LayoutNode n = new LayoutNode(); - n.width = DUMMY_WIDTH; - n.height = DUMMY_HEIGHT; - n.layer = layer; - n.succs.add(e); - nodes.add(n); - LayoutEdge result = new LayoutEdge(); - result.vip = e.vip; - n.preds.add(result); - result.to = n; - result.relativeTo = n.width / 2; - result.from = e.from; - result.relativeFrom = e.relativeFrom; - result.link = e.link; - e.relativeFrom = n.width / 2; - e.from.succs.remove(e); - e.from.succs.add(result); - e.from = n; - return result; + sweepDown(graph); + adjustSpace(graph); + sweepUp(graph); } - @Override - public void printStatistics() { - System.out.println("Dummy nodes created: " + (nodes.size() - oldNodeCount)); + private static void adjustSpace(LayoutGraph graph) { + for (int i = 0; i < graph.getLayerCount(); i++) { + for (LayoutNode n : graph.getLayer(i)) { + space.get(i).add(n.getX()); + } + } } - @Override - public void postCheck() { - ArrayList currentNodes = new ArrayList<>(nodes); - for (LayoutNode n : currentNodes) { - for (LayoutEdge e : n.succs) { - assert e.from.layer == e.to.layer - 1; + private static void sweepUp(LayoutGraph graph) { + for (int i = graph.getLayerCount() - 1; i >= 0; i--) { + NodeRow r = new NodeRow(space.get(i)); + for (LayoutNode n : upProcessingOrder.get(i)) { + int optimal = n.calculateOptimalXFromSuccessors(true); + r.insert(n, optimal); } } + } - for (int i = 0; i < layers.length; i++) { - assert layers[i].size() > 0; - for (LayoutNode n : layers[i]) { - assert n.layer == i; + private static void sweepDown(LayoutGraph graph) { + for (int i = 1; i < graph.getLayerCount(); i++) { + NodeRow r = new NodeRow(space.get(i)); + for (LayoutNode n : downProcessingOrder.get(i)) { + int optimal = n.calculateOptimalXFromPredecessors(true); + r.insert(n, optimal); } } } } - private class AssignLayers extends AlgorithmPart { + public static class NodeRow { - @Override - public void preCheck() { - for (LayoutNode n : nodes) { - assert n.layer == -1; - } + private final TreeSet treeSet; + private final ArrayList space; + + public NodeRow(ArrayList space) { + treeSet = new TreeSet<>(NODE_POS_COMPARATOR); + this.space = space; } - @Override - protected void run() { - assignLayerDownwards(); - assignLayerUpwards(); + public int offset(LayoutNode n1, LayoutNode n2) { + int v1 = space.get(n1.getPos()) + n1.getOuterWidth(); + int v2 = space.get(n2.getPos()); + return v2 - v1; } - private void assignLayerDownwards() { - ArrayList hull = new ArrayList<>(); - for (LayoutNode n : nodes) { - if (n.preds.isEmpty()) { - hull.add(n); - n.layer = 0; - } + public void insert(LayoutNode n, int pos) { + + SortedSet headSet = treeSet.headSet(n); + + LayoutNode leftNeighbor; + int minX = Integer.MIN_VALUE; + if (!headSet.isEmpty()) { + leftNeighbor = headSet.last(); + minX = leftNeighbor.getOuterRight() + offset(leftNeighbor, n); } - int z = minLayerDifference; - while (!hull.isEmpty()) { - ArrayList newSet = new ArrayList<>(); - for (LayoutNode n : hull) { - for (LayoutEdge se : n.succs) { - LayoutNode s = se.to; - if (s.layer != -1) { - // This node was assigned before. - } else { - boolean unassignedPred = false; - for (LayoutEdge pe : s.preds) { - LayoutNode p = pe.from; - if (p.layer == -1 || p.layer >= z) { - // This now has an unscheduled successor or a successor that was scheduled only in this round. - unassignedPred = true; - break; - } - } + if (pos < minX) { + n.setX(minX); + } else { - if (unassignedPred) { - // This successor node can not yet be assigned. - } else { - s.layer = z; - newSet.add(s); - } - } - } + LayoutNode rightNeighbor; + SortedSet tailSet = treeSet.tailSet(n); + int maxX = Integer.MAX_VALUE; + if (!tailSet.isEmpty()) { + rightNeighbor = tailSet.first(); + maxX = rightNeighbor.getX() - offset(n, rightNeighbor) - n.getOuterWidth(); } - hull = newSet; - z += minLayerDifference; + n.setX(Math.min(pos, maxX)); } - layerCount = z - minLayerDifference; - for (LayoutNode n : nodes) { - n.layer = (layerCount - 1 - n.layer); - } + treeSet.add(n); } + } - private void assignLayerUpwards() { - ArrayList hull = new ArrayList<>(); - for (LayoutNode n : nodes) { - if (n.succs.isEmpty()) { - hull.add(n); - } else { - n.layer = -1; - } - } + public static class WriteResult { - int z = minLayerDifference; - while (!hull.isEmpty()) { - ArrayList newSet = new ArrayList<>(); - for (LayoutNode n : hull) { - if (n.layer < z) { - for (LayoutEdge se : n.preds) { - LayoutNode s = se.from; - if (s.layer != -1) { - // This node was assigned before. - } else { - boolean unassignedSucc = false; - for (LayoutEdge pe : s.succs) { - LayoutNode p = pe.to; - if (p.layer == -1 || p.layer >= z) { - // This now has an unscheduled successor or a successor that was scheduled only in this round. - unassignedSucc = true; - break; - } - } + private static HashMap> computeLinkPositions(LayoutGraph graph) { + HashMap> linkToSplitEndPoints = new HashMap<>(); + HashMap> linkPositions = new HashMap<>(); + + for (LayoutNode layoutNode : graph.getLayoutNodes()) { + for (LayoutEdge predEdge : layoutNode.getPredecessors()) { + LayoutNode fromNode = predEdge.getFrom(); + LayoutNode toNode = predEdge.getTo(); + + ArrayList linkPoints = new ArrayList<>(); + // input edge stub + linkPoints.add(new Point(predEdge.getEndX(), predEdge.getEndY())); + linkPoints.add(new Point(predEdge.getEndX(), graph.getLayer(toNode.getLayer()).getTop() - LAYER_OFFSET)); + + LayoutEdge curEdge = predEdge; + while (fromNode.isDummy() && fromNode.hasPredecessors()) { + linkPoints.add(new Point(fromNode.getCenterX(), graph.getLayer(fromNode.getLayer()).getBottom() + LAYER_OFFSET)); + linkPoints.add(new Point(fromNode.getCenterX(), graph.getLayer(fromNode.getLayer()).getTop() - LAYER_OFFSET)); + curEdge = fromNode.getPredecessors().get(0); + fromNode = curEdge.getFrom(); + } + linkPoints.add(new Point(curEdge.getStartX(), graph.getLayer(fromNode.getLayer()).getBottom() + LAYER_OFFSET)); + // output edge stub + linkPoints.add(new Point(curEdge.getStartX(), curEdge.getStartY())); + + if (predEdge.isReversed()) { + for (Point relativeEnd : toNode.getReversedLinkEndPoints().get(predEdge.getLink())) { + Point endPoint = new Point(toNode.getLeft() + relativeEnd.x, toNode.getTop() + relativeEnd.y); + linkPoints.add(0, endPoint); + } - if (unassignedSucc) { - // This predecessor node can not yet be assigned. - } else { - s.layer = z; - newSet.add(s); + if (!fromNode.isDummy()) { + if (fromNode.getReversedLinkStartPoints().containsKey(predEdge.getLink())) { + for (Point relativeStart : fromNode.getReversedLinkStartPoints().get(predEdge.getLink())) { + Point startPoint = new Point(fromNode.getLeft() + relativeStart.x, fromNode.getTop() + relativeStart.y); + linkPoints.add(startPoint); } } } } else { - newSet.add(n); + Collections.reverse(linkPoints); } - } - hull = newSet; - z += minLayerDifference; - } - - layerCount = z - minLayerDifference; - - for (LayoutNode n : nodes) { - n.layer = (layerCount - 1 - n.layer); - } - } - - @Override - public void postCheck() { - for (LayoutNode n : nodes) { - assert n.layer >= 0; - assert n.layer < layerCount; - for (LayoutEdge e : n.succs) { - assert e.from.layer < e.to.layer; - } - } - } - } - - private class ReverseEdges extends AlgorithmPart { - - private HashSet visited; - private HashSet active; - - @Override - protected void run() { - - // Reverse inputs of roots - for (LayoutNode node : nodes) { - if (node.vertex.isRoot()) { - boolean ok = true; - for (LayoutEdge e : node.preds) { - if (e.from.vertex.isRoot()) { - ok = false; - break; + if (fromNode.isDummy()) { + if (predEdge.isReversed()) { + Collections.reverse(linkPoints); } - } - if (ok) { - reverseAllInputs(node); + linkToSplitEndPoints.put(predEdge.getLink(), linkPoints); + + } else { + linkPositions.put(predEdge.getLink(), linkPoints); } } } - // Start DFS and reverse back edges - visited = new HashSet<>(); - active = new HashSet<>(); - for (LayoutNode node : nodes) { - DFS(node); - } + for (LayoutNode layoutNode : graph.getLayoutNodes()) { + for (LayoutEdge succEdge : layoutNode.getSuccessors()) { + if (succEdge.getLink() == null) continue; - for (LayoutNode node : nodes) { + LayoutNode fromNode = succEdge.getFrom(); + LayoutNode toNode = succEdge.getTo(); - SortedSet reversedDown = new TreeSet<>(); + ArrayList linkPoints = new ArrayList<>(); + linkPoints.add(new Point(succEdge.getStartX(), fromNode.getBottom())); + linkPoints.add(new Point(succEdge.getStartX(), graph.getLayer(fromNode.getLayer()).getBottom() + LAYER_OFFSET)); - boolean hasSelfEdge = false; - for (LayoutEdge e : node.succs) { - if (reversedLinks.contains(e.link)) { - reversedDown.add(e.relativeFrom); - if (e.from == e.to) { - hasSelfEdge = true; - } + LayoutEdge curEdge = succEdge; + while (toNode.isDummy() && toNode.hasSuccessors()) { + linkPoints.add(new Point(toNode.getCenterX(), graph.getLayer(toNode.getLayer()).getTop() - LAYER_OFFSET)); + linkPoints.add(new Point(toNode.getCenterX(), graph.getLayer(toNode.getLayer()).getBottom() + LAYER_OFFSET)); + curEdge = toNode.getSuccessors().get(0); + toNode = curEdge.getTo(); } - } + linkPoints.add(new Point(curEdge.getEndX(), graph.getLayer(toNode.getLayer()).getTop() - LAYER_OFFSET)); + linkPoints.add(new Point(curEdge.getEndX(), toNode.getTop())); - // Whether the node has non-self reversed edges going downwards. - // If so, reversed edges going upwards are drawn to the left. - boolean hasReversedDown = - reversedDown.size() > 0 && - !(reversedDown.size() == 1 && hasSelfEdge); + if (succEdge.isReversed()) { + Collections.reverse(linkPoints); - SortedSet reversedUp = null; - if (hasReversedDown) { - reversedUp = new TreeSet<>(); - } else { - reversedUp = new TreeSet<>(Collections.reverseOrder()); - } - - for (LayoutEdge e : node.preds) { - if (reversedLinks.contains(e.link)) { - reversedUp.add(e.relativeTo); - } - } - - final int offset = xOffset + DUMMY_WIDTH; - - int curY = 0; - int curWidth = node.width + reversedDown.size() * offset; - for (int pos : reversedDown) { - ArrayList reversedSuccs = new ArrayList<>(); - for (LayoutEdge e : node.succs) { - if (e.relativeFrom == pos && reversedLinks.contains(e.link)) { - reversedSuccs.add(e); - e.relativeFrom = curWidth; + if (fromNode.getReversedLinkStartPoints().containsKey(succEdge.getLink())) { + for (Point relativeStart : fromNode.getReversedLinkStartPoints().get(succEdge.getLink())) { + Point startPoint = new Point(fromNode.getLeft() + relativeStart.x, fromNode.getTop() + relativeStart.y); + linkPoints.add(startPoint); + } } - } - - ArrayList startPoints = new ArrayList<>(); - startPoints.add(new Point(curWidth, curY)); - startPoints.add(new Point(pos, curY)); - startPoints.add(new Point(pos, reversedDown.size() * offset)); - for (LayoutEdge e : reversedSuccs) { - reversedLinkStartPoints.put(e.link, startPoints); - } - - node.inOffsets.put(pos, -curY); - curY += offset; - node.height += offset; - node.yOffset += offset; - curWidth -= offset; - } - - int widthFactor = reversedDown.size(); - if (hasSelfEdge) { - widthFactor--; - } - node.width += widthFactor * offset; - - int curX = 0; - int minX = 0; - if (hasReversedDown) { - minX = -offset * reversedUp.size(); - } - int oldNodeHeight = node.height; - for (int pos : reversedUp) { - ArrayList reversedPreds = new ArrayList<>(); - for (LayoutEdge e : node.preds) { - if (e.relativeTo == pos && reversedLinks.contains(e.link)) { - if (hasReversedDown) { - e.relativeTo = curX - offset; - } else { - e.relativeTo = node.width + offset; + if (!toNode.isDummy()) { + if (toNode.getReversedLinkEndPoints().containsKey(succEdge.getLink())) { + for (Point relativeEnd : toNode.getReversedLinkEndPoints().get(succEdge.getLink())) { + Point endPoint = new Point(toNode.getLeft() + relativeEnd.x, toNode.getTop() + relativeEnd.y); + linkPoints.add(0, endPoint); + } } - - reversedPreds.add(e); } } - node.height += offset; - ArrayList endPoints = new ArrayList<>(); - - node.width += offset; - if (hasReversedDown) { - curX -= offset; - endPoints.add(new Point(curX, node.height)); - } else { - curX += offset; - endPoints.add(new Point(node.width, node.height)); - } - - node.outOffsets.put(pos - minX, curX); - curX += offset; - node.bottomYOffset += offset; - endPoints.add(new Point(pos, node.height)); - endPoints.add(new Point(pos, oldNodeHeight)); - for (LayoutEdge e : reversedPreds) { - reversedLinkEndPoints.put(e.link, endPoints); + if (linkToSplitEndPoints.containsKey(succEdge.getLink())) { + if (succEdge.isReversed()) { + Collections.reverse(linkPoints); + } + linkPoints.add(null); + linkPoints.addAll(linkToSplitEndPoints.get(succEdge.getLink())); + if (succEdge.isReversed()) { + Collections.reverse(linkPoints); + } } + linkPositions.put(succEdge.getLink(), linkPoints); } + } - if (minX < 0) { - for (LayoutEdge e : node.preds) { - e.relativeTo -= minX; - } - - for (LayoutEdge e : node.succs) { - e.relativeFrom -= minX; + if (graph.showSelfEdges()) { + for (LayoutNode layoutNode : graph.getLayoutNodes()) { + if (layoutNode.hasSelfEdge()) { + LayoutEdge selfEdge = layoutNode.getSelfEdge(); + ArrayList points = layoutNode.getSelfEdgePoints(); + for (Point point : points) { + point.setLocation(point.getX() + layoutNode.getLeft(), point.getY() + layoutNode.getTop()); + } + linkPositions.put(selfEdge.getLink(), points); } - - node.xOffset = -minX; - node.width += -minX; } } + return linkPositions; } - private void DFS(LayoutNode startNode) { - if (visited.contains(startNode)) { - return; - } - - Stack stack = new Stack<>(); - stack.push(startNode); + public static void apply(LayoutGraph graph) { + // Assign Y coordinates + graph.positionLayers(); - while (!stack.empty()) { - LayoutNode node = stack.pop(); + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; - if (visited.contains(node)) { - // Node no longer active - active.remove(node); - continue; - } + HashMap> linkPositions = computeLinkPositions(graph); - // Repush immediately to know when no longer active - stack.push(node); - visited.add(node); - active.add(node); - - ArrayList succs = new ArrayList<>(node.succs); - for (LayoutEdge e : succs) { - if (active.contains(e.to)) { - assert visited.contains(e.to); - // Encountered back edge - reverseEdge(e); - } else if (!visited.contains(e.to) && (linksToFollow.size() == 0 || linksToFollow.contains(e.link))) { - stack.push(e.to); + for (List points : linkPositions.values()) { + for (Point point : points) { + if (point != null) { + minX = Math.min(minX, point.x); + minY = Math.min(minY, point.y); } } } - } - private void reverseAllInputs(LayoutNode node) { - for (LayoutEdge e : node.preds) { - assert !reversedLinks.contains(e.link); - reversedLinks.add(e.link); - node.succs.add(e); - e.from.preds.add(e); - e.from.succs.remove(e); - int oldRelativeFrom = e.relativeFrom; - int oldRelativeTo = e.relativeTo; - e.to = e.from; - e.from = node; - e.relativeFrom = oldRelativeTo; - e.relativeTo = oldRelativeFrom; + for (LayoutNode layoutNode : graph.getLayoutNodes()) { + minX = Math.min(minX, layoutNode.getX()); + minY = Math.min(minY, layoutNode.getY()); } - node.preds.clear(); - } - - private void reverseEdge(LayoutEdge e) { - assert !reversedLinks.contains(e.link); - reversedLinks.add(e.link); - LayoutNode oldFrom = e.from; - LayoutNode oldTo = e.to; - int oldRelativeFrom = e.relativeFrom; - int oldRelativeTo = e.relativeTo; - - e.from = oldTo; - e.to = oldFrom; - e.relativeFrom = oldRelativeTo; - e.relativeTo = oldRelativeFrom; - - oldFrom.succs.remove(e); - oldFrom.preds.add(e); - oldTo.preds.remove(e); - oldTo.succs.add(e); - } - - @Override - public void postCheck() { - - for (LayoutNode n : nodes) { - - HashSet curVisited = new HashSet<>(); - Queue queue = new LinkedList<>(); - for (LayoutEdge e : n.succs) { - LayoutNode s = e.to; - queue.add(s); - curVisited.add(s); - } - - while (!queue.isEmpty()) { - LayoutNode curNode = queue.remove(); - - for (LayoutEdge e : curNode.succs) { - assert e.to != n; - if (!curVisited.contains(e.to)) { - queue.add(e.to); - curVisited.add(e.to); - } - } - } + for (LayoutNode dummyNode : graph.getDummyNodes()) { + minX = Math.min(minX, dummyNode.getX()); + minY = Math.min(minY, dummyNode.getY()); } - } - } - private final Comparator linkComparator = (l1, l2) -> { - if (l1.isVIP() && !l2.isVIP()) { - return -1; - } - if (!l1.isVIP() && l2.isVIP()) { - return 1; - } - - int result = l1.getFrom().getVertex().compareTo(l2.getFrom().getVertex()); - if (result != 0) { - return result; - } - result = l1.getTo().getVertex().compareTo(l2.getTo().getVertex()); - if (result != 0) { - return result; - } - result = l1.getFrom().getRelativePosition().x - l2.getFrom().getRelativePosition().x; - if (result != 0) { - return result; - } - result = l1.getTo().getRelativePosition().x - l2.getTo().getRelativePosition().x; - return result; - }; - - private class BuildDatastructure extends AlgorithmPart { - - @Override - protected void run() { - // Set up nodes - List vertices = new ArrayList<>(graph.getVertices()); - // Order roots first to create more natural layer assignments. - vertices.sort((Vertex a, Vertex b) -> - a.isRoot() == b.isRoot() ? a.compareTo(b) : Boolean.compare(b.isRoot(), a.isRoot())); - - for (Vertex v : vertices) { - LayoutNode node = new LayoutNode(); - Dimension size = v.getSize(); - node.width = (int) size.getWidth(); - node.height = (int) size.getHeight(); - node.vertex = v; - nodes.add(node); - vertexToLayoutNode.put(v, node); + for (LayoutLayer layer : graph.getLayers()) { + minY = Math.min(minY, layer.getTop()); } - // Set up edges - List links = new ArrayList<>(graph.getLinks()); - links.sort(linkComparator); - for (Link l : links) { - LayoutEdge edge = new LayoutEdge(); - assert vertexToLayoutNode.containsKey(l.getFrom().getVertex()); - assert vertexToLayoutNode.containsKey(l.getTo().getVertex()); - edge.from = vertexToLayoutNode.get(l.getFrom().getVertex()); - edge.to = vertexToLayoutNode.get(l.getTo().getVertex()); - edge.relativeFrom = l.getFrom().getRelativePosition().x; - edge.relativeTo = l.getTo().getRelativePosition().x; - edge.link = l; - edge.vip = l.isVIP(); - edge.from.succs.add(edge); - edge.to.preds.add(edge); + for (LayoutNode layoutNode : graph.getLayoutNodes()) { + layoutNode.setX(layoutNode.getX() - minX); + layoutNode.setY(layoutNode.getY() - minY); } - - for (Link l : importantLinks) { - if (!vertexToLayoutNode.containsKey(l.getFrom().getVertex()) - || vertexToLayoutNode.containsKey(l.getTo().getVertex())) { - continue; - } - LayoutNode from = vertexToLayoutNode.get(l.getFrom().getVertex()); - LayoutNode to = vertexToLayoutNode.get(l.getTo().getVertex()); - for (LayoutEdge e : from.succs) { - if (e.to == to) { - linksToFollow.add(e.link); - } - } + for (LayoutNode dummyNode : graph.getDummyNodes()) { + dummyNode.setX(dummyNode.getX() - minX); + dummyNode.setY(dummyNode.getY() - minY); } - } - - @Override - public void postCheck() { - assert vertexToLayoutNode.keySet().size() == nodes.size(); - assert nodes.size() == graph.getVertices().size(); - - for (Vertex v : graph.getVertices()) { - - LayoutNode node = vertexToLayoutNode.get(v); - assert node != null; + for (LayoutLayer layer : graph.getLayers()) { + layer.moveLayerVertically(-minY); + } - for (LayoutEdge e : node.succs) { - assert e.from == node; - } + // Shift vertices by minX/minY + for (LayoutNode layoutNode : graph.getLayoutNodes()) { + Vertex vertex = layoutNode.getVertex(); + vertex.setPosition(new Point(layoutNode.getLeft(), layoutNode.getTop())); + } - for (LayoutEdge e : node.preds) { - assert e.to == node; + // shift links by minX/minY + for (Map.Entry> entry : linkPositions.entrySet()) { + Link link = entry.getKey(); + List points = entry.getValue(); + for (Point p : points) { + if (p != null) { + p.x -= minX; + p.y -= minY; + } } + // write points back to links + link.setControlPoints(points); } } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalStableLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalStableLayoutManager.java index 2d76d15394c..afd692e8446 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalStableLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalStableLayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -23,29 +23,23 @@ */ package com.sun.hotspot.igv.hierarchicallayout; -import com.sun.hotspot.igv.layout.LayoutGraph; +import static com.sun.hotspot.igv.hierarchicallayout.LayoutManager.LAYER_OFFSET; +import static com.sun.hotspot.igv.hierarchicallayout.LayoutManager.NODE_OFFSET; import com.sun.hotspot.igv.layout.Link; import com.sun.hotspot.igv.layout.Vertex; -import com.sun.hotspot.igv.util.Statistics; import java.awt.Dimension; import java.awt.Point; import java.util.*; public class HierarchicalStableLayoutManager { - public static final int DUMMY_HEIGHT = 1; - public static final int DUMMY_WIDTH = 1; - public static final int X_OFFSET = 8; - public static final int LAYER_OFFSET = 8; // Algorithm global data structures private Set currentVertices; private Set currentLinks; private Set reversedLinks; private List nodes; private final HashMap vertexToLayoutNode; - private HashMap> reversedLinkStartPoints; - private HashMap> reversedLinkEndPoints; - private HashMap> layers; + private HashMap layers; private final HierarchicalLayoutManager manager; private HashMap vertexToAction; @@ -94,7 +88,7 @@ public LinkAction(Link link, Action action) { public HierarchicalStableLayoutManager() { oldVertices = new HashSet<>(); oldLinks = new HashSet<>(); - manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS); + manager = new HierarchicalLayoutManager(); vertexToLayoutNode = new HashMap<>(); nodes = new ArrayList<>(); } @@ -109,26 +103,48 @@ public boolean getCutEdges() { } private int calculateOptimalBoth(LayoutNode n) { - if (n.preds.isEmpty() && n.succs.isEmpty()) { - return n.x; + if (n.getPredecessors().isEmpty() && n.getSuccessors().isEmpty()) { + return n.getX(); } - int[] values = new int[n.preds.size() + n.succs.size()]; + int[] values = new int[n.getPredecessors().size() + n.getSuccessors().size()]; int i = 0; - for (LayoutEdge e : n.preds) { - values[i] = e.from.x + e.relativeFrom - e.relativeTo; + for (LayoutEdge e : n.getPredecessors()) { + values[i] = e.getFrom().getX() + e.getRelativeFromX() - e.getRelativeToX(); i++; } - for (LayoutEdge e : n.succs) { - values[i] = e.to.x + e.relativeTo - e.relativeFrom; + for (LayoutEdge e : n.getSuccessors()) { + values[i] = e.getTo().getX() + e.getRelativeToX() - e.getRelativeFromX(); i++; } - return Statistics.median(values); + return median(values); } + public static int median(int[] values) { + Arrays.sort(values); + if (values.length % 2 == 0) { + return (values[values.length / 2 - 1] + values[values.length / 2]) / 2; + } else { + return values[values.length / 2]; + } + } + + public static final Comparator nodeProcessingUpComparator = (n1, n2) -> { + if (n1.getVertex() == null) { + if (n2.getVertex() == null) { + return 0; + } + return -1; + } + if (n2.getVertex() == null) { + return 1; + } + return n1.getSuccessors().size() - n2.getSuccessors().size(); + }; + /** * Adjust the X-coordinates of the nodes in the given layer, as a new node has * been inserted at that layer @@ -138,16 +154,16 @@ private void adjustXCoordinates(int layer) { ArrayList space = new ArrayList<>(); List nodeProcessingOrder = new ArrayList<>(); - nodes.sort(HierarchicalLayoutManager.nodePositionComparator); + nodes.sort(Comparator.comparingInt(LayoutNode::getPos)); int curX = 0; for (LayoutNode n : nodes) { space.add(curX); - curX += n.width + X_OFFSET; + curX += n.getOuterWidth() + NODE_OFFSET; nodeProcessingOrder.add(n); } - nodeProcessingOrder.sort(HierarchicalLayoutManager.nodeProcessingUpComparator); + nodeProcessingOrder.sort(nodeProcessingUpComparator); HierarchicalLayoutManager.NodeRow r = new HierarchicalLayoutManager.NodeRow(space); for (LayoutNode n : nodeProcessingOrder) { int optimal = calculateOptimalBoth(n); @@ -157,8 +173,8 @@ private void adjustXCoordinates(int layer) { private void ensureNeighborEdgeConsistency() { for (LayoutNode n : nodes) { - n.succs.removeIf(e -> !nodes.contains(e.to)); - n.preds.removeIf(e -> !nodes.contains(e.from)); + n.getSuccessorsRaw().removeIf(e -> !nodes.contains(e.getTo())); + n.getPredecessorsRaw().removeIf(e -> !nodes.contains(e.getFrom())); } } @@ -248,8 +264,8 @@ private void findInitialReversedLinks() { for (Link link : oldLinks) { for (Link l : currentLinks) { if (l.equals(link)) { - if (vertexToLayoutNode.get(l.getFrom().getVertex()).layer > vertexToLayoutNode - .get(l.getTo().getVertex()).layer) { + if (vertexToLayoutNode.get(l.getFrom().getVertex()).getLayer() > vertexToLayoutNode + .get(l.getTo().getVertex()).getLayer()) { // Link is reversed reversedLinks.add(l); updateReversedLinkPositions(l); @@ -265,7 +281,7 @@ private void updateReversedLinkPositions(Link link) { // Correct direction, is reversed link assert fromNode != null && toNode != null; assert nodes.contains(fromNode) && nodes.contains(toNode); - assert fromNode.layer > toNode.layer; + assert fromNode.getLayer() > toNode.getLayer(); assert reversedLinks.contains(link); updateNodeWithReversedEdges(fromNode); @@ -273,138 +289,7 @@ private void updateReversedLinkPositions(Link link) { } private void updateNodeWithReversedEdges(LayoutNode node) { - // Reset node data in case there were previous reversed edges - node.width = (int) node.vertex.getSize().getWidth(); - node.height = (int) node.vertex.getSize().getHeight(); - node.yOffset = 0; - node.bottomYOffset = 0; - node.xOffset = 0; - node.inOffsets.clear(); - node.outOffsets.clear(); - - SortedSet reversedDown = new TreeSet<>(); - - // Reset relativeFrom for all succ edges - for (LayoutEdge e : node.succs) { - if (e.link == null) { - continue; - } - e.relativeFrom = e.link.getFrom().getRelativePosition().x; - if (reversedLinks.contains(e.link)) { - e.relativeFrom = e.link.getTo().getRelativePosition().x; - reversedDown.add(e.relativeFrom); - } - } - - // Whether the node has non-self reversed edges going downwards. - // If so, reversed edges going upwards are drawn to the left. - boolean hasReversedDown = !reversedDown.isEmpty(); - - SortedSet reversedUp; - if (hasReversedDown) { - reversedUp = new TreeSet<>(); - } else { - reversedUp = new TreeSet<>(Collections.reverseOrder()); - } - - // Reset relativeTo for all pred edges - for (LayoutEdge e : node.preds) { - if (e.link == null) { - continue; - } - e.relativeTo = e.link.getTo().getRelativePosition().x; - if (reversedLinks.contains(e.link)) { - e.relativeTo = e.link.getFrom().getRelativePosition().x; - reversedUp.add(e.relativeTo); - } - } - - final int offset = X_OFFSET + DUMMY_WIDTH; - - int curY = 0; - int curWidth = node.width + reversedDown.size() * offset; - for (int pos : reversedDown) { - ArrayList reversedSuccs = new ArrayList<>(); - for (LayoutEdge e : node.succs) { - if (e.relativeFrom == pos && reversedLinks.contains(e.link)) { - reversedSuccs.add(e); - e.relativeFrom = curWidth; - } - } - - ArrayList startPoints = new ArrayList<>(); - startPoints.add(new Point(curWidth, curY)); - startPoints.add(new Point(pos, curY)); - startPoints.add(new Point(pos, reversedDown.size() * offset)); - for (LayoutEdge e : reversedSuccs) { - reversedLinkStartPoints.put(e.link, startPoints); - } - - node.inOffsets.put(pos, -curY); - curY += offset; - node.height += offset; - node.yOffset += offset; - curWidth -= offset; - } - - int widthFactor = reversedDown.size(); - node.width += widthFactor * offset; - - int curX = 0; - int minX = 0; - if (hasReversedDown) { - minX = -offset * reversedUp.size(); - } - - int oldNodeHeight = node.height; - for (int pos : reversedUp) { - ArrayList reversedPreds = new ArrayList<>(); - for (LayoutEdge e : node.preds) { - if (e.relativeTo == pos && reversedLinks.contains(e.link)) { - if (hasReversedDown) { - e.relativeTo = curX - offset; - } else { - e.relativeTo = node.width + offset; - } - - reversedPreds.add(e); - } - } - node.height += offset; - ArrayList endPoints = new ArrayList<>(); - - node.width += offset; - if (hasReversedDown) { - curX -= offset; - endPoints.add(new Point(curX, node.height)); - } else { - curX += offset; - endPoints.add(new Point(node.width, node.height)); - } - - node.outOffsets.put(pos - minX, curX); - curX += offset; - node.bottomYOffset += offset; - - endPoints.add(new Point(pos, node.height)); - endPoints.add(new Point(pos, oldNodeHeight)); - for (LayoutEdge e : reversedPreds) { - reversedLinkEndPoints.put(e.link, endPoints); - } - } - - if (minX < 0) { - for (LayoutEdge e : node.preds) { - e.relativeTo -= minX; - } - - for (LayoutEdge e : node.succs) { - e.relativeFrom -= minX; - } - - node.xOffset = -minX; - node.width -= minX; - } + node.computeReversedLinkPoints(false); } /** @@ -418,8 +303,6 @@ public void updateLayout(Set vertices, Set lin currentVertices = vertices; currentLinks = links; reversedLinks = new HashSet<>(); - reversedLinkStartPoints = new HashMap<>(); - reversedLinkEndPoints = new HashMap<>(); vertexActions = new LinkedList<>(); linkActions = new LinkedList<>(); vertexToAction = new HashMap<>(); @@ -430,12 +313,12 @@ public void updateLayout(Set vertices, Set lin // If the layout is too messy it should be redrawn using the static algorithm, // currently HierarchicalLayoutManager manager.doLayout(new LayoutGraph(links, vertices)); - nodes = manager.getNodes(); + nodes = new ArrayList<>(manager.getNodes()); shouldRedrawLayout = false; } else { generateActions(); - new BuildDatastructure().run(); + new BuildDatastructures().run(); findInitialReversedLinks(); @@ -471,7 +354,7 @@ private void run() { } } - private class BuildDatastructure { + private class BuildDatastructures { // In case there are changes in the node size, its layer must be updated Set layersToUpdate = new HashSet<>(); @@ -482,53 +365,51 @@ private class BuildDatastructure { */ private void updateNodeObjects() { for (LayoutNode node : nodes) { - if (node.vertex != null) { + if (node.getVertex() != null) { for (Vertex vertex : currentVertices) { - if (vertex.equals(node.vertex)) { + if (vertex.equals(node.getVertex())) { Dimension size = vertex.getSize(); - if (node.width < (int) size.getWidth()) { - layersToUpdate.add(node.layer); + if (node.getOuterWidth() < (int) size.getWidth()) { + layersToUpdate.add(node.getLayer()); } - node.width = (int) size.getWidth(); - node.height = (int) size.getHeight(); - node.vertex = vertex; + node.initSize(); + node.setVertex(vertex); } } - vertexToLayoutNode.put(node.vertex, node); + vertexToLayoutNode.put(node.getVertex(), node); } else { - node.height = DUMMY_HEIGHT; - node.width = DUMMY_WIDTH; + node.initSize(); } - for (LayoutEdge edge : node.preds) { - if (edge.link != null) { + for (LayoutEdge edge : node.getPredecessors()) { + if (edge.getLink() != null) { for (Link link : currentLinks) { - if (link.equals(edge.link)) { - edge.link = link; - if (link.getTo().getVertex().equals(edge.from.vertex)) { + if (link.equals(edge.getLink())) { + edge.setLink(link); + if (link.getTo().getVertex().equals(edge.getFrom().getVertex())) { // reversed link - edge.relativeFrom = link.getTo().getRelativePosition().x; - edge.relativeTo = link.getFrom().getRelativePosition().x; + edge.setRelativeFromX(link.getTo().getRelativePosition().x); + edge.setRelativeToX(link.getFrom().getRelativePosition().x); } else { - edge.relativeFrom = link.getFrom().getRelativePosition().x; - edge.relativeTo = link.getTo().getRelativePosition().x; + edge.setRelativeFromX(link.getFrom().getRelativePosition().x); + edge.setRelativeToX(link.getTo().getRelativePosition().x); } break; } } } } - for (LayoutEdge edge : node.succs) { - if (edge.link != null) { + for (LayoutEdge edge : node.getSuccessors()) { + if (edge.getLink() != null) { for (Link link : currentLinks) { - if (link.equals(edge.link)) { - edge.link = link; - if (link.getTo().getVertex().equals(edge.from.vertex)) { + if (link.equals(edge.getLink())) { + edge.setLink(link); + if (link.getTo().getVertex().equals(edge.getFrom().getVertex())) { // reversed link - edge.relativeFrom = link.getTo().getRelativePosition().x; - edge.relativeTo = link.getFrom().getRelativePosition().x; + edge.setRelativeFromX(link.getTo().getRelativePosition().x); + edge.setRelativeToX(link.getFrom().getRelativePosition().x); } else { - edge.relativeFrom = link.getFrom().getRelativePosition().x; - edge.relativeTo = link.getTo().getRelativePosition().x; + edge.setRelativeFromX(link.getFrom().getRelativePosition().x); + edge.setRelativeToX(link.getTo().getRelativePosition().x); } break; } @@ -544,14 +425,14 @@ private void updateNodeObjects() { private void storeNodeLayers() { layers = new HashMap<>(); for (LayoutNode node : nodes) { - if (!layers.containsKey(node.layer)) { - layers.put(node.layer, new ArrayList<>()); + if (!layers.containsKey(node.getLayer())) { + layers.put(node.getLayer(), new LayoutLayer()); } - layers.get(node.layer).add(node); + layers.get(node.getLayer()).add(node); } for (int i = 0; i < layers.keySet().size(); i++) { if (!layers.containsKey(i)) { - layers.put(i, new ArrayList<>()); + layers.put(i, new LayoutLayer()); } } } @@ -578,7 +459,7 @@ private int optimalPosition(LayoutNode node, int layer) { assert layers.containsKey(layer); List layerNodes = layers.get(layer); - layerNodes.sort(HierarchicalLayoutManager.nodePositionComparator); + layerNodes.sort(Comparator.comparingInt(LayoutNode::getPos)); int edgeCrossings = Integer.MAX_VALUE; int optimalPos = -1; @@ -586,9 +467,9 @@ private int optimalPosition(LayoutNode node, int layer) { for (int i = 0; i < layerNodes.size() + 1; i++) { int xCoord; if (i == 0) { - xCoord = layerNodes.get(i).x - node.width - 1; + xCoord = layerNodes.get(i).getX() - node.getOuterWidth() - 1; } else { - xCoord = layerNodes.get(i - 1).x + layerNodes.get(i - 1).width + 1; + xCoord = layerNodes.get(i - 1).getX() + layerNodes.get(i - 1).getOuterWidth() + 1; } int currentCrossings = 0; @@ -596,28 +477,28 @@ private int optimalPosition(LayoutNode node, int layer) { if (layers.containsKey(layer - 1)) { List predNodes = layers.get(layer - 1); // For each link with an end point in vertex, check how many edges cross it - for (LayoutEdge edge : node.preds) { - if (edge.from.layer == layer - 1) { - int fromNodeXCoord = edge.from.x; - if (edge.from.vertex != null) { - fromNodeXCoord += edge.relativeFrom; + for (LayoutEdge edge : node.getPredecessors()) { + if (edge.getFrom().getLayer() == layer - 1) { + int fromNodeXCoord = edge.getFrom().getX(); + if (edge.getFrom().getVertex() != null) { + fromNodeXCoord += edge.getRelativeFromX(); } int toNodeXCoord = xCoord; - if (node.vertex != null) { - toNodeXCoord += edge.relativeTo; + if (node.getVertex() != null) { + toNodeXCoord += edge.getRelativeToX(); } for (LayoutNode n : predNodes) { - for (LayoutEdge e : n.succs) { - if (e.to == null) { + for (LayoutEdge e : n.getSuccessors()) { + if (e.getTo() == null) { continue; } - int compFromXCoord = e.from.x; - if (e.from.vertex != null) { - compFromXCoord += e.relativeFrom; + int compFromXCoord = e.getFrom().getX(); + if (e.getFrom().getVertex() != null) { + compFromXCoord += e.getRelativeFromX(); } - int compToXCoord = e.to.x; - if (e.to.vertex != null) { - compToXCoord += e.relativeTo; + int compToXCoord = e.getTo().getX(); + if (e.getTo().getVertex() != null) { + compToXCoord += e.getRelativeToX(); } if ((fromNodeXCoord > compFromXCoord && toNodeXCoord < compToXCoord) || (fromNodeXCoord < compFromXCoord @@ -633,28 +514,28 @@ private int optimalPosition(LayoutNode node, int layer) { if (layers.containsKey(layer + 1)) { List succsNodes = layers.get(layer + 1); // For each link with an end point in vertex, check how many edges cross it - for (LayoutEdge edge : node.succs) { - if (edge.to.layer == layer + 1) { - int toNodeXCoord = edge.to.x; - if (edge.to.vertex != null) { - toNodeXCoord += edge.relativeTo; + for (LayoutEdge edge : node.getSuccessors()) { + if (edge.getTo().getLayer() == layer + 1) { + int toNodeXCoord = edge.getTo().getX(); + if (edge.getTo().getVertex() != null) { + toNodeXCoord += edge.getRelativeToX(); } int fromNodeXCoord = xCoord; - if (node.vertex != null) { - fromNodeXCoord += edge.relativeFrom; + if (node.getVertex() != null) { + fromNodeXCoord += edge.getRelativeFromX(); } for (LayoutNode n : succsNodes) { - for (LayoutEdge e : n.preds) { - if (e.from == null) { + for (LayoutEdge e : n.getPredecessors()) { + if (e.getFrom() == null) { continue; } - int compFromXCoord = e.from.x; - if (e.from.vertex != null) { - compFromXCoord += e.relativeFrom; + int compFromXCoord = e.getFrom().getX(); + if (e.getFrom().getVertex() != null) { + compFromXCoord += e.getRelativeFromX(); } - int compToXCoord = e.to.x; - if (e.to.vertex != null) { - compToXCoord += e.relativeTo; + int compToXCoord = e.getTo().getX(); + if (e.getTo().getVertex() != null) { + compToXCoord += e.getRelativeToX(); } if ((fromNodeXCoord > compFromXCoord && toNodeXCoord < compToXCoord) || (fromNodeXCoord < compFromXCoord @@ -682,18 +563,18 @@ private int optimalPosition(LayoutNode node, int layer) { private void insertNode(LayoutNode node, int layer) { assert layers.containsKey(layer) || layer == 0; - node.layer = layer; - List layerNodes = layers.getOrDefault(layer, new ArrayList()); + node.setLayer(layer); + LayoutLayer layerNodes = layers.getOrDefault(layer, new LayoutLayer()); if (layerNodes.isEmpty()) { - node.pos = 0; + node.setPos(0); } else { - node.pos = optimalPosition(node, layer); + node.setPos(optimalPosition(node, layer)); } for (LayoutNode n : layerNodes) { - if (n.pos >= node.pos) { - n.pos += 1; + if (n.getPos() >= node.getPos()) { + n.setPos(n.getPos() + 1); } } layerNodes.add(node); @@ -702,18 +583,18 @@ private void insertNode(LayoutNode node, int layer) { if (!nodes.contains(node)) { nodes.add(node); } - if (node.vertex != null) { - vertexToLayoutNode.put(node.vertex, node); + if (node.getVertex() != null) { + vertexToLayoutNode.put(node.getVertex(), node); } adjustXCoordinates(layer); } private void processSingleEdge(LayoutEdge e) { - LayoutNode n = e.to; - if (e.to.layer - 1 > e.from.layer) { + LayoutNode n = e.getTo(); + if (e.getTo().getLayer() - 1 > e.getFrom().getLayer()) { LayoutEdge last = e; - for (int i = n.layer - 1; i > last.from.layer; i--) { + for (int i = n.getLayer() - 1; i > last.getFrom().getLayer(); i--) { last = addBetween(last, i); } } @@ -721,34 +602,26 @@ private void processSingleEdge(LayoutEdge e) { private LayoutEdge addBetween(LayoutEdge e, int layer) { LayoutNode n = new LayoutNode(); - n.width = DUMMY_WIDTH; - n.height = DUMMY_HEIGHT; - n.succs.add(e); - LayoutEdge result = new LayoutEdge(); - result.vip = e.vip; - n.preds.add(result); - result.to = n; - result.relativeTo = n.width / 2; - result.from = e.from; - result.relativeFrom = e.relativeFrom; - result.link = e.link; - e.relativeFrom = n.width / 2; - e.from.succs.remove(e); - e.from.succs.add(result); - e.from = n; + n.addSuccessor(e); + LayoutEdge result = new LayoutEdge(e.getFrom(), n, e.getRelativeFromX(), n.getOuterWidth() / 2, e.getLink()); + n.addPredecessor(result); + e.setRelativeFromX(n.getOuterWidth() / 2); + e.getFrom().removeSuccessor(e); + e.getFrom().addSuccessor(result); + e.setFrom(n); insertNode(n, layer); return result; } private void insertDummyNodes(LayoutEdge edge) { - LayoutNode from = edge.from; - LayoutNode to = edge.to; + LayoutNode from = edge.getFrom(); + LayoutNode to = edge.getTo(); boolean hasEdgeFromSamePort = false; - LayoutEdge edgeFromSamePort = new LayoutEdge(); + LayoutEdge edgeFromSamePort = null; - for (LayoutEdge e : edge.from.succs) { - if (e.relativeFrom == edge.relativeFrom && e.to.vertex == null) { + for (LayoutEdge e : edge.getFrom().getSuccessors()) { + if (e.getRelativeFromX() == edge.getRelativeFromX() && e.getTo().getVertex() == null) { edgeFromSamePort = e; hasEdgeFromSamePort = true; break; @@ -760,16 +633,16 @@ private void insertDummyNodes(LayoutEdge edge) { } else { LayoutEdge curEdge = edgeFromSamePort; boolean newEdge = true; - while (curEdge.to.layer < to.layer - 1 && curEdge.to.vertex == null && newEdge) { + while (curEdge.getTo().getLayer() < to.getLayer() - 1 && curEdge.getTo().getVertex() == null && newEdge) { // Traverse down the chain of dummy nodes linking together the edges originating // from the same port newEdge = false; - if (curEdge.to.succs.size() == 1) { - curEdge = curEdge.to.succs.get(0); + if (curEdge.getTo().getSuccessors().size() == 1) { + curEdge = curEdge.getTo().getSuccessors().get(0); newEdge = true; } else { - for (LayoutEdge e : curEdge.to.succs) { - if (e.to.vertex == null) { + for (LayoutEdge e : curEdge.getTo().getSuccessors()) { + if (e.getTo().getVertex() == null) { curEdge = e; newEdge = true; break; @@ -779,27 +652,27 @@ private void insertDummyNodes(LayoutEdge edge) { } LayoutNode prevDummy; - if (curEdge.to.vertex != null) { - prevDummy = curEdge.from; + if (curEdge.getTo().getVertex() != null) { + prevDummy = curEdge.getFrom(); } else { - prevDummy = curEdge.to; + prevDummy = curEdge.getTo(); } - edge.from = prevDummy; - edge.relativeFrom = prevDummy.width / 2; - from.succs.remove(edge); - prevDummy.succs.add(edge); + edge.setFrom(prevDummy); + edge.setRelativeFromX(prevDummy.getOuterWidth() / 2); + from.removeSuccessor(edge); + prevDummy.addSuccessor(edge); processSingleEdge(edge); } } private boolean canMoveNodeUp(LayoutNode node) { - if (node.layer == 0) { + if (node.getLayer() == 0) { return false; } - int newLayer = node.layer - 1; - for (LayoutEdge e : node.preds) { - if (e.from.vertex != null && e.from.layer == newLayer) { + int newLayer = node.getLayer() - 1; + for (LayoutEdge e : node.getPredecessors()) { + if (e.getFrom().getVertex() != null && e.getFrom().getLayer() == newLayer) { return false; } } @@ -807,12 +680,12 @@ private boolean canMoveNodeUp(LayoutNode node) { } private boolean canMoveNodeDown(LayoutNode node) { - if (node.layer == layers.keySet().size() - 1) { + if (node.getLayer() == layers.keySet().size() - 1) { return false; } - int newLayer = node.layer + 1; - for (LayoutEdge e : node.succs) { - if (e.to.vertex != null && e.to.layer == newLayer) { + int newLayer = node.getLayer() + 1; + for (LayoutEdge e : node.getSuccessors()) { + if (e.getTo().getVertex() != null && e.getTo().getLayer() == newLayer) { return false; } } @@ -822,23 +695,23 @@ private boolean canMoveNodeDown(LayoutNode node) { private void moveNodeUp(LayoutNode node) { assert canMoveNodeUp(node); - List previousPredEdges = List.copyOf(node.preds); + List previousPredEdges = List.copyOf(node.getPredecessors()); for (LayoutEdge edge : previousPredEdges) { - LayoutNode predNode = edge.from; - assert predNode.vertex == null; - for (LayoutEdge e : predNode.preds) { - e.to = edge.to; - e.relativeTo = edge.relativeTo; - node.preds.add(e); - node.preds.remove(edge); + LayoutNode predNode = edge.getFrom(); + assert predNode.getVertex() == null; + for (LayoutEdge e : predNode.getPredecessors()) { + e.setTo(edge.getTo()); + e.setRelativeToX(edge.getRelativeToX()); + node.addPredecessor(e); + node.removePredecessor(edge); } removeNodeWithoutRemovingLayer(predNode); } removeNodeWithoutRemovingLayer(node); - insertNode(node, node.layer - 1); + insertNode(node, node.getLayer() - 1); - for (LayoutEdge edge : List.copyOf(node.succs)) { + for (LayoutEdge edge : List.copyOf(node.getSuccessors())) { processSingleEdge(edge); } } @@ -846,23 +719,23 @@ private void moveNodeUp(LayoutNode node) { private void moveNodeDown(LayoutNode node) { assert canMoveNodeDown(node); - List previousSuccEdges = List.copyOf(node.succs); + List previousSuccEdges = List.copyOf(node.getSuccessors()); for (LayoutEdge edge : previousSuccEdges) { - LayoutNode succNode = edge.to; - assert succNode.vertex == null; - for (LayoutEdge e : succNode.succs) { - e.from = edge.from; - e.relativeFrom = edge.relativeFrom; - node.succs.add(e); - node.succs.remove(edge); + LayoutNode succNode = edge.getTo(); + assert succNode.getVertex() == null; + for (LayoutEdge e : succNode.getSuccessors()) { + e.setFrom(edge.getFrom()); + e.setRelativeFromX(edge.getRelativeFromX()); + node.addSuccessor(e); + node.removeSuccessor(edge); } removeNodeWithoutRemovingLayer(succNode); } removeNodeWithoutRemovingLayer(node); - insertNode(node, node.layer + 1); + insertNode(node, node.getLayer() + 1); - for (LayoutEdge edge : List.copyOf(node.preds)) { + for (LayoutEdge edge : List.copyOf(node.getPredecessors())) { processSingleEdge(edge); } } @@ -884,25 +757,25 @@ private void handleNeighborNodesOnSameLayer(LayoutNode from, LayoutNode to) { * remaining layers numbers */ private void expandNewLayerBeneath(LayoutNode node) { - int layer = node.layer + 1; + int layer = node.getLayer() + 1; // Move all necessary layers down one step for (int i = layers.size() - 1; i >= layer; i--) { - List list = layers.get(i); + LayoutLayer list = layers.get(i); for (LayoutNode n : list) { - n.layer = i + 1; + n.setLayer(i + 1); } layers.remove(i); layers.put(i + 1, list); } // Create new empty layer - List l = new ArrayList<>(); + LayoutLayer l = new LayoutLayer(); layers.put(layer, l); assert layers.get(layer).isEmpty(); for (LayoutNode n : nodes) { - assert n.layer != layer; + assert n.getLayer() != layer; } // Add dummy nodes for edges going across new layer. One for each port on the @@ -911,35 +784,28 @@ private void expandNewLayerBeneath(LayoutNode node) { for (LayoutNode n : predLayer) { HashMap> portHashes = new HashMap<>(); - for (LayoutEdge e : n.succs) { - if (!portHashes.containsKey(e.relativeFrom)) { - portHashes.put(e.relativeFrom, new ArrayList<>()); + for (LayoutEdge e : n.getSuccessors()) { + if (!portHashes.containsKey(e.getRelativeFromX())) { + portHashes.put(e.getRelativeFromX(), new ArrayList<>()); } - portHashes.get(e.relativeFrom).add(e); + portHashes.get(e.getRelativeFromX()).add(e); } for (Integer i : portHashes.keySet()) { List edges = portHashes.get(i); LayoutNode dummy = new LayoutNode(); - dummy.width = DUMMY_WIDTH; - dummy.height = DUMMY_HEIGHT; - - LayoutEdge newEdge = new LayoutEdge(); - newEdge.from = n; - newEdge.relativeFrom = i; - newEdge.to = dummy; - newEdge.relativeTo = dummy.width / 2; - newEdge.link = edges.get(0).link; // issue? - n.succs.add(newEdge); - dummy.preds.add(newEdge); + + LayoutEdge newEdge = new LayoutEdge(n, dummy, i, dummy.getOuterWidth() / 2, edges.get(0).getLink()); + n.addSuccessor(newEdge); + dummy.addPredecessor(newEdge); for (LayoutEdge e : edges) { - e.from = dummy; - e.relativeFrom = dummy.width / 2; - n.succs.remove(e); - dummy.succs.add(e); - assert e.to.layer == layer + 1; + e.setFrom(dummy); + e.setRelativeFromX(dummy.getOuterWidth() / 2); + n.removeSuccessor(e); + dummy.addSuccessor(e); + assert e.getTo().getLayer() == layer + 1; } insertNode(dummy, layer); @@ -949,7 +815,7 @@ private void expandNewLayerBeneath(LayoutNode node) { // Move node to new layer moveNodeDown(node); assert layers.get(layer).contains(node); - assert node.layer == layer; + assert node.getLayer() == layer; } private void applyAddLinkAction(Link l) { @@ -962,18 +828,12 @@ private void applyAddLinkAction(Link l) { return; } - if (toNode.layer == fromNode.layer) { + if (toNode.getLayer() == fromNode.getLayer()) { handleNeighborNodesOnSameLayer(fromNode, toNode); } - LayoutEdge edge = new LayoutEdge(); - edge.link = l; - edge.from = fromNode; - edge.relativeFrom = l.getFrom().getRelativePosition().x; - edge.to = toNode; - edge.relativeTo = l.getTo().getRelativePosition().x; - - boolean reversedLink = fromNode.layer > toNode.layer; + LayoutEdge edge = new LayoutEdge(fromNode, toNode, l.getFrom().getRelativePosition().x, l.getTo().getRelativePosition().x, l); + boolean reversedLink = fromNode.getLayer() > toNode.getLayer(); if (reversedLink) { // Reversed link reversedLinks.add(l); @@ -982,23 +842,23 @@ private void applyAddLinkAction(Link l) { fromNode = toNode; toNode = temp; - int oldRelativeFrom = edge.relativeFrom; - int oldRelativeTo = edge.relativeTo; + int oldRelativeFrom = edge.getRelativeFromX(); + int oldRelativeTo = edge.getRelativeToX(); - edge.from = fromNode; - edge.to = toNode; - edge.relativeFrom = oldRelativeTo; - edge.relativeTo = oldRelativeFrom; + edge.setFrom(fromNode); + edge.setTo(toNode); + edge.setRelativeFromX(oldRelativeTo); + edge.setRelativeToX(oldRelativeFrom); } - fromNode.succs.add(edge); - toNode.preds.add(edge); + fromNode.addSuccessor(edge); + toNode.addPredecessor(edge); if (reversedLink) { updateReversedLinkPositions(l); } - if (fromNode.layer != toNode.layer - 1) { + if (fromNode.getLayer() != toNode.getLayer() - 1) { // Edge span multiple layers - must insert dummy nodes insertDummyNodes(edge); } @@ -1032,20 +892,20 @@ private int optimalLayer(Vertex vertex, List links) { LayoutNode fromNode = vertexToLayoutNode.get(link.getFrom().getVertex()); LayoutNode toNode = vertexToLayoutNode.get(link.getTo().getVertex()); if (link.getTo().getVertex().equals(vertex) && fromNode != null) { - if (fromNode.layer > i) { + if (fromNode.getLayer() > i) { curReversedEdges += 1; - } else if (fromNode.layer == i) { + } else if (fromNode.getLayer() == i) { curNeighborsOnSameLayer += 1; } - curTotalEdgeLength += Math.abs(fromNode.layer - i); + curTotalEdgeLength += Math.abs(fromNode.getLayer() - i); } if (link.getFrom().getVertex().equals(vertex) && toNode != null) { - if (toNode.layer < i) { + if (toNode.getLayer() < i) { curReversedEdges += 1; - } else if (toNode.layer == i) { + } else if (toNode.getLayer() == i) { curNeighborsOnSameLayer += 1; } - curTotalEdgeLength += Math.abs(i - toNode.layer); + curTotalEdgeLength += Math.abs(i - toNode.getLayer()); } } @@ -1066,12 +926,7 @@ private int optimalLayer(Vertex vertex, List links) { } private void applyAddVertexAction(VertexAction action) { - LayoutNode node = new LayoutNode(); - Dimension size = action.vertex.getSize(); - node.width = (int) size.getWidth(); - node.height = (int) size.getHeight(); - node.vertex = action.vertex; - + LayoutNode node = new LayoutNode(action.vertex); List links = new ArrayList<>(); for (LinkAction a : action.linkActions) { links.add(a.link); @@ -1081,26 +936,19 @@ private void applyAddVertexAction(VertexAction action) { // Temporarily add the links so that the node insertion accounts for edge // crossings for (Link l : links) { - LayoutEdge e = new LayoutEdge(); if (l.getTo().getVertex().equals(action.vertex) && nodes.contains(vertexToLayoutNode.get(l.getFrom().getVertex()))) { - e.to = node; - e.from = vertexToLayoutNode.get(l.getFrom().getVertex()); - e.relativeFrom = l.getFrom().getRelativePosition().x; - e.relativeTo = l.getTo().getRelativePosition().x; - node.preds.add(e); + LayoutEdge e = new LayoutEdge(vertexToLayoutNode.get(l.getFrom().getVertex()), node, l.getFrom().getRelativePosition().x, l.getTo().getRelativePosition().x, null); + node.addPredecessor(e); } else if (l.getFrom().getVertex().equals(action.vertex) && nodes.contains(vertexToLayoutNode.get(l.getTo().getVertex()))) { - e.from = node; - e.to = vertexToLayoutNode.get(l.getTo().getVertex()); - e.relativeFrom = l.getFrom().getRelativePosition().x; - e.relativeTo = l.getTo().getRelativePosition().x; - node.succs.add(e); + LayoutEdge e = new LayoutEdge(node, vertexToLayoutNode.get(l.getTo().getVertex()), l.getFrom().getRelativePosition().x, l.getTo().getRelativePosition().x, null); + node.addSuccessor(e); } } insertNode(node, layer); - node.succs.clear(); - node.preds.clear(); + node.clearSuccessors(); + node.clearPredecessors(); // Add associated edges for (LinkAction a : action.linkActions) { @@ -1116,44 +964,39 @@ private void applyRemoveLinkAction(Link l) { LayoutNode toNode = vertexToLayoutNode.get(to); LayoutNode fromNode = vertexToLayoutNode.get(from); - if (toNode.layer < fromNode.layer) { + if (toNode.getLayer() < fromNode.getLayer()) { // Reversed edge - LayoutNode temp = toNode; toNode = fromNode; - fromNode = temp; - reversedLinks.remove(l); - reversedLinkEndPoints.remove(l); - reversedLinkStartPoints.remove(l); } // Remove preds-edges bottom up, starting at "to" node // Cannot start from "from" node since there might be joint edges - List toNodePredsEdges = List.copyOf(toNode.preds); + List toNodePredsEdges = List.copyOf(toNode.getPredecessors()); for (LayoutEdge edge : toNodePredsEdges) { - LayoutNode n = edge.from; + LayoutNode n = edge.getFrom(); LayoutEdge edgeToRemove; - if (edge.link != null && edge.link.equals(l)) { - toNode.preds.remove(edge); + if (edge.getLink() != null && edge.getLink().equals(l)) { + toNode.removePredecessor(edge); edgeToRemove = edge; } else { // Wrong edge, look at next continue; } - if (n.vertex != null && n.vertex.equals(from)) { + if (n.getVertex() != null && n.getVertex().equals(from)) { // No dummy nodes inbetween 'from' and 'to' vertex - n.succs.remove(edgeToRemove); + n.removeSuccessor(edgeToRemove); break; } else { // Must remove edges between dummy nodes boolean found = true; LayoutNode prev = toNode; - while (n.vertex == null && found) { + while (n.getVertex() == null && found) { found = false; - if (n.succs.size() <= 1 && n.preds.size() <= 1) { + if (n.getSuccessors().size() <= 1 && n.getPredecessors().size() <= 1) { // Dummy node used only for this link, remove if not already removed if (nodes.contains(n)) { removeNode(n); @@ -1163,17 +1006,17 @@ private void applyRemoveLinkAction(Link l) { break; } - if (n.preds.size() == 1) { - n.succs.remove(edgeToRemove); + if (n.getPredecessors().size() == 1) { + n.removeSuccessor(edgeToRemove); prev = n; - edgeToRemove = n.preds.get(0); - n = edgeToRemove.from; + edgeToRemove = n.getPredecessors().get(0); + n = edgeToRemove.getFrom(); found = true; } } - n.succs.remove(edgeToRemove); - prev.preds.remove(edgeToRemove); + n.removeSuccessor(edgeToRemove); + prev.removePredecessor(edgeToRemove); } break; } @@ -1192,19 +1035,19 @@ private void removeNode(LayoutNode node) { if (!nodes.contains(node)) { return; } - int layer = node.layer; - int pos = node.pos; - List remainingLayerNodes = layers.get(layer); + int layer = node.getLayer(); + int pos = node.getPos(); + LayoutLayer remainingLayerNodes = layers.get(layer); assert remainingLayerNodes.contains(node); remainingLayerNodes.remove(node); // Update position of remaining nodes on the same layer boolean onlyDummiesLeft = true; for (LayoutNode n : remainingLayerNodes) { - if (n.pos > pos) { - n.pos -= 1; + if (n.getPos() > pos) { + n.setPos(n.getPos() - 1); } - if (n.vertex != null || n.preds.size() > 1) { + if (n.getVertex() != null || n.getPredecessors().size() > 1) { onlyDummiesLeft = false; } } @@ -1212,22 +1055,22 @@ private void removeNode(LayoutNode node) { if (onlyDummiesLeft && shouldRemoveEmptyLayers) { layers.remove(layer); for (int i = layer + 1; i <= layers.size(); i++) { - List list = layers.get(i); + LayoutLayer list = layers.get(i); layers.remove(i); layers.put(i - 1, list); for (LayoutNode n : list) { - n.layer -= 1; + n.setLayer(n.getLayer() - 1); } } for (LayoutNode n : remainingLayerNodes) { - if (n.preds.size() == 1) { - LayoutEdge predEdge = n.preds.get(0); - LayoutNode fromNode = predEdge.from; - fromNode.succs.remove(predEdge); - for (LayoutEdge e : n.succs) { - e.from = fromNode; - e.relativeFrom = predEdge.relativeFrom; - fromNode.succs.add(e); + if (n.getPredecessors().size() == 1) { + LayoutEdge predEdge = n.getPredecessors().get(0); + LayoutNode fromNode = predEdge.getFrom(); + fromNode.removeSuccessor(predEdge); + for (LayoutEdge e : n.getSuccessors()) { + e.setFrom(fromNode); + e.setRelativeFromX(predEdge.getRelativeFromX()); + fromNode.addSuccessor(e); } } nodes.remove(n); @@ -1286,12 +1129,12 @@ void run() { Set layoutedLinks = new HashSet<>(); Set layoutedNodes = new HashSet<>(); for (LayoutNode n : nodes) { - for (LayoutEdge e : n.preds) { - if (e.link != null) { - layoutedLinks.add(e.link); + for (LayoutEdge e : n.getPredecessors()) { + if (e.getLink() != null) { + layoutedLinks.add(e.getLink()); } } - if (n.vertex != null) { + if (n.getVertex() != null) { layoutedNodes.add(n); } } @@ -1314,135 +1157,151 @@ void run() { private class AssignYCoordinates { void run() { + int currentY = 0; + for (int i = 0; i < layers.size(); i++) { + LayoutLayer layer = layers.get(i); + layer.setTop(currentY); - // Reset all values before assigning y-coordinates - for (LayoutNode n : nodes) { - if (n.vertex != null) { - updateNodeWithReversedEdges(n); - } else { - n.height = DUMMY_HEIGHT; - } - n.y = 0; + // Calculate the maximum layer height and set it for the layer + int maxLayerHeight = layer.calculateMaxLayerHeight(); + layer.setHeight(maxLayerHeight); + + // Center nodes vertically within the layer + layer.centerNodesVertically(); + + // Update currentY to account for the padded bottom of this layer + currentY += layer.calculatePaddedHeight(); } + } + } - int curY = 0; + private class WriteResult { - for (int i = 0; i < layers.size(); i++) { - List layer = layers.get(i); - int maxHeight = 0; - int baseLine = 0; - int bottomBaseLine = 0; - for (LayoutNode n : layer) { - maxHeight = Math.max(maxHeight, n.height - n.yOffset - n.bottomYOffset); - baseLine = Math.max(baseLine, n.yOffset); - bottomBaseLine = Math.max(bottomBaseLine, n.bottomYOffset); - } + private HashMap> computeLinkPositions() { + HashMap> linkToSplitEndPoints = new HashMap<>(); + HashMap> linkPositions = new HashMap<>(); - int maxXOffset = 0; - for (LayoutNode n : layer) { - if (n.vertex == null) { - // Dummy node - n.y = curY; - n.height = maxHeight + baseLine + bottomBaseLine; + for (LayoutNode layoutNode : nodes) { + if (layoutNode.isDummy()) continue; + for (LayoutEdge predEdge : layoutNode.getPredecessors()) { + LayoutNode fromNode = predEdge.getFrom(); + LayoutNode toNode = predEdge.getTo(); + + ArrayList linkPoints = new ArrayList<>(); + // input edge stub + linkPoints.add(new Point(predEdge.getEndX(), predEdge.getEndY())); + linkPoints.add(new Point(predEdge.getEndX(), layers.get(toNode.getLayer()).getTop() - LAYER_OFFSET)); + + LayoutEdge curEdge = predEdge; + while (fromNode.isDummy() && fromNode.hasPredecessors()) { + linkPoints.add(new Point(fromNode.getCenterX(), layers.get(fromNode.getLayer()).getBottom() + LAYER_OFFSET)); + linkPoints.add(new Point(fromNode.getCenterX(), layers.get(fromNode.getLayer()).getTop() - LAYER_OFFSET)); + curEdge = fromNode.getPredecessors().get(0); + fromNode = curEdge.getFrom(); + } + linkPoints.add(new Point(curEdge.getStartX(), layers.get(fromNode.getLayer()).getBottom() + LAYER_OFFSET)); + // output edge stub + linkPoints.add(new Point(curEdge.getStartX(), curEdge.getStartY())); + + if (predEdge.isReversed()) { + for (Point relativeEnd : toNode.getReversedLinkEndPoints().get(predEdge.getLink())) { + Point endPoint = new Point(toNode.getLeft() + relativeEnd.x, toNode.getTop() + relativeEnd.y); + linkPoints.add(0, endPoint); + } + if (!fromNode.isDummy()) { + if (fromNode.getReversedLinkStartPoints().containsKey(predEdge.getLink())) { + for (Point relativeStart : fromNode.getReversedLinkStartPoints().get(predEdge.getLink())) { + Point startPoint = new Point(fromNode.getLeft() + relativeStart.x, fromNode.getTop() + relativeStart.y); + linkPoints.add(startPoint); + } + } + } } else { - n.y = curY + baseLine + (maxHeight - (n.height - n.yOffset - n.bottomYOffset)) / 2 - n.yOffset; + Collections.reverse(linkPoints); } - for (LayoutEdge e : n.succs) { - int curXOffset = Math.abs(n.x - e.to.x); - maxXOffset = Math.max(curXOffset, maxXOffset); + if (fromNode.isDummy()) { + if (predEdge.isReversed()) { + Collections.reverse(linkPoints); + } + linkToSplitEndPoints.put(predEdge.getLink(), linkPoints); + + } else { + linkPositions.put(predEdge.getLink(), linkPoints); } } - - curY += maxHeight + baseLine + bottomBaseLine; - curY += LAYER_OFFSET + ((int) (Math.sqrt(maxXOffset) * 1.5)); } - } - } - private class WriteResult { + for (LayoutNode layoutNode : nodes) { + if (layoutNode.isDummy()) continue; + for (LayoutEdge succEdge : layoutNode.getSuccessors()) { + if (succEdge.getLink() == null) continue; - private List edgePoints(LayoutEdge e) { - ArrayList points = new ArrayList<>(); + LayoutNode fromNode = succEdge.getFrom(); + LayoutNode toNode = succEdge.getTo(); - Point p = new Point(e.to.x + e.relativeTo, - e.to.y + e.to.yOffset + e.link.getTo().getRelativePosition().y); - points.add(p); - if (e.to.inOffsets.containsKey(e.relativeTo)) { - points.add(new Point(p.x, - p.y + e.to.inOffsets.get(e.relativeTo) + e.link.getTo().getRelativePosition().y)); - } + ArrayList linkPoints = new ArrayList<>(); + linkPoints.add(new Point(succEdge.getStartX(), fromNode.getBottom())); + linkPoints.add(new Point(succEdge.getStartX(), layers.get(fromNode.getLayer()).getBottom() + LAYER_OFFSET)); - LayoutNode cur = e.from; - LayoutEdge curEdge = e; - while (cur.vertex == null && !cur.preds.isEmpty()) { - if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 - && points.get(points.size() - 2).x == cur.x + cur.width / 2) { - // On the same vertical line, can remove previous point - points.remove(points.size() - 1); - } - // Top of the dummy node - points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height)); - if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 - && points.get(points.size() - 2).x == cur.x + cur.width / 2) { - points.remove(points.size() - 1); - } - // Bottom of the dummy node - points.add(new Point(cur.x + cur.width / 2, cur.y)); - assert cur.preds.size() == 1; - curEdge = cur.preds.get(0); - cur = curEdge.from; - } - - p = new Point(cur.x + curEdge.relativeFrom, cur.y + cur.height - cur.bottomYOffset - + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y)); - if (curEdge.from.outOffsets.containsKey(curEdge.relativeFrom)) { - points.add(new Point(p.x, p.y + curEdge.from.outOffsets.get(curEdge.relativeFrom) - + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y))); - } - points.add(p); + LayoutEdge curEdge = succEdge; + while (toNode.isDummy() && toNode.hasSuccessors()) { + linkPoints.add(new Point(toNode.getCenterX(), layers.get(toNode.getLayer()).getTop() - LAYER_OFFSET)); + linkPoints.add(new Point(toNode.getCenterX(), layers.get(toNode.getLayer()).getBottom() + LAYER_OFFSET)); + curEdge = toNode.getSuccessors().get(0); + toNode = curEdge.getTo(); + } + linkPoints.add(new Point(curEdge.getEndX(), layers.get(toNode.getLayer()).getTop() - LAYER_OFFSET)); + linkPoints.add(new Point(curEdge.getEndX(), toNode.getTop())); - Collections.reverse(points); + if (succEdge.isReversed()) { + Collections.reverse(linkPoints); - if (reversedLinks.contains(e.link)) { - Collections.reverse(points); + if (fromNode.getReversedLinkStartPoints().containsKey(succEdge.getLink())) { + for (Point relativeStart : fromNode.getReversedLinkStartPoints().get(succEdge.getLink())) { + Point startPoint = new Point(fromNode.getLeft() + relativeStart.x, fromNode.getTop() + relativeStart.y); + linkPoints.add(startPoint); + } + } - assert reversedLinkStartPoints.containsKey(e.link); - for (Point p1 : reversedLinkStartPoints.get(e.link)) { - points.add(new Point(p1.x + cur.x, p1.y + cur.y)); - } + if (!toNode.isDummy()) { + if (toNode.getReversedLinkEndPoints().containsKey(succEdge.getLink())) { + for (Point relativeEnd : toNode.getReversedLinkEndPoints().get(succEdge.getLink())) { + Point endPoint = new Point(toNode.getLeft() + relativeEnd.x, toNode.getTop() + relativeEnd.y); + linkPoints.add(0, endPoint); + } + } + } + } - assert reversedLinkEndPoints.containsKey(e.link); - for (Point p1 : reversedLinkEndPoints.get(e.link)) { - points.add(0, new Point(p1.x + e.to.x, p1.y + e.to.y)); + if (linkToSplitEndPoints.containsKey(succEdge.getLink())) { + if (succEdge.isReversed()) { + Collections.reverse(linkPoints); + } + linkPoints.add(null); + linkPoints.addAll(linkToSplitEndPoints.get(succEdge.getLink())); + if (succEdge.isReversed()) { + Collections.reverse(linkPoints); + } + } + linkPositions.put(succEdge.getLink(), linkPoints); } } - return points; + return linkPositions; } + void run() { HashMap vertexPositions = new HashMap<>(); - HashMap> linkPositions = new HashMap<>(); + HashMap> linkPositions = computeLinkPositions(); - for (LayoutNode n : nodes) { - if (n.vertex != null) { - assert !vertexPositions.containsKey(n.vertex); - vertexPositions.put(n.vertex, new Point(n.x + n.xOffset, n.y + n.yOffset)); - } else { - continue; - } - - // All edges can be drawn from bottom up, the links are stored in the preds list - // of each node - for (LayoutEdge e : n.preds) { - if (e.link != null && !linkPositions.containsKey(e.link)) { - List points = edgePoints(e); - assert !linkPositions.containsKey(e.link); - linkPositions.put(e.link, points); - } + for (LayoutNode n : nodes) { + if (n.getVertex() != null) { + assert !vertexPositions.containsKey(n.getVertex()); + vertexPositions.put(n.getVertex(), new Point(n.getLeft(), n.getTop())); } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/InterClusterConnection.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/InterClusterConnection.java index 0ff9a7dd04c..3789f604371 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/InterClusterConnection.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/InterClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -74,8 +74,4 @@ public List getControlPoints() { public String toString() { return "InterClusterConnection[from=" + getFrom() + ", to=" + getTo() + "]"; } - - public boolean isVIP() { - return false; - } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java index 081135a2abf..f45e6268e2c 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -26,19 +26,195 @@ import com.sun.hotspot.igv.layout.Link; +/** + * The LayoutEdge class represents a connection between two nodes (LayoutNode) in a hierarchical graph layout. + * It stores information about the starting node (from), the ending node (to), and the positions where + * the edge connects to these nodes. It also keeps track of whether the edge has been reversed, + * which is useful for handling edges that go against the main flow in hierarchical layouts (like loops or back edges). + * This class ensures that edges are drawn correctly between nodes, + * helping to create clear and understandable visualizations of the graph. + */ public class LayoutEdge { - public LayoutNode from; - public LayoutNode to; - // Horizontal distance relative to start of 'from'. - public int relativeFrom; - // Horizontal distance relative to start of 'to'. - public int relativeTo; - public Link link; - public boolean vip; + private LayoutNode from; + private LayoutNode to; + // Horizontal distance relative to start of 'from'. + private int relativeFromX; + // Horizontal distance relative to start of 'to'. + private int relativeToX; + private Link link; + private boolean isReversed; + + /** + * Constructs a LayoutEdge between two nodes with the specified link. + * The relative positions are set to zero by default. + * + * @param from The source LayoutNode. + * @param to The target LayoutNode. + * @param link The Link associated with this edge. + */ + public LayoutEdge(LayoutNode from, LayoutNode to, Link link) { + this.from = from; + this.to = to; + this.link = link; + this.isReversed = false; + } + + /** + * Constructs a LayoutEdge between two nodes with specified relative positions and link. + * + * @param from The source LayoutNode. + * @param to The target LayoutNode. + * @param relativeFromX The horizontal distance relative to the start of 'from' node. + * @param relativeToX The horizontal distance relative to the start of 'to' node. + * @param link The Link associated with this edge. + */ + public LayoutEdge(LayoutNode from, LayoutNode to, int relativeFromX, int relativeToX, Link link) { + this(from, to, link); + this.relativeFromX = relativeFromX; + this.relativeToX = relativeToX; + } + + /** + * Gets the absolute x-coordinate of the starting point of the edge. + * + * @return The x-coordinate of the edge's starting point. + */ + public int getStartX() { + return relativeFromX + from.getLeft(); + } + + /** + * Gets the absolute y-coordinate of the starting point of the edge. + * + * @return The y-coordinate of the edge's starting point. + */ + public int getStartY() { + return from.getBottom(); + } + + /** + * Gets the absolute x-coordinate of the ending point of the edge. + * + * @return The x-coordinate of the edge's ending point. + */ + public int getEndX() { + return relativeToX + to.getLeft(); + } + + /** + * Gets the absolute y-coordinate of the ending point of the edge. + * + * @return The y-coordinate of the edge's ending point. + */ + public int getEndY() { + return to.getTop(); + } + + /** + * Reverses the direction of the edge. + * Marks the edge as reversed, which is used to represent back edges in hierarchical layouts. + */ + public void reverse() { + isReversed = !isReversed; + } + + /** + * Checks if the edge is reversed. + * + * @return True if the edge is reversed; false otherwise. + */ + public boolean isReversed() { + return isReversed; + } + + @Override + public String toString() { + return "Edge " + from + ", " + to; + } + + /** + * Gets the source node of the edge. + * + * @return The source LayoutNode. + */ + public LayoutNode getFrom() { + return from; + } + + /** + * Sets the source node of the edge. + * + * @param from The LayoutNode to set as the source. + */ + public void setFrom(LayoutNode from) { + this.from = from; + } + + /** + * Gets the target node of the edge. + * + * @return The target LayoutNode. + */ + public LayoutNode getTo() { + return to; + } + + /** + * Sets the target node of the edge. + * + * @param to The LayoutNode to set as the target. + */ + public void setTo(LayoutNode to) { + this.to = to; + } + + /** + * Gets the relative horizontal position from the source node's left boundary to the edge's starting point. + * + * @return The relative x-coordinate from the source node. + */ + public int getRelativeFromX() { + return relativeFromX; + } + + /** + * Sets the relative horizontal position from the source node's left boundary to the edge's starting point. + * + * @param relativeFromX The relative x-coordinate to set. + */ + public void setRelativeFromX(int relativeFromX) { + this.relativeFromX = relativeFromX; + } + + /** + * Gets the relative horizontal position from the target node's left boundary to the edge's ending point. + * + * @return The relative x-coordinate to the target node. + */ + public int getRelativeToX() { + return relativeToX; + } + + /** + * Sets the relative horizontal position from the target node's left boundary to the edge's ending point. + * + * @param relativeToX The relative x-coordinate to set. + */ + public void setRelativeToX(int relativeToX) { + this.relativeToX = relativeToX; + } + + /** + * Gets the Link associated with this edge. + * + * @return The Link object. + */ + public Link getLink() { + return link; + } - @Override - public String toString() { - return "Edge " + from + ", " + to; - } + public void setLink(Link link) { + this.link = link; } +} diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java new file mode 100644 index 00000000000..bd72616c2e6 --- /dev/null +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java @@ -0,0 +1,433 @@ +/* + * 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. + * + */ +package com.sun.hotspot.igv.hierarchicallayout; + +import com.sun.hotspot.igv.layout.Link; +import com.sun.hotspot.igv.layout.Port; +import com.sun.hotspot.igv.layout.Vertex; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * The LayoutGraph class is responsible for organizing and arranging a graph's nodes and edges for visual display. + * It takes a collection of nodes (Vertex) and connections between them (Link) and structures them into layers, + * creating a hierarchical layout. The class handles complexities like edges that span multiple layers + * by inserting temporary "dummy" nodes to maintain a clear hierarchy. + * This organization helps ensure that when the graph is displayed, it is easy to understand and visually coherent, + * making the relationships between nodes clear and straightforward. + * + * @author Thomas Wuerthinger + */ +public class LayoutGraph { + + public static final Comparator LINK_COMPARATOR = + Comparator.comparing((Link l) -> l.getFrom().getVertex()) + .thenComparing(l -> l.getTo().getVertex()) + .thenComparingInt(l -> l.getFrom().getRelativePosition().x) + .thenComparingInt(l -> l.getTo().getRelativePosition().x); + + // Registered Graph Components: Links, Vertices, and Port Mappings + private final Set links; + private final SortedSet vertices; + private final LinkedHashMap> inputPorts; + + // Layout Management: LayoutNodes and LayoutLayers + private final LinkedHashMap layoutNodes; + private final List dummyNodes; + private final List layers; + private boolean showSelfEdges = false; + + /** + * Constructs a new LayoutGraph using the provided collection of links and additional vertices. + * Initializes the graph layout structure with the given links and includes any additional vertices. + * + * @param links The collection of links that represent the edges of the graph. + * @param additionalVertices The collection of additional vertices to be included in the graph. + */ + public LayoutGraph(Collection links, Collection additionalVertices) { + this.links = new HashSet<>(links); + vertices = new TreeSet<>(additionalVertices); + LinkedHashMap> portLinks = new LinkedHashMap<>(links.size()); + inputPorts = new LinkedHashMap<>(links.size()); + LinkedHashMap> outputPorts = new LinkedHashMap<>(links.size()); + + for (Link link : links) { + assert link.getFrom() != null; + assert link.getTo() != null; + Port fromPort = link.getFrom(); + Port toPort = link.getTo(); + Vertex fromVertex = fromPort.getVertex(); + Vertex toVertex = toPort.getVertex(); + + vertices.add(fromVertex); + vertices.add(toVertex); + + outputPorts.computeIfAbsent(fromVertex, k -> new HashSet<>()).add(fromPort); + inputPorts.computeIfAbsent(toVertex, k -> new HashSet<>()).add(toPort); + + portLinks.computeIfAbsent(fromPort, k -> new HashSet<>()).add(link); + portLinks.computeIfAbsent(toPort, k -> new HashSet<>()).add(link); + } + + // cleanup + layoutNodes = new LinkedHashMap<>(); + dummyNodes = new ArrayList<>(); + layers = new ArrayList<>(); + } + + public void clearLayout() { + layoutNodes.clear(); + dummyNodes.clear(); + layers.clear(); + } + + public boolean showSelfEdges() { + return showSelfEdges; + } + + public void setShowSelfEdges(boolean showSelfEdges) { + this.showSelfEdges = showSelfEdges; + } + + /** + * Initializes or resets the layout structures by clearing existing nodes, dummy nodes, and layers. + * It then sets up the layout nodes for each vertex and creates layout edges based on the sorted links. + */ + public void initializeLayout() { + // Reset layout structures + clearLayout(); + + // Set up layout nodes for each vertex + for (Vertex vertex : getVertices()) { + createLayoutNode(vertex); + } + + // Set up layout edges in a sorted order for reproducibility + List sortedLinks = new ArrayList<>(links); + sortedLinks.sort(LINK_COMPARATOR); + for (Link link : sortedLinks) { + createLayoutEdge(link); + } + } + + /** + * Initializes the layers of the graph with the specified number of empty layers. + * + * @param layerCount The number of layers to initialize. + */ + public void initLayers(int layerCount) { + layers.clear(); + for (int i = 0; i < layerCount; i++) { + layers.add(new LayoutLayer()); + } + } + + /** + * Retrieves an unmodifiable list of dummy nodes in the graph. + * + * @return An unmodifiable list containing all dummy nodes in the graph. + */ + public List getDummyNodes() { + return Collections.unmodifiableList(dummyNodes); + } + + /** + * Retrieves a collection of all layout nodes in the graph. + * + * @return A collection containing all LayoutNodes. + */ + public Collection getLayoutNodes() { + return Collections.unmodifiableCollection(layoutNodes.values()); + } + + /** + * Retrieves a combined list of all nodes in the graph, + * including both layout nodes and dummy nodes. + * + * @return An unmodifiable list containing all nodes in the graph. + */ + public List getAllNodes() { + List allNodes = new ArrayList<>(); + allNodes.addAll(layoutNodes.values()); + allNodes.addAll(dummyNodes); + return Collections.unmodifiableList(allNodes); + } + + /** + * Retrieves an unmodifiable list of all layers in the graph. + * + * @return An unmodifiable list containing all layers. + */ + public List getLayers() { + return Collections.unmodifiableList(layers); + } + + /** + * Returns the total number of layers in the graph. + * + * @return The number of layers. + */ + public int getLayerCount() { + return layers.size(); + } + + /** + * Adds a LayoutNode to the specified layer and registers it in the graph. + * + * @param node The LayoutNode to add to the layer. + * @param layerNumber The index of the layer to which the node will be added. + */ + public void addDummyToLayer(LayoutNode node, int layerNumber) { + assert node.isDummy(); + node.setLayer(layerNumber); + getLayer(layerNumber).add(node); + dummyNodes.add(node); + } + + /** + * Updates the positions of all nodes in each layer. + * Should be called after changes to node positions or layer compositions. + */ + public void updatePositions() { + for (LayoutLayer layer : layers) { + layer.updateNodeIndices(); + } + } + + // Create and register LayoutNode + public LayoutNode createLayoutNode(Vertex vertex) { + if (!vertices.contains(vertex)) { + throw new IllegalArgumentException("Vertex does not exist in the graph: " + vertex); + } + LayoutNode node = new LayoutNode(vertex); + layoutNodes.put(vertex, node); + return node; + } + + /** + * Creates a LayoutEdge based on the given Link and connects it to the corresponding LayoutNodes. + * + * @param link The Link representing the edge in the graph. + * @return The newly created LayoutEdge. + */ + public LayoutEdge createLayoutEdge(Link link) { + LayoutEdge edge = new LayoutEdge( + layoutNodes.get(link.getFrom().getVertex()), + layoutNodes.get(link.getTo().getVertex()), + link.getFrom().getRelativePosition().x, + link.getTo().getRelativePosition().x, + link); + edge.getFrom().addSuccessor(edge); + edge.getTo().addPredecessor(edge); + return edge; + } + + /** + * Retrieves the set of all links (edges) in the graph. + * + * @return A set containing all links in the graph. + */ + public Set getLinks() { + return links; + } + + /** + * Retrieves the set of all vertices in the graph, sorted in natural order. + * + * @return A sorted set of all vertices in the graph. + */ + public SortedSet getVertices() { + return vertices; + } + + /** + * Checks whether the graph contains the specified vertex. + * + * @param vertex The vertex to check for presence in the graph. + * @return True if the vertex is present, false otherwise. + */ + public boolean containsVertex(Vertex vertex) { + return vertices.contains(vertex); + } + + /** + * Finds all root vertices in the graph (vertices with no incoming links). + * + * @return A set of root vertices. + */ + public Set findRootVertices() { + return vertices.stream() + .filter(v -> inputPorts.getOrDefault(v, Collections.emptySet()).isEmpty()) + .collect(Collectors.toSet()); + } + + /** + * Retrieves the LayoutLayer at the specified index. + * + * @param layerNr The index of the layer to retrieve. + * @return The LayoutLayer at the specified index. + */ + public LayoutLayer getLayer(int layerNr) { + return layers.get(layerNr); + } + + /** + * Positions the layers vertically, calculating their heights and setting their positions. + * Centers the nodes within each layer vertically. + */ + public void positionLayers() { + int currentY = 0; + for (LayoutLayer layer : getLayers()) { + layer.setTop(currentY); + + // Calculate the maximum layer height and set it for the layer + int maxLayerHeight = layer.calculateMaxLayerHeight(); + layer.setHeight(maxLayerHeight); + + // Center nodes vertically within the layer + layer.centerNodesVertically(); + + // Update currentY to account for the padded bottom of this layer + currentY += layer.calculatePaddedHeight(); + } + } + + /** + * Inserts dummy nodes along the edges to successors of the specified node, + * for edges that span more than one layer. + * Can limit the maximum length of layers an edge spans using maxLayerLength. + * + * @param layoutNode The node for which to create successor dummy nodes. + * @param maxLayerLength The maximum number of layers an edge can span without splitting it + */ + public void createDummiesForNodeSuccessor(LayoutNode layoutNode, int maxLayerLength) { + LinkedHashMap> portsToUnprocessedEdges = new LinkedHashMap<>(); + ArrayList succs = new ArrayList<>(layoutNode.getSuccessors()); + LinkedHashMap portToTopNode = new LinkedHashMap<>(); + LinkedHashMap> portToBottomNodeMapping = new LinkedHashMap<>(); + for (LayoutEdge succEdge : succs) { + int startPort = succEdge.getRelativeFromX(); + LayoutNode fromNode = succEdge.getFrom(); + LayoutNode toNode = succEdge.getTo(); + + // edge is longer than one layer => needs dummy nodes + if (fromNode.getLayer() != toNode.getLayer() - 1) { + // the edge needs to be cut + if (maxLayerLength != -1 && toNode.getLayer() - fromNode.getLayer() > maxLayerLength) { + // remove the succEdge before replacing it + toNode.removePredecessor(succEdge); + fromNode.removeSuccessor(succEdge); + + LayoutNode topCutNode = portToTopNode.get(startPort); + if (topCutNode == null) { + topCutNode = new LayoutNode(); + topCutNode.setLayer(fromNode.getLayer() + 1); + addDummyToLayer(topCutNode, topCutNode.getLayer()); + portToTopNode.put(startPort, topCutNode); + portToBottomNodeMapping.put(startPort, new LinkedHashMap<>()); + } + LayoutEdge edgeToTopCut = new LayoutEdge(fromNode, topCutNode, succEdge.getRelativeFromX(), topCutNode.getWidth() / 2, succEdge.getLink()); + if (succEdge.isReversed()) edgeToTopCut.reverse(); + fromNode.addSuccessor(edgeToTopCut); + topCutNode.addPredecessor(edgeToTopCut); + + LinkedHashMap layerToBottomNode = portToBottomNodeMapping.get(startPort); + LayoutNode bottomCutNode = layerToBottomNode.get(toNode.getLayer()); + if (bottomCutNode == null) { + bottomCutNode = new LayoutNode(); + bottomCutNode.setLayer(toNode.getLayer() - 1); + addDummyToLayer(bottomCutNode, bottomCutNode.getLayer()); + layerToBottomNode.put(toNode.getLayer(), bottomCutNode); + } + LayoutEdge bottomEdge = new LayoutEdge(bottomCutNode, toNode, bottomCutNode.getWidth() / 2, succEdge.getRelativeToX(), succEdge.getLink()); + if (succEdge.isReversed()) bottomEdge.reverse(); + toNode.addPredecessor(bottomEdge); + bottomCutNode.addSuccessor(bottomEdge); + + } else { // the edge is not cut, but needs dummy nodes + portsToUnprocessedEdges.putIfAbsent(startPort, new ArrayList<>()); + portsToUnprocessedEdges.get(startPort).add(succEdge); + } + } + } + + for (Map.Entry> portToUnprocessedEdges : portsToUnprocessedEdges.entrySet()) { + Integer startPort = portToUnprocessedEdges.getKey(); + List unprocessedEdges = portToUnprocessedEdges.getValue(); + unprocessedEdges.sort(Comparator.comparingInt(e -> e.getTo().getLayer())); + + if (unprocessedEdges.size() == 1) { + // process a single edge + LayoutEdge singleEdge = unprocessedEdges.get(0); + LayoutNode fromNode = singleEdge.getFrom(); + if (singleEdge.getTo().getLayer() > fromNode.getLayer() + 1) { + LayoutEdge previousEdge = singleEdge; + for (int i = fromNode.getLayer() + 1; i < previousEdge.getTo().getLayer(); i++) { + LayoutNode dummyNode = new LayoutNode(); + dummyNode.setLayer(i); + dummyNode.addPredecessor(previousEdge); + addDummyToLayer(dummyNode, dummyNode.getLayer()); + LayoutEdge dummyEdge = new LayoutEdge(dummyNode, previousEdge.getTo(), dummyNode.getWidth() / 2, previousEdge.getRelativeToX(), singleEdge.getLink()); + if (previousEdge.isReversed()) dummyEdge.reverse(); + dummyNode.addSuccessor(dummyEdge); + previousEdge.setRelativeToX(dummyNode.getWidth() / 2); + previousEdge.getTo().removePredecessor(previousEdge); + previousEdge.getTo().addPredecessor(dummyEdge); + previousEdge.setTo(dummyNode); + previousEdge = dummyEdge; + } + } + } else { + int lastLayer = unprocessedEdges.get(unprocessedEdges.size() - 1).getTo().getLayer(); + int dummyCnt = lastLayer - layoutNode.getLayer() - 1; + LayoutEdge[] newDummyEdges = new LayoutEdge[dummyCnt]; + LayoutNode[] newDummyNodes = new LayoutNode[dummyCnt]; + + newDummyNodes[0] = new LayoutNode(); + newDummyNodes[0].setLayer(layoutNode.getLayer() + 1); + newDummyEdges[0] = new LayoutEdge(layoutNode, newDummyNodes[0], startPort, newDummyNodes[0].getWidth() / 2, null); + newDummyNodes[0].addPredecessor(newDummyEdges[0]); + layoutNode.addSuccessor(newDummyEdges[0]); + for (int j = 1; j < dummyCnt; j++) { + newDummyNodes[j] = new LayoutNode(); + newDummyNodes[j].setLayer(layoutNode.getLayer() + j + 1); + newDummyEdges[j] = new LayoutEdge(newDummyNodes[j - 1], newDummyNodes[j], null); + newDummyNodes[j].addPredecessor(newDummyEdges[j]); + newDummyNodes[j - 1].addSuccessor(newDummyEdges[j]); + } + for (LayoutEdge unprocessedEdge : unprocessedEdges) { + LayoutNode anchorNode = newDummyNodes[unprocessedEdge.getTo().getLayer() - layoutNode.getLayer() - 2]; + anchorNode.addSuccessor(unprocessedEdge); + unprocessedEdge.setFrom(anchorNode); + unprocessedEdge.setRelativeFromX(anchorNode.getWidth() / 2); + layoutNode.removeSuccessor(unprocessedEdge); + } + for (LayoutNode dummyNode : newDummyNodes) { + addDummyToLayer(dummyNode, dummyNode.getLayer()); + } + } + } + } +} diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java new file mode 100644 index 00000000000..2f87be1587d --- /dev/null +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java @@ -0,0 +1,206 @@ +/* + * 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. + * + */ +package com.sun.hotspot.igv.hierarchicallayout; + +import static com.sun.hotspot.igv.hierarchicallayout.LayoutManager.*; +import static com.sun.hotspot.igv.hierarchicallayout.LayoutNode.NODE_X_COMPARATOR; +import java.util.ArrayList; +import java.util.Collection; + +/** + * The LayoutLayer class represents a single horizontal layer in a hierarchical graph layout. + * It holds a list of nodes (LayoutNode) that are all on the same vertical level. + * This class provides simple methods to manage these nodes: you can add nodes to the layer, + * calculate the maximum height needed to fit all nodes, center the nodes vertically within the layer, and set + * their horizontal positions with proper spacing. In essence, LayoutLayer helps organize nodes neatly in a graph, + * making it easier to display the graph clearly and understand the relationships between nodes. + */ +public class LayoutLayer extends ArrayList { + + private int height = 0; + private int y = 0; + + /** + * Adds all LayoutNodes from the specified collection to this layer. + * Updates the layer's height based on the nodes added. + * + * @param c The collection of LayoutNodes to be added. + * @return true if this layer changed as a result of the call. + */ + @Override + public boolean addAll(Collection c) { + c.forEach(this::updateLayerHeight); + return super.addAll(c); + } + + /** + * Adds a single LayoutNode to this layer. + * Updates the layer's height based on the node added. + * + * @param n The LayoutNode to be added. + * @return true if the node was added successfully. + */ + @Override + public boolean add(LayoutNode n) { + updateLayerHeight(n); + return super.add(n); + } + + /** + * Updates the layer's height if the outer height of the given node exceeds the current height. + * + * @param n The LayoutNode whose height is to be considered. + */ + private void updateLayerHeight(LayoutNode n) { + height = Math.max(height, n.getOuterHeight()); + } + + /** + * Calculates and returns the maximum height among the nodes in this layer, including their margins. + * Adjusts the top and bottom margins of non-dummy nodes to be equal, effectively centering them vertically. + * + * @return The maximum outer height of nodes in this layer. + */ + public int calculateMaxLayerHeight() { + int maxLayerHeight = 0; + for (LayoutNode layoutNode : this) { + if (!layoutNode.isDummy()) { + // Center the node by setting equal top and bottom margins + layoutNode.centerNode(); + } + maxLayerHeight = Math.max(maxLayerHeight, layoutNode.getOuterHeight()); + } + return maxLayerHeight; + } + + /** + * Calculates and returns the total height of this layer, including additional padding + * based on the maximum horizontal offset among the edges of its nodes. + * This padding helps in scaling the layer vertically to accommodate edge bends and crossings. + * + * @return The total padded height of the layer. + */ + public int calculatePaddedHeight() { + int maxXOffset = 0; + + for (LayoutNode layoutNode : this) { + for (LayoutEdge succEdge : layoutNode.getSuccessors()) { + maxXOffset = Math.max(Math.abs(succEdge.getStartX() - succEdge.getEndX()), maxXOffset); + } + } + + int scalePaddedBottom = this.getHeight(); + scalePaddedBottom += (int) (SCALE_LAYER_PADDING * Math.max((int) (Math.sqrt(maxXOffset) * 2), LAYER_OFFSET * 3)); + return scalePaddedBottom; + } + + /** + * Centers all nodes in this layer vertically within the layer's assigned space. + * Adjusts each node's Y-coordinate so that it is centered based on the layer's top and height. + */ + public void centerNodesVertically() { + for (LayoutNode layoutNode : this) { + int centeredY = getTop() + (getHeight() - layoutNode.getOuterHeight()) / 2; + layoutNode.setY(centeredY); + } + } + + /** + * Shifts the top Y-coordinate of this layer by the specified amount. + * Useful for moving the entire layer up or down. + * + * @param shift The amount to shift the layer's top position. Positive values move it down. + */ + public void moveLayerVertically(int shift) { + y += shift; + } + + /** + * Gets the top Y-coordinate of this layer. + * + * @return The Y-coordinate representing the top of the layer. + */ + public int getTop() { + return y; + } + + /** + * Sets the top Y-coordinate of this layer. + * + * @param top The Y-coordinate representing the top of the layer. + */ + public void setTop(int top) { + y = top; + } + + /** + * Gets the bottom Y-coordinate of this layer. + * + * @return The Y-coordinate representing the bottom of the layer. + */ + public int getBottom() { + return y + height; + } + + /** + * Gets the height of this layer. + * + * @return The height of the layer. + */ + public int getHeight() { + return height; + } + + /** + * Sets the height of this layer. + * + * @param height The height to set for the layer. + */ + public void setHeight(int height) { + this.height = height; + } + + /** + * Initializes nodes' X positions with spacing. + */ + public void initXPositions() { + int curX = 0; + for (LayoutNode node : this) { + node.setX(curX); + curX += node.getOuterWidth() + NODE_OFFSET; + } + } + + /** + * Updates the position indices of the nodes in this layer based on their order in the list. + * Useful after nodes have been added or removed to ensure position indices are consistent. + */ + public void updateNodeIndices() { + int pos = 0; + for (LayoutNode layoutNode : this) { + layoutNode.setPos(pos); + pos++; + } + } +} diff --git a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutManager.java similarity index 63% rename from src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutManager.java rename to src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutManager.java index a74ef280ebe..a7ad1a3a6f1 100644 --- a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -21,19 +21,24 @@ * questions. * */ -package com.sun.hotspot.igv.layout; +package com.sun.hotspot.igv.hierarchicallayout; -import java.util.Set; +import java.awt.Font; /** * * @author Thomas Wuerthinger */ -public interface LayoutManager { +public abstract class LayoutManager { - void setCutEdges(boolean enable); + public static final Font TITLE_FONT = new Font("Arial", Font.BOLD, 14); + public static final int SWEEP_ITERATIONS = 1; + public static final int CROSSING_ITERATIONS = 1; + public static final int NODE_OFFSET = 8; + public static final int LAYER_OFFSET = 8; + public static final double SCALE_LAYER_PADDING = 1.5; - void doLayout(LayoutGraph graph); + public abstract void setCutEdges(boolean enable); - void doLayout(LayoutGraph graph, Set importantLinks); + public abstract void doLayout(LayoutGraph graph); } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java index 848ada1b77d..0997de46430 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -23,31 +23,518 @@ */ package com.sun.hotspot.igv.hierarchicallayout; +import static com.sun.hotspot.igv.hierarchicallayout.LayoutManager.NODE_OFFSET; +import com.sun.hotspot.igv.layout.Link; import com.sun.hotspot.igv.layout.Vertex; +import java.awt.Dimension; +import java.awt.Point; import java.util.*; +/** + * The LayoutNode class represents a node in a hierarchical graph layout. + * It can be either an actual node from the original graph or a temporary "dummy" node added during the layout process + * to handle complex edge connections. + * This class stores important layout information like the node's position (x and y coordinates), + * size (width and height), layer level, and connections to other nodes through incoming and outgoing edges. + * It provides methods to calculate optimal positions, manage margins, and handle reversed edges, + * all aimed at arranging the nodes neatly in layers to create a clear and visually organized graph display. + */ public class LayoutNode { - public int x; - public int y; - public int width; - public int height; - public int layer = -1; - public int xOffset; - public int yOffset; - public int bottomYOffset; - public Vertex vertex; // Only used for non-dummy nodes, otherwise null + // Comparator constants for sorting LayoutNodes in various ways + public static final Comparator LAYOUT_NODE_DEGREE_COMPARATOR = Comparator.comparingInt(LayoutNode::getDegree); + public static final Comparator NODE_POS_COMPARATOR = Comparator.comparingInt(LayoutNode::getPos); + public static final Comparator NODE_X_COMPARATOR = Comparator.comparingInt(LayoutNode::getX); + public static final Comparator NODE_CROSSING_COMPARATOR = Comparator.comparingInt(LayoutNode::getCrossingNumber); + + + // Default dimensions for dummy nodes + public static final int DUMMY_HEIGHT = 1; + public static final int DUMMY_WIDTH = 1; + public static final int REVERSE_EDGE_OFFSET = NODE_OFFSET + LayoutNode.DUMMY_WIDTH; + private Vertex vertex; // Associated graph vertex; null for dummy nodes + private final List preds = new ArrayList<>(); // Incoming edges + private final List succs = new ArrayList<>(); // Outgoing edges + private LayoutEdge selfEdge = null; + private final HashMap> reversedLinkStartPoints = new HashMap<>(); // Start points of reversed edges + private final HashMap> reversedLinkEndPoints = new HashMap<>(); // End points of reversed edges + // Layout properties + private int layer = -1; + private int x; + private int y; + private int width; + private int height; + private int topMargin; + private int bottomMargin; + private int rightMargin; + private int leftMargin; + private int pos = -1; // Position within its layer + private int crossingNumber = 0; + + public boolean hasSelfEdge() { + return selfEdge != null; + } + + public void setSelfEdge(LayoutEdge selfEdge) { + this.selfEdge = selfEdge; + if (selfEdge != null) { + topMargin += REVERSE_EDGE_OFFSET; + bottomMargin += REVERSE_EDGE_OFFSET; + rightMargin += REVERSE_EDGE_OFFSET; + } + } + + public LayoutEdge getSelfEdge() { + return selfEdge; + } + + /** + * Constructs a LayoutNode associated with the given Vertex. + * Initializes the node's size based on the vertex's dimensions. + * + * @param v The Vertex associated with this LayoutNode. If null, the node is a dummy node. + */ + public LayoutNode(Vertex v) { + vertex = v; + initSize(); + } + + /** + * Constructs a dummy LayoutNode + */ + public LayoutNode() { + this(null); + } + + /** + * Initializes the size and margins of the node. + * If the node represents a real vertex, it uses the vertex's size. + * Dummy nodes use default dimensions. + */ + public void initSize() { + if (vertex == null) { + height = DUMMY_HEIGHT; + width = DUMMY_WIDTH; + } else { + Dimension size = vertex.getSize(); + height = size.height; + width = size.width; + } + topMargin = 0; + bottomMargin = 0; + leftMargin = 0; + rightMargin = 0; + if (hasSelfEdge()) { + topMargin += REVERSE_EDGE_OFFSET; + bottomMargin += REVERSE_EDGE_OFFSET; + rightMargin += REVERSE_EDGE_OFFSET; + } + } + + public int getCrossingNumber() { + return crossingNumber; + } + + public void setCrossingNumber(int crossingNumber) { + this.crossingNumber = crossingNumber; + } + + public int calculateOptimalXFromPredecessors(boolean useMedian) { + int numPreds = preds.size(); + + // If there are no predecessors, retain the current x position + if (numPreds == 0) { + return getX(); + } + + // Collect the x positions from all predecessor edges + List positions = new ArrayList<>(numPreds); + for (LayoutEdge edge : preds) { + positions.add(edge.getStartX() - edge.getRelativeToX()); + } + + if (useMedian) { + // Calculate the median position + Collections.sort(positions); + int midIndex = numPreds / 2; + + if (numPreds % 2 == 0) { + // Even number of predecessors: average the two middle values + return (positions.get(midIndex - 1) + positions.get(midIndex)) / 2; + } else { + // Odd number of predecessors: take the middle value + return positions.get(midIndex); + } + } else { + // Calculate the average position + long sum = 0; + for (int pos : positions) { + sum += pos; + } + // Integer division is used; adjust as needed for rounding + return (int) (sum / numPreds); + } + } + + + public int calculateOptimalXFromSuccessors(boolean useMedian) { + int numSuccs = succs.size(); + + // If there are no successors, retain the current x position + if (numSuccs == 0) { + return getX(); + } + + // Collect the x positions from all successor edges + List positions = new ArrayList<>(numSuccs); + for (LayoutEdge edge : succs) { + positions.add(edge.getEndX() - edge.getRelativeFromX()); + } + + if (useMedian) { + // Calculate the median position + Collections.sort(positions); + int midIndex = numSuccs / 2; + + if (numSuccs % 2 == 0) { + // Even number of successors: average the two middle values + return (positions.get(midIndex - 1) + positions.get(midIndex)) / 2; + } else { + // Odd number of successors: take the middle value + return positions.get(midIndex); + } + } else { + // Calculate the average position + long sum = 0; + for (int pos : positions) { + sum += pos; + } + // Integer division is used; adjust as needed for rounding + return (int) (sum / numSuccs); + } + } + + /** + * Calculates the node's out-degree (number of outgoing edges). + * + * @return The out-degree of the node. + */ + public int getOutDegree() { + return succs.size(); + } + + /** + * Calculates the node's in-degree (number of incoming edges). + * + * @return The in-degree of the node. + */ + public int getInDegree() { + return preds.size(); + } - public List preds = new ArrayList<>(); - public List succs = new ArrayList<>(); - public HashMap outOffsets = new HashMap<>(); - public HashMap inOffsets = new HashMap<>(); - public int pos = -1; // Position within layer + /** + * Calculates the total degree of the node (sum of in-degree and out-degree). + * + * @return The total degree of the node. + */ + public int getDegree() { + return preds.size() + succs.size(); + } - public int crossingNumber; + /** + * Gets the left boundary (excluding left margin) of the node. + * + * @return The x-coordinate of the left boundary. + */ + public int getLeft() { + return x + leftMargin; + } + + /** + * Gets the total width of the node, including left and right margins. + * + * @return The total outer width. + */ + public int getOuterWidth() { + return leftMargin + width + rightMargin; + } + + /** + * Gets the total height of the node, including top and bottom margins. + * + * @return The total outer height. + */ + public int getOuterHeight() { + return topMargin + height + bottomMargin; + } + + public int getHeight() { + return height; + } - @Override - public String toString() { - return "Node " + vertex; + /** + * Gets the outer right boundary (including right margin) of the node. + * + * @return The x-coordinate of the outer right boundary. + */ + public int getOuterRight() { + return x + leftMargin + width + rightMargin; + } + + /** + * Gets the horizontal center point of the node. + * + * @return The x-coordinate of the center. + */ + public int getCenterX() { + return x + leftMargin + (width / 2); + } + + /** + * Gets the top boundary (excluding top margin) of the node. + * + * @return The y-coordinate of the top boundary. + */ + public int getTop() { + return y + topMargin; + } + + /** + * Gets the bottom boundary (excluding bottom margin) of the node. + * + * @return The y-coordinate of the bottom boundary. + */ + public int getBottom() { + return y + topMargin + height; + } + + /** + * Checks if the node is a dummy node. + * + * @return True if the node is a dummy node; false otherwise. + */ + public boolean isDummy() { + return vertex == null; + } + + @Override + public String toString() { + if (vertex != null) { + return vertex.toString(); + } else { + return "dummy"; } } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getWidth() { + return width; + } + + public int getLayer() { + return layer; + } + + public void setLayer(int layer) { + this.layer = layer; + } + + /** + * Centers the node by setting equal top and bottom margins. + * The larger of the two margins is applied to both. + */ + public void centerNode() { + int offset = Math.max(topMargin, bottomMargin); + topMargin = offset; + bottomMargin = offset; + } + + public Vertex getVertex() { + return vertex; + } + + public void setVertex(Vertex vertex) { + this.vertex = vertex; + } + + public boolean hasPredecessors() { + return !preds.isEmpty(); + } + + public boolean hasSuccessors() { + return !succs.isEmpty(); + } + + public void clearSuccessors() { + succs.clear(); + } + + public void clearPredecessors() { + preds.clear(); + } + + public List getSuccessors() { + return Collections.unmodifiableList(succs); + } + + public List getSuccessorsRaw() { + return succs; + } + + public List getPredecessors() { + return Collections.unmodifiableList(preds); + } + + public List getPredecessorsRaw() { + return preds; + } + + public void addSuccessor(LayoutEdge successor) { + succs.add(successor); + } + + public void removeSuccessor(LayoutEdge successor) { + succs.remove(successor); + } + + public void addPredecessor(LayoutEdge predecessor) { + preds.add(predecessor); + } + + public void removePredecessor(LayoutEdge predecessor) { + preds.remove(predecessor); + } + + public Map> getReversedLinkStartPoints() { + return Collections.unmodifiableMap(reversedLinkStartPoints); + } + + public Map> getReversedLinkEndPoints() { + return Collections.unmodifiableMap(reversedLinkEndPoints); + } + + public int getPos() { + return pos; + } + + public void setPos(int pos) { + this.pos = pos; + } + + private boolean computeReversedStartPoints(boolean left) { + TreeMap> sortedDownMap = left ? new TreeMap<>() : new TreeMap<>(Collections.reverseOrder()); + for (LayoutEdge succEdge : succs) { + if (succEdge.isReversed()) { + succEdge.setRelativeFromX(succEdge.getLink().getTo().getRelativePosition().x); + sortedDownMap.putIfAbsent(succEdge.getRelativeFromX(), new ArrayList<>()); + sortedDownMap.get(succEdge.getRelativeFromX()).add(succEdge); + } + } + + int offset = REVERSE_EDGE_OFFSET; + int offsetX = left ? -offset : offset; + int currentX = left ? 0 : width; + int startY = 0; + int currentY = 0; + for (Map.Entry> entry : sortedDownMap.entrySet()) { + int startX = entry.getKey(); + ArrayList reversedSuccs = entry.getValue(); + + currentX += offsetX; + currentY -= offset; + topMargin += offset; + + ArrayList startPoints = new ArrayList<>(); + startPoints.add(new Point(currentX, currentY)); + startPoints.add(new Point(startX, currentY)); + startPoints.add(new Point(startX, startY)); + for (LayoutEdge revEdge : reversedSuccs) { + revEdge.setRelativeFromX(currentX); + reversedLinkStartPoints.put(revEdge.getLink(), startPoints); + } + } + if (left) { + leftMargin += sortedDownMap.size() * offset; + } else { + rightMargin += sortedDownMap.size() * offset; + } + return !sortedDownMap.isEmpty(); + } + + private boolean computeReversedEndPoints(boolean left) { + TreeMap> sortedUpMap = left ? new TreeMap<>() : new TreeMap<>(Collections.reverseOrder()); + for (LayoutEdge predEdge : preds) { + if (predEdge.isReversed()) { + predEdge.setRelativeToX(predEdge.getLink().getFrom().getRelativePosition().x); + sortedUpMap.putIfAbsent(predEdge.getRelativeToX(), new ArrayList<>()); + sortedUpMap.get(predEdge.getRelativeToX()).add(predEdge); + } + } + + int offset = REVERSE_EDGE_OFFSET; + int offsetX = left ? -offset : offset; + int currentX = left ? 0 : getWidth(); + int startY = height; + int currentY = height; + for (Map.Entry> entry : sortedUpMap.entrySet()) { + int startX = entry.getKey(); + ArrayList reversedPreds = entry.getValue(); + + currentX += offsetX; + currentY += offset; + bottomMargin += offset; + + ArrayList endPoints = new ArrayList<>(); + endPoints.add(new Point(currentX, currentY)); + endPoints.add(new Point(startX, currentY)); + endPoints.add(new Point(startX, startY)); + for (LayoutEdge revEdge : reversedPreds) { + revEdge.setRelativeToX(currentX); + reversedLinkEndPoints.put(revEdge.getLink(), endPoints); + } + } + if (left) { + leftMargin += sortedUpMap.size() * offset; + } else { + rightMargin += sortedUpMap.size() * offset; + } + + return !sortedUpMap.isEmpty(); + } + + public void computeReversedLinkPoints(boolean reverseLeft) { + initSize(); + reversedLinkStartPoints.clear(); + reversedLinkEndPoints.clear(); + + boolean hasReversedDown = computeReversedStartPoints(reverseLeft); + computeReversedEndPoints(hasReversedDown != reverseLeft); + } + + public ArrayList getSelfEdgePoints() { + ArrayList points = new ArrayList<>(); + + Link selfEdgeLink = getSelfEdge().getLink(); + + points.add(new Point(selfEdgeLink.getFrom().getRelativePosition().x, selfEdgeLink.getFrom().getRelativePosition().y-REVERSE_EDGE_OFFSET)); + points.add(new Point(width + REVERSE_EDGE_OFFSET, selfEdgeLink.getFrom().getRelativePosition().y-REVERSE_EDGE_OFFSET)); + points.add(new Point(width + REVERSE_EDGE_OFFSET, height + REVERSE_EDGE_OFFSET)); + points.add(new Point(selfEdgeLink.getTo().getRelativePosition().x, height + REVERSE_EDGE_OFFSET)); + return points; + } +} diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LinearLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LinearLayoutManager.java deleted file mode 100644 index 69d53ebc9fa..00000000000 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LinearLayoutManager.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2022, 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. - * - */ -package com.sun.hotspot.igv.hierarchicallayout; - -import com.sun.hotspot.igv.layout.LayoutGraph; -import com.sun.hotspot.igv.layout.LayoutManager; -import com.sun.hotspot.igv.layout.Link; -import com.sun.hotspot.igv.layout.Vertex; -import java.awt.Point; -import java.util.*; - -public class LinearLayoutManager implements LayoutManager { - - // Ranking determining the vertical node ordering. - private final Map vertexRank; - - public LinearLayoutManager(Map vertexRank) { - this.vertexRank = vertexRank; - } - - @Override - public void setCutEdges(boolean enable) {} - - @Override - public void doLayout(LayoutGraph graph) { - doLayout(graph, new HashSet<>()); - } - - @Override - public void doLayout(LayoutGraph graph, Set importantLinks) { - - assert (graph.getLinks().isEmpty()); - - // Sort vertices according to given rank. - List vertices = new ArrayList<>(graph.getVertices()); - vertices.sort(Comparator.comparingInt((Vertex v) -> vertexRank.getOrDefault(v, Integer.MAX_VALUE))); - - // Assign vertical coordinates in rank order. - assignVerticalCoordinates(vertices); - } - - private void assignVerticalCoordinates(List vertices) { - int curY = 0; - for (Vertex v : vertices) { - v.setPosition(new Point(0, curY)); - curY += v.getSize().getHeight(); - } - } -} diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/Timing.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/Timing.java deleted file mode 100644 index 080516df120..00000000000 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/Timing.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2008, 2015, 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. - * - */ -package com.sun.hotspot.igv.hierarchicallayout; - -/** - * - * @author Thomas Wuerthinger - */ -public class Timing { - - private long lastValue; - private long sum; - private final String name; - - public Timing(String name) { - this.name = name; - } - - @Override - public String toString() { - long val = sum; - if (lastValue != 0) { - // Timer running - long newValue = System.nanoTime(); - val += (newValue - lastValue); - } - return "Timing for " + name + " is: " + val / 1000000 + " ms"; - } - - public void print() { - System.out.println(); - } - - public void start() { - lastValue = System.nanoTime(); - } - - public void stop() { - if (lastValue == 0) { - throw new IllegalStateException("You must call start before stop"); - } - long newValue = System.nanoTime(); - sum += newValue - lastValue; - lastValue = 0; - } -} diff --git a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Cluster.java b/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Cluster.java index e6938cb2f19..84ddd5dd695 100644 --- a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Cluster.java +++ b/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Cluster.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -23,8 +23,9 @@ */ package com.sun.hotspot.igv.layout; -import java.awt.Dimension; +import java.awt.Point; import java.awt.Rectangle; +import java.util.List; import java.util.Set; /** @@ -35,7 +36,13 @@ public interface Cluster extends Comparable { void setBounds(Rectangle r); - Set getSuccessors(); + void setPosition(Point p); + + Point getPosition(); + + Rectangle getBounds(); - Dimension getNodeOffset(); + List getVertices(); + + Set getSuccessors(); } diff --git a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutGraph.java b/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutGraph.java deleted file mode 100644 index ad358cba299..00000000000 --- a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/LayoutGraph.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2008, 2022, 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. - * - */ -package com.sun.hotspot.igv.layout; - -import java.util.*; - -/** - * - * @author Thomas Wuerthinger - */ -public class LayoutGraph { - - private final Set links; - private final SortedSet vertices; - private final HashMap> inputPorts; - private final HashMap> outputPorts; - private final HashMap> portLinks; - - public LayoutGraph(Set links) { - this(links, new HashSet<>()); - } - - public LayoutGraph(Set links, Set additionalVertices) { - this.links = links; - assert verify(); - - vertices = new TreeSet<>(); - portLinks = new HashMap<>(links.size()); - inputPorts = new HashMap<>(links.size()); - outputPorts = new HashMap<>(links.size()); - - for (Link l : links) { - if (l.getFrom() == null || l.getTo() == null) { - continue; - } - Port p = l.getFrom(); - Port p2 = l.getTo(); - Vertex v1 = p.getVertex(); - Vertex v2 = p2.getVertex(); - - if (!vertices.contains(v1)) { - - outputPorts.put(v1, new HashSet<>(1)); - inputPorts.put(v1, new HashSet<>(3)); - vertices.add(v1); - assert vertices.contains(v1); - } - - if (!vertices.contains(v2)) { - vertices.add(v2); - assert vertices.contains(v2); - outputPorts.put(v2, new HashSet<>(1)); - inputPorts.put(v2, new HashSet<>(3)); - } - - if (!portLinks.containsKey(p)) { - HashSet hashSet = new HashSet<>(3); - portLinks.put(p, hashSet); - } - - if (!portLinks.containsKey(p2)) { - portLinks.put(p2, new HashSet<>(3)); - } - - outputPorts.get(v1).add(p); - inputPorts.get(v2).add(p2); - - portLinks.get(p).add(l); - portLinks.get(p2).add(l); - } - - for (Vertex v : additionalVertices) { - if (!vertices.contains(v)) { - outputPorts.put(v, new HashSet<>(1)); - inputPorts.put(v, new HashSet<>(3)); - vertices.add(v); - } - } - } - - public Set getInputPorts(Vertex v) { - return this.inputPorts.get(v); - } - - public Set getOutputPorts(Vertex v) { - return this.outputPorts.get(v); - } - - public Set getPortLinks(Port p) { - return portLinks.get(p); - } - - public Set getLinks() { - return links; - } - - public boolean verify() { - return true; - } - - public SortedSet getVertices() { - return vertices; - } - - private void markNotRoot(Set notRootSet, Vertex v, Vertex startingVertex) { - - if (notRootSet.contains(v)) { - return; - } - if (v != startingVertex) { - notRootSet.add(v); - } - Set outPorts = getOutputPorts(v); - for (Port p : outPorts) { - Set portLinks = getPortLinks(p); - for (Link l : portLinks) { - Port other = l.getTo(); - Vertex otherVertex = other.getVertex(); - if (otherVertex != startingVertex) { - markNotRoot(notRootSet, otherVertex, startingVertex); - } - } - } - } - - // Returns a set of vertices with the following properties: - // - All Vertices in the set startingRoots are elements of the set. - // - When starting a DFS at every vertex in the set, every vertex of the - // whole graph is visited. - public Set findRootVertices(Set startingRoots) { - - Set notRootSet = new HashSet<>(); - for (Vertex v : startingRoots) { - if (!notRootSet.contains(v)) { - markNotRoot(notRootSet, v, v); - } - } - - Set tmpVertices = getVertices(); - for (Vertex v : tmpVertices) { - if (!notRootSet.contains(v)) { - if (this.getInputPorts(v).size() == 0) { - markNotRoot(notRootSet, v, v); - } - } - } - - for (Vertex v : tmpVertices) { - if (!notRootSet.contains(v)) { - markNotRoot(notRootSet, v, v); - } - } - - Set result = new HashSet<>(); - for (Vertex v : tmpVertices) { - if (!notRootSet.contains(v)) { - result.add(v); - } - } - assert tmpVertices.size() == 0 || result.size() > 0; - return result; - } - - public Set findRootVertices() { - return findRootVertices(new HashSet<>()); - } - - public SortedSet getClusters() { - - SortedSet clusters = new TreeSet<>(); - for (Vertex v : getVertices()) { - if (v.getCluster() != null) { - clusters.add(v.getCluster()); - } - } - - return clusters; - } - - @Override - public String toString() { - return "LayoutGraph(" + vertices + ", " + links + ", " + getClusters() + ")"; - } -} diff --git a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Link.java b/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Link.java index 681ef7af33a..bfe057a4739 100644 --- a/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Link.java +++ b/src/utils/IdealGraphVisualizer/Layout/src/main/java/com/sun/hotspot/igv/layout/Link.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -40,8 +40,6 @@ public interface Link { Cluster getToCluster(); - boolean isVIP(); - List getControlPoints(); void setControlPoints(List list); diff --git a/src/utils/IdealGraphVisualizer/Util/src/main/java/com/sun/hotspot/igv/util/Statistics.java b/src/utils/IdealGraphVisualizer/Util/src/main/java/com/sun/hotspot/igv/util/Statistics.java deleted file mode 100644 index b40ce7919df..00000000000 --- a/src/utils/IdealGraphVisualizer/Util/src/main/java/com/sun/hotspot/igv/util/Statistics.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2023, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package com.sun.hotspot.igv.util; - -import java.util.Arrays; - -public class Statistics { - - public static int median(int[] values) { - Arrays.sort(values); - if (values.length % 2 == 0) { - return (values[values.length / 2 - 1] + values[values.length / 2]) / 2; - } else { - return values[values.length / 2]; - } - } -} diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java index 3f7fec74505..297c7d9b998 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java @@ -27,7 +27,8 @@ import com.sun.hotspot.igv.data.*; import com.sun.hotspot.igv.graph.*; import com.sun.hotspot.igv.hierarchicallayout.*; -import com.sun.hotspot.igv.layout.LayoutGraph; +import com.sun.hotspot.igv.layout.Cluster; +import com.sun.hotspot.igv.hierarchicallayout.LayoutGraph; import com.sun.hotspot.igv.selectioncoordinator.SelectionCoordinator; import com.sun.hotspot.igv.util.ColorIcon; import com.sun.hotspot.igv.util.DoubleClickAction; @@ -44,13 +45,12 @@ import javax.swing.*; import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS; import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS; +import javax.swing.border.Border; import javax.swing.event.UndoableEditEvent; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import org.netbeans.api.visual.action.*; -import org.netbeans.api.visual.animator.AnimatorEvent; -import org.netbeans.api.visual.animator.AnimatorListener; import org.netbeans.api.visual.layout.LayoutFactory; import org.netbeans.api.visual.model.*; import org.netbeans.api.visual.widget.LayerWidget; @@ -84,7 +84,10 @@ public class DiagramScene extends ObjectScene implements DiagramViewer, DoubleCl private final DiagramViewModel model; private ModelState modelState; private boolean rebuilding; + private final HierarchicalStableLayoutManager hierarchicalStableLayoutManager; + private HierarchicalLayoutManager seaLayoutManager; + /** * The alpha level of partially visible figures. @@ -94,14 +97,13 @@ public class DiagramScene extends ObjectScene implements DiagramViewer, DoubleCl /** * The offset of the graph to the border of the window showing it. */ - public static final int BORDER_SIZE = 100; + public static final int BORDER_SIZE = 50; public static final int UNDOREDO_LIMIT = 100; public static final int SCROLL_UNIT_INCREMENT = 80; public static final int SCROLL_BLOCK_INCREMENT = 400; public static final float ZOOM_MAX_FACTOR = 4.0f; public static final float ZOOM_MIN_FACTOR = 0.25f; public static final float ZOOM_INCREMENT = 1.5f; - public static final int SLOT_OFFSET = 8; public static final int ANIMATION_LIMIT = 40; @SuppressWarnings("unchecked") @@ -325,16 +327,20 @@ public void select(Widget widget, Point localLocation, boolean invertSelection) getActions().addAction(selectAction); + Border emptyBorder = BorderFactory.createEmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE); + blockLayer = new LayerWidget(this); + blockLayer.setBorder(emptyBorder); addChild(blockLayer); connectionLayer = new LayerWidget(this); + connectionLayer.setBorder(emptyBorder); addChild(connectionLayer); mainLayer = new LayerWidget(this); + mainLayer.setBorder(emptyBorder); addChild(mainLayer); - setBorder(BorderFactory.createLineBorder(Color.white, BORDER_SIZE)); setLayout(LayoutFactory.createAbsoluteLayout()); getActions().addAction(mouseZoomAction); getActions().addAction(ActionFactory.createPopupMenuAction((widget, localLocation) -> createPopupMenu())); @@ -357,6 +363,7 @@ public void select(Widget widget, Point localLocation, boolean invertSelection) rectangle.height *= -1; } + clearSelectedNodes(); Set selectedObjects = new HashSet<>(); for (Figure f : getModel().getDiagram().getFigures()) { FigureWidget w = getWidget(f); @@ -478,9 +485,6 @@ public void focusChanged(ObjectSceneEvent arg0, Object arg1, Object arg2) { }; addObjectSceneListener(selectionChangedListener, ObjectSceneEventType.OBJECT_SELECTION_CHANGED, ObjectSceneEventType.OBJECT_HIGHLIGHTING_CHANGED, ObjectSceneEventType.OBJECT_HOVER_CHANGED); - this.model = model; - modelState = new ModelState(model); - model.getDiagramChangedEvent().addListener(m -> update()); model.getGraphChangedEvent().addListener(m -> graphChanged()); model.getHiddenNodesChangedEvent().addListener(m -> hiddenNodesChanged()); @@ -498,6 +502,10 @@ public void ancestorResized(HierarchyEvent e) { }); hierarchicalStableLayoutManager = new HierarchicalStableLayoutManager(); + seaLayoutManager = new HierarchicalLayoutManager(); + + this.model = model; + modelState = new ModelState(model); } @Override @@ -554,9 +562,11 @@ public void actionPerformed(ActionEvent e) { } private void clearObjects() { - Collection objects = new ArrayList<>(getObjects()); - for (Object o : objects) { - removeObject(o); + Set objectSet = new HashSet<>(getObjects()); + for (Object object : objectSet) { + if (isObject(object)) { + removeObject(object); + } } } @@ -569,19 +579,19 @@ private void updateFigureTexts() { private void updateFigureWidths() { if (getModel().getShowCFG()) { - Map maxWidth = new HashMap<>(); - for (InputBlock inputBlock : getModel().getDiagram().getInputBlocks()) { - maxWidth.put(inputBlock, 10); + Map maxWidth = new HashMap<>(); + for (Block block : getModel().getDiagram().getBlocks()) { + maxWidth.put(block, 10); } for (Figure figure : getModel().getDiagram().getFigures()) { // Compute max node width in each block. - if (figure.getWidth() > maxWidth.get(figure.getBlock().getInputBlock())) { - maxWidth.put(figure.getBlock().getInputBlock(), figure.getWidth()); + if (figure.getWidth() > maxWidth.get(figure.getBlock())) { + maxWidth.put(figure.getBlock(), figure.getWidth()); } } for (Figure figure : getModel().getDiagram().getFigures()) { // Set all nodes' width to the maximum width in the blocks? - figure.setWidth(maxWidth.get(figure.getBlock().getInputBlock())); + figure.setWidth(maxWidth.get(figure.getBlock())); } } } @@ -598,7 +608,7 @@ private void rebuildMainLayer() { mainLayer.addChild(figureWidget); for (InputSlot inputSlot : figure.getInputSlots()) { - SlotWidget slotWidget = new InputSlotWidget(inputSlot, this, figureWidget, figureWidget); + SlotWidget slotWidget = new InputSlotWidget(inputSlot, this, figureWidget); slotWidget.getActions().addAction(new DoubleClickAction(slotWidget)); slotWidget.getActions().addAction(hoverAction); slotWidget.getActions().addAction(selectAction); @@ -606,7 +616,7 @@ private void rebuildMainLayer() { } for (OutputSlot outputSlot : figure.getOutputSlots()) { - SlotWidget slotWidget = new OutputSlotWidget(outputSlot, this, figureWidget, figureWidget); + SlotWidget slotWidget = new OutputSlotWidget(outputSlot, this, figureWidget); slotWidget.getActions().addAction(new DoubleClickAction(slotWidget)); slotWidget.getActions().addAction(hoverAction); slotWidget.getActions().addAction(selectAction); @@ -618,11 +628,11 @@ private void rebuildMainLayer() { private void rebuildBlockLayer() { blockLayer.removeChildren(); if (getModel().getShowBlocks() || getModel().getShowCFG()) { - for (InputBlock inputBlock : getModel().getDiagram().getInputBlocks()) { - BlockWidget blockWidget = new BlockWidget(this, inputBlock); + for (Block block : getModel().getDiagram().getBlocks()) { + BlockWidget blockWidget = new BlockWidget(this, block); blockWidget.getActions().addAction(new DoubleClickAction(blockWidget)); blockWidget.setVisible(false); - addObject(inputBlock, blockWidget); + addObject(block, blockWidget); blockLayer.addChild(blockWidget); } } @@ -636,8 +646,41 @@ private void update() { rebuildMainLayer(); rebuildBlockLayer(); relayout(); - setFigureSelection(model.getSelectedFigures()); + rebuilding = false; + } + + private void hiddenNodesChanged() { + relayout(); + addUndo(); + } + + private void relayout() { + rebuilding = true; + Set oldVisibleFigureWidgets = getVisibleFigureWidgets(); + Set oldVisibleBlockWidgets = getVisibleBlockWidgets(); + + updateVisibleFigureWidgets(); + updateNodeHull(); + updateVisibleBlockWidgets(); validateAll(); + + Set
visibleFigures = getVisibleFigures(); + Set visibleConnections = getVisibleConnections(); + if (getModel().getShowStableSea()) { + doStableSeaLayout(visibleFigures, visibleConnections); + } else if (getModel().getShowSea()) { + doSeaLayout(visibleFigures, visibleConnections); + } else if (getModel().getShowBlocks()) { + doClusteredLayout(visibleFigures, visibleConnections); + } else if (getModel().getShowCFG()) { + doCFGLayout(visibleFigures, visibleConnections); + } + rebuildConnectionLayer(); + + updateFigureWidgetLocations(oldVisibleFigureWidgets); + updateBlockWidgetBounds(oldVisibleBlockWidgets); + validateAll(); + setFigureSelection(model.getSelectedFigures()); centerSelectedFigures(); rebuilding = false; } @@ -663,7 +706,7 @@ private void centerRootNode() { Rectangle bounds = rootWidget.getBounds(); if (bounds != null) { Point location = rootWidget.getLocation(); - centerRectangle(new Rectangle(location.x, location.y, bounds.width, bounds.height), false); + centerRectangle(new Rectangle(location.x, location.y, bounds.width, bounds.height)); } } } @@ -671,35 +714,29 @@ private void centerRootNode() { } } - private void hiddenNodesChanged() { - relayout(); - addUndo(); - } - protected boolean isRebuilding() { return rebuilding; } - private boolean isVisible(Connection c) { + public boolean isVisibleBlockConnection(BlockConnection blockConnection) { + Widget w1 = getWidget(blockConnection.getFromCluster()); + Widget w2 = getWidget(blockConnection.getToCluster()); + return w1.isVisible() && w2.isVisible(); + } + + private boolean isVisibleFigureConnection(FigureConnection figureConnection) { // Generally, a connection is visible if its source and destination // widgets are visible. An exception is Figure connections in the CFG // view, which are never shown. - if (getModel().getShowCFG() && c instanceof FigureConnection) { + if (getModel().getShowCFG()) { return false; } - Widget w1, w2; - if (c instanceof BlockConnection) { - w1 = getWidget(((Block)c.getFromCluster()).getInputBlock()); - w2 = getWidget(((Block)c.getToCluster()).getInputBlock()); - } else { - assert (c instanceof FigureConnection); - w1 = getWidget(c.getFrom().getVertex()); - w2 = getWidget(c.getTo().getVertex()); - } + Widget w1 = getWidget(figureConnection.getFrom().getVertex()); + Widget w2 = getWidget(figureConnection.getTo().getVertex()); return w1.isVisible() && w2.isVisible(); } - private void doStableSeaLayout(HashSet
visibleFigures, HashSet visibleConnections) { + private void doStableSeaLayout(Set
visibleFigures, Set visibleConnections) { boolean enable = model.getCutEdges(); boolean previous = hierarchicalStableLayoutManager.getCutEdges(); hierarchicalStableLayoutManager.setCutEdges(enable); @@ -710,73 +747,23 @@ private void doStableSeaLayout(HashSet
visibleFigures, HashSet figures, HashSet edges) { - HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS); - manager.setCutEdges(model.getCutEdges()); - manager.doLayout(new LayoutGraph(edges, figures)); - hierarchicalStableLayoutManager.setShouldRedrawLayout(true); - } - - private void doClusteredLayout(HashSet edges) { - HierarchicalClusterLayoutManager m = new HierarchicalClusterLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS); - HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS); - manager.setCutEdges(model.getCutEdges()); - manager.setMinLayerDifference(3); - m.setManager(manager); - m.setSubManager(new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS)); - m.doLayout(new LayoutGraph(edges)); - } - - private void doCFGLayout(HashSet
figures, HashSet edges) { - Diagram diagram = getModel().getDiagram(); - HierarchicalCFGLayoutManager m = new HierarchicalCFGLayoutManager(); - HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS); - manager.setCutEdges(model.getCutEdges()); - manager.setMinLayerDifference(1); - manager.setLayoutSelfEdges(true); - manager.setXOffset(25); - manager.setLayerOffset(25); - m.setManager(manager); - Map nodeFig = new HashMap<>(); - for (Figure f : figures) { - InputNode n = f.getInputNode(); - if (n != null) { - nodeFig.put(n, f); - } - } - // Compute global ranking among figures given by in-block order. If - // needed, this could be cached as long as it is computed for all the - // figures in the model, not just the visible ones. - Map figureRank = new HashMap<>(figures.size()); - int r = 0; - for (InputBlock b : diagram.getInputBlocks()) { - for (InputNode n : b.getNodes()) { - Figure f = nodeFig.get(n); - if (f != null) { - figureRank.put(f, r); - r++; - } - } - } - // Add visible connections for CFG edges. - for (BlockConnection c : diagram.getBlockConnections()) { - if (isVisible(c)) { - edges.add(c); - } - } - m.setSubManager(new LinearLayoutManager(figureRank)); - Set visibleBlocks = new HashSet<>(); - for (Block b : diagram.getBlocks()) { - BlockWidget w = getWidget(b.getInputBlock()); - if (w.isVisible()) { - visibleBlocks.add(b); - } - } - m.setClusters(new HashSet<>(visibleBlocks)); - m.doLayout(new LayoutGraph(edges, figures)); + private void doSeaLayout(Set
visibleFigures, Set visibleConnections) { + seaLayoutManager = new HierarchicalLayoutManager(); + seaLayoutManager.setCutEdges(model.getCutEdges()); + seaLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures)); } + private void doClusteredLayout(Set
visibleFigures, Set visibleConnections) { + HierarchicalClusterLayoutManager clusterLayoutManager = new HierarchicalClusterLayoutManager(); + clusterLayoutManager.setCutEdges(model.getCutEdges()); + clusterLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures)); + } + private void doCFGLayout(Set
visibleFigures, Set visibleConnections) { + HierarchicalCFGLayoutManager cfgLayoutManager = new HierarchicalCFGLayoutManager(getVisibleBlockConnections(), getVisibleBlocks()); + cfgLayoutManager.setCutEdges(model.getCutEdges()); + cfgLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures)); + } private boolean shouldAnimate() { int visibleFigureCount = 0; @@ -790,47 +777,35 @@ private boolean shouldAnimate() { private final Point specialNullPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); - private void processOutputSlot(OutputSlot outputSlot, List connections, int controlPointIndex, Point lastPoint, LineWidget predecessor) { - Map> pointMap = new HashMap<>(connections.size()); - - for (Connection connection : connections) { - if (!isVisible(connection)) { - continue; - } + private void processOutputSlot(OutputSlot outputSlot, List connections, int controlPointIndex, Point lastPoint, LineWidget predecessor) { + Map> pointMap = new HashMap<>(connections.size()); - List controlPoints = connection.getControlPoints(); - if (controlPointIndex >= controlPoints.size()) { - continue; - } - - Point currentPoint = controlPoints.get(controlPointIndex); - if (currentPoint == null) { // Long connection, has been cut vertically. - currentPoint = specialNullPoint; - } else if (connection.hasSlots()) { - if (controlPointIndex == 0 && !outputSlot.shouldShowName()) { - currentPoint = new Point(currentPoint.x, currentPoint.y - SLOT_OFFSET); - } else if (controlPointIndex == controlPoints.size() - 1 && - !((Slot)connection.getTo()).shouldShowName()) { - currentPoint = new Point(currentPoint.x, currentPoint.y + SLOT_OFFSET); + for (FigureConnection connection : connections) { + if (isVisibleFigureConnection(connection)) { + List controlPoints = connection.getControlPoints(); + if (controlPointIndex < controlPoints.size()) { + Point currentPoint = controlPoints.get(controlPointIndex); + if (currentPoint == null) { // Long connection, has been cut vertically. + currentPoint = specialNullPoint; + } else { + currentPoint = new Point(currentPoint.x, currentPoint.y); + } + if (pointMap.containsKey(currentPoint)) { + pointMap.get(currentPoint).add(connection); + } else { + pointMap.put(currentPoint, new ArrayList<>(Collections.singletonList(connection))); + } } } - - if (pointMap.containsKey(currentPoint)) { - pointMap.get(currentPoint).add(connection); - } else { - List newList = new ArrayList<>(2); - newList.add(connection); - pointMap.put(currentPoint, newList); - } } for (Point currentPoint : pointMap.keySet()) { - List connectionList = pointMap.get(currentPoint); + List connectionList = pointMap.get(currentPoint); boolean isBold = false; boolean isDashed = true; boolean isVisible = true; - for (Connection c : connectionList) { + for (FigureConnection c : connectionList) { if (c.getStyle() == Connection.ConnectionStyle.BOLD) { isBold = true; } else if (c.getStyle() == Connection.ConnectionStyle.INVISIBLE) { @@ -848,13 +823,57 @@ private void processOutputSlot(OutputSlot outputSlot, List connectio newPredecessor = new LineWidget(this, outputSlot, connectionList, src, dest, predecessor, isBold, isDashed); newPredecessor.setVisible(isVisible); - connectionLayer.addChild(newPredecessor); + if (predecessor == null) { + if (outputSlotToLineWidget.containsKey(outputSlot)) { + outputSlotToLineWidget.get(outputSlot).add(newPredecessor); + } else { + outputSlotToLineWidget.put(outputSlot, new HashSet<>(Collections.singleton(newPredecessor))); + } + } + + newWidgets.add(newPredecessor); addObject(new ConnectionSet(connectionList), newPredecessor); newPredecessor.getActions().addAction(hoverAction); } processOutputSlot(outputSlot, connectionList, controlPointIndex + 1, currentPoint, newPredecessor); } + + if (pointMap.isEmpty()) { + for (FigureConnection connection : connections) { + if (isVisibleFigureConnection(connection)) { + InputSlot inputSlot = connection.getInputSlot(); + if (inputSlotToLineWidget.containsKey(inputSlot)) { + inputSlotToLineWidget.get(inputSlot).add(predecessor); + } else { + inputSlotToLineWidget.put(inputSlot, new HashSet<>(Collections.singleton(predecessor))); + } + } + } + } + } + + private void processBlockConnection(BlockConnection blockConnection) { + boolean isDashed = blockConnection.getStyle() == Connection.ConnectionStyle.DASHED; + boolean isBold = blockConnection.getStyle() == Connection.ConnectionStyle.BOLD; + boolean isVisible = blockConnection.getStyle() != Connection.ConnectionStyle.INVISIBLE; + Point lastPoint = null; + LineWidget predecessor = null; + for (Point currentPoint : blockConnection.getControlPoints()) { + if (currentPoint == null) { // Long connection, has been cut vertically. + currentPoint = specialNullPoint; + } else if (lastPoint != specialNullPoint && lastPoint != null) { + List connectionList = Collections.singletonList(blockConnection); + Point src = new Point(lastPoint); + Point dest = new Point(currentPoint); + predecessor = new LineWidget(this, null, connectionList, src, dest, predecessor, isBold, isDashed); + predecessor.setVisible(isVisible); + connectionLayer.addChild(predecessor); + addObject(new ConnectionSet(connectionList), predecessor); + predecessor.getActions().addAction(hoverAction); + } + lastPoint = currentPoint; + } } @Override @@ -871,14 +890,10 @@ public void handleDoubleClick(Widget w, WidgetAction.WidgetMouseEvent e) { private class ConnectionSet { - private Set connections; + private Collection connections; - public ConnectionSet(Collection connections) { - connections = new HashSet<>(connections); - } - - public Set getConnectionSet() { - return Collections.unmodifiableSet(connections); + public ConnectionSet(Collection connections) { + this.connections = connections; } } @@ -888,9 +903,9 @@ public Lookup getLookup() { } private void gotoBlock(final Block block) { - BlockWidget bw = getWidget(block.getInputBlock()); + BlockWidget bw = getWidget(block); if (bw != null) { - centerRectangle(bw.getBounds(), true); + centerRectangle(bw.getBounds()); } } @@ -937,28 +952,26 @@ public void centerSelectedFigures() { } } if (overallRect != null) { - centerRectangle(overallRect, true); + centerRectangle(overallRect); } } - private void centerRectangle(Rectangle r, boolean zoomToFit) { + private void centerRectangle(Rectangle r) { Rectangle rect = convertSceneToView(r); Rectangle viewRect = scrollPane.getViewport().getViewRect(); - if (zoomToFit) { - double factor = Math.min(viewRect.getWidth() / rect.getWidth(), viewRect.getHeight() / rect.getHeight()); - double zoomFactor = getZoomFactor(); - double newZoomFactor = zoomFactor * factor; - if (factor < 1.0 || zoomFactor < 1.0) { - newZoomFactor = Math.min(1.0, newZoomFactor); - centredZoom(newZoomFactor, null); - factor = newZoomFactor / zoomFactor; - rect.x *= factor; - rect.y *= factor; - rect.width *= factor; - rect.height *= factor; - } - } + double factor = Math.min(viewRect.getWidth() / rect.getWidth(), viewRect.getHeight() / rect.getHeight()); + double zoomFactor = getZoomFactor(); + double newZoomFactor = zoomFactor * factor; + if (factor < 1.0 || zoomFactor < 1.0) { + newZoomFactor = Math.min(1.0, newZoomFactor); + centredZoom(newZoomFactor, null); + factor = newZoomFactor / zoomFactor; + rect.x *= factor; + rect.y *= factor; + rect.width *= factor; + rect.height *= factor; + } viewRect.x = rect.x + rect.width / 2 - viewRect.width / 2; viewRect.y = rect.y + rect.height / 2 - viewRect.height / 2; // Ensure to be within area @@ -1003,22 +1016,30 @@ public void componentShowing() { SelectionCoordinator.getInstance().getSelectedChangedEvent().addListener(selectedCoordinatorListener); } + private final ArrayList newWidgets = new ArrayList<>(); + private void rebuildConnectionLayer() { + outputSlotToLineWidget.clear(); + inputSlotToLineWidget.clear(); connectionLayer.removeChildren(); + newWidgets.clear(); for (Figure figure : getModel().getDiagram().getFigures()) { for (OutputSlot outputSlot : figure.getOutputSlots()) { - List connectionList = new ArrayList<>(outputSlot.getConnections()); + List connectionList = new ArrayList<>(outputSlot.getConnections()); processOutputSlot(outputSlot, connectionList, 0, null, null); } } if (getModel().getShowCFG()) { for (BlockConnection blockConnection : getModel().getDiagram().getBlockConnections()) { - if (isVisible(blockConnection)) { - processOutputSlot(null, Collections.singletonList(blockConnection), 0, null, null); + if (isVisibleBlockConnection(blockConnection)) { + processBlockConnection(blockConnection); } } } + + connectionLayer.addChildren(newWidgets); + newWidgets.clear(); } private Set getVisibleFigureWidgets() { @@ -1035,8 +1056,8 @@ private Set getVisibleFigureWidgets() { private Set getVisibleBlockWidgets() { Set visibleBlockWidgets = new HashSet<>(); if (getModel().getShowBlocks() || getModel().getShowCFG()) { - for (InputBlock inputBlock : getModel().getDiagram().getInputBlocks()) { - BlockWidget blockWidget = getWidget(inputBlock); + for (Block block : getModel().getDiagram().getBlocks()) { + BlockWidget blockWidget = getWidget(block); if (blockWidget.isVisible()) { visibleBlockWidgets.add(blockWidget); } @@ -1085,16 +1106,16 @@ private void updateNodeHull() { private void updateVisibleBlockWidgets() { if (getModel().getShowBlocks() || getModel().getShowCFG()) { - Set visibleBlocks = new HashSet<>(); + Set visibleBlocks = new HashSet<>(); for (Figure figure : getModel().getDiagram().getFigures()) { FigureWidget figureWidget = getWidget(figure); if (figureWidget.isVisible()) { - visibleBlocks.add(figure.getBlock().getInputBlock()); + visibleBlocks.add(figure.getBlock()); } } if (getModel().getShowCFG() && getModel().getShowEmptyBlocks()) { // Add remaining blocks. - visibleBlocks.addAll(getModel().getDiagram().getInputBlocks()); + visibleBlocks.addAll(getModel().getDiagram().getBlocks()); } if (getModel().getShowCFG()) { // Blockless figures and artificial blocks are hidden in this view. @@ -1106,19 +1127,18 @@ private void updateVisibleBlockWidgets() { } } - for (InputBlock inputBlock : getModel().getDiagram().getInputBlocks()) { + for (Block block : getModel().getDiagram().getBlocks()) { // A block is visible if it is marked as such, except for // artificial or null blocks in the CFG view. - boolean visibleAfter = visibleBlocks.contains(inputBlock) && - !(getModel().getShowCFG() && (inputBlock.isArtificial() || inputBlock.getNodes().isEmpty())); - - BlockWidget blockWidget = getWidget(inputBlock); + boolean visibleAfter = visibleBlocks.contains(block) && + !(getModel().getShowCFG() && (block.getInputBlock().isArtificial() || block.getInputBlock().getNodes().isEmpty())); + BlockWidget blockWidget = getWidget(block); blockWidget.setVisible(visibleAfter); } } } - private HashSet
getVisibleFigures() { + private Set
getVisibleFigures() { HashSet
visibleFigures = new HashSet<>(); for (Figure figure : getModel().getDiagram().getFigures()) { FigureWidget figureWidget = getWidget(figure); @@ -1129,10 +1149,31 @@ private HashSet
getVisibleFigures() { return visibleFigures; } + private Set getVisibleBlocks() { + Set visibleBlocks = new HashSet<>(); + for (Block b : getModel().getDiagram().getBlocks()) { + BlockWidget w = getWidget(b); + if (w.isVisible()) { + visibleBlocks.add(b); + } + } + return visibleBlocks; + } + + private Set getVisibleBlockConnections() { + Set clusterLinks = new HashSet<>(); + for (BlockConnection c : getModel().getDiagram().getBlockConnections()) { + if (isVisibleBlockConnection(c)) { + clusterLinks.add(c); + } + } + return clusterLinks; + } + private HashSet getVisibleConnections() { HashSet visibleConnections = new HashSet<>(); - for (Connection connection : getModel().getDiagram().getConnections()) { - if (isVisible(connection)) { + for (FigureConnection connection : getModel().getDiagram().getConnections()) { + if (isVisibleFigureConnection(connection)) { visibleConnections.add(connection); } } @@ -1158,7 +1199,7 @@ private void updateBlockWidgetBounds(Set oldVisibleBlockWidgets) { if (getModel().getShowBlocks() || getModel().getShowCFG()) { boolean doAnimation = shouldAnimate(); for (Block block : getModel().getDiagram().getBlocks()) { - BlockWidget blockWidget = getWidget(block.getInputBlock()); + BlockWidget blockWidget = getWidget(block); if (blockWidget != null && blockWidget.isVisible()) { Rectangle bounds = new Rectangle(block.getBounds()); if (doAnimation && oldVisibleBlockWidgets.contains(blockWidget)) { @@ -1171,65 +1212,8 @@ private void updateBlockWidgetBounds(Set oldVisibleBlockWidgets) { } } - private void centerSingleSelectedFigure() { - if (model.getSelectedFigures().size() == 1) { - if (getSceneAnimator().getPreferredLocationAnimator().isRunning()) { - getSceneAnimator().getPreferredLocationAnimator().addAnimatorListener(new AnimatorListener() { - @Override - public void animatorStarted(AnimatorEvent animatorEvent) {} - - @Override - public void animatorReset(AnimatorEvent animatorEvent) {} - - @Override - public void animatorFinished(AnimatorEvent animatorEvent) { - getSceneAnimator().getPreferredLocationAnimator().removeAnimatorListener(this); - } - - @Override - public void animatorPreTick(AnimatorEvent animatorEvent) {} - - @Override - public void animatorPostTick(AnimatorEvent animatorEvent) { - validateAll(); - centerSelectedFigures(); - } - }); - } else { - centerSelectedFigures(); - } - } - } - - private void relayout() { - rebuilding = true; - Set oldVisibleFigureWidgets = getVisibleFigureWidgets(); - Set oldVisibleBlockWidgets = getVisibleBlockWidgets(); - - updateVisibleFigureWidgets(); - updateNodeHull(); - updateVisibleBlockWidgets(); - - HashSet
visibleFigures = getVisibleFigures(); - HashSet visibleConnections = getVisibleConnections(); - if (getModel().getShowStableSea()) { - doStableSeaLayout(visibleFigures, visibleConnections); - } else if (getModel().getShowSea()) { - doSeaLayout(visibleFigures, visibleConnections); - } else if (getModel().getShowBlocks()) { - doClusteredLayout(visibleConnections); - } else if (getModel().getShowCFG()) { - doCFGLayout(visibleFigures, visibleConnections); - } - rebuildConnectionLayer(); - - updateFigureWidgetLocations(oldVisibleFigureWidgets); - updateBlockWidgetBounds(oldVisibleBlockWidgets); - validateAll(); - - centerSingleSelectedFigure(); - rebuilding = false; - } + Map> outputSlotToLineWidget = new HashMap<>(); + Map> inputSlotToLineWidget = new HashMap<>(); public JPopupMenu createPopupMenu() { JPopupMenu menu = new JPopupMenu(); @@ -1295,6 +1279,7 @@ private static class ModelState { public final int firstPos; public final int secondPos; + public ModelState(DiagramViewModel model) { hiddenNodes = new HashSet<>(model.getHiddenNodes()); firstPos = model.getFirstPosition(); @@ -1303,11 +1288,11 @@ public ModelState(DiagramViewModel model) { } private void addUndo() { - ModelState newModelState = new ModelState(model); if (undoRedoEnabled) { + ModelState newModelState = new ModelState(model); DiagramUndoRedo undoRedo = new DiagramUndoRedo(this, getScrollPosition(), modelState, newModelState); getUndoRedoManager().undoableEditHappened(new UndoableEditEvent(this, undoRedo)); + modelState = newModelState; } - modelState = newModelState; } } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java index d69047c8c3e..d067925fb87 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/EditorTopComponent.java @@ -140,6 +140,7 @@ public EditorTopComponent(DiagramViewModel diagramViewModel) { cardLayout = new CardLayout(); centerPanel = new JPanel(); centerPanel.setLayout(cardLayout); + centerPanel.setOpaque(true); centerPanel.setBackground(Color.WHITE); satelliteComponent = scene.createSatelliteView(); satelliteComponent.setSize(200, 200); @@ -196,17 +197,6 @@ public void mouseMoved(MouseEvent e) {} layoutButtons.add(cfgLayoutButton); toolBar.add(cfgLayoutButton); - diagramViewModel.getGraphChangedEvent().addListener(model -> { - // HierarchicalStableLayoutManager is not reliable for difference graphs - boolean isDiffGraph = model.getGraph().isDiffGraph(); - // deactivate HierarchicalStableLayoutManager for difference graphs - stableSeaLayoutButton.setEnabled(!isDiffGraph); - if (stableSeaLayoutButton.isSelected() && isDiffGraph) { - // fallback to HierarchicalLayoutManager for difference graphs - seaLayoutButton.setSelected(true); - } - }); - toolBar.addSeparator(); toolBar.add(new JToggleButton(new PredSuccAction(diagramViewModel.getShowNodeHull()))); toolBar.add(new JToggleButton(new ShowEmptyBlocksAction(cfgLayoutAction, diagramViewModel.getShowEmptyBlocks()))); diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/BlockWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/BlockWidget.java index b7dcf7bc0d8..941836f5bb7 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/BlockWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/BlockWidget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -23,6 +23,7 @@ */ package com.sun.hotspot.igv.view.widgets; +import com.sun.hotspot.igv.graph.Block; import com.sun.hotspot.igv.data.InputBlock; import com.sun.hotspot.igv.data.services.InputGraphProvider; import com.sun.hotspot.igv.util.DoubleClickHandler; @@ -44,11 +45,11 @@ public class BlockWidget extends Widget implements DoubleClickHandler { public static final Color BACKGROUND_COLOR = new Color(235, 235, 255); private static final Font TITLE_FONT = new Font("Arial", Font.BOLD, 14); public static final Color TITLE_COLOR = new Color(42, 42, 171); - private final InputBlock blockNode; + private final Block block; - public BlockWidget(Scene scene, InputBlock blockNode) { + public BlockWidget(Scene scene, Block block) { super(scene); - this.blockNode = blockNode; + this.block = block; this.setBackground(BACKGROUND_COLOR); this.setOpaque(true); this.setCheckClipping(true); @@ -71,7 +72,7 @@ protected void paintWidget() { g.setColor(TITLE_COLOR); g.setFont(TITLE_FONT); - String s = "B" + blockNode.getName(); + String s = "B" + getBlockNode().getName(); Rectangle2D r1 = g.getFontMetrics().getStringBounds(s, g); g.drawString(s, r.x + 5, r.y + (int) r1.getHeight()); g.setStroke(old); @@ -83,10 +84,18 @@ private void addToSelection(BlockWidget blockWidget, boolean additiveSelection) if (!additiveSelection) { graphProvider.clearSelectedNodes(); } - graphProvider.addSelectedNodes(blockWidget.blockNode.getNodes(), false); + graphProvider.addSelectedNodes(blockWidget.getBlockNode().getNodes(), false); } } + public void updatePosition() { + setPreferredLocation(block.getPosition()); + } + + public InputBlock getBlockNode() { + return block.getInputBlock(); + } + private int getModifierMask () { return Utilities.isMac() ? MouseEvent.META_DOWN_MASK : MouseEvent.CTRL_DOWN_MASK; } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java index bbdf08dc8b8..6b9b8e548d1 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java @@ -26,12 +26,15 @@ import com.sun.hotspot.igv.data.Properties; import com.sun.hotspot.igv.graph.Diagram; import com.sun.hotspot.igv.graph.Figure; +import com.sun.hotspot.igv.graph.Slot; import com.sun.hotspot.igv.util.DoubleClickAction; import com.sun.hotspot.igv.util.DoubleClickHandler; import com.sun.hotspot.igv.util.PropertiesConverter; import com.sun.hotspot.igv.util.PropertiesSheet; import com.sun.hotspot.igv.view.DiagramScene; import java.awt.*; +import java.awt.geom.Path2D; +import java.awt.geom.RoundRectangle2D; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -39,6 +42,8 @@ import javax.swing.BorderFactory; import javax.swing.JMenu; import javax.swing.JPopupMenu; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import org.netbeans.api.visual.action.PopupMenuProvider; @@ -62,13 +67,11 @@ public class FigureWidget extends Widget implements Properties.Provider, PopupMenuProvider, DoubleClickHandler { private static final double LABEL_ZOOM_FACTOR = 0.3; - private Figure figure; - private Widget middleWidget; - private ArrayList labelWidgets; - private DiagramScene diagramScene; + private final Figure figure; + private final Widget middleWidget; + private final ArrayList labelWidgets; + private final DiagramScene diagramScene; private boolean boundary; - private final Node node; - private Widget dummyTop; private static final Image warningSign = ImageUtilities.loadImage("com/sun/hotspot/igv/view/images/warning.png"); public void setBoundary(boolean b) { @@ -117,34 +120,20 @@ public FigureWidget(final Figure f, DiagramScene scene) { this.setCheckClipping(true); this.diagramScene = scene; - Widget outer = new Widget(scene); - outer.setBackground(f.getColor()); - outer.setLayout(LayoutFactory.createOverlayLayout()); - middleWidget = new Widget(scene); - SerialAlignment textAlign = scene.getModel().getShowCFG() ? - LayoutFactory.SerialAlignment.LEFT_TOP : - LayoutFactory.SerialAlignment.CENTER; - middleWidget.setLayout(LayoutFactory.createVerticalFlowLayout(textAlign, 0)); + middleWidget.setPreferredBounds(new Rectangle(0, 0, f.getWidth(), f.getHeight())); + middleWidget.setLayout(LayoutFactory.createHorizontalFlowLayout(SerialAlignment.CENTER, 0)); middleWidget.setOpaque(true); middleWidget.getActions().addAction(new DoubleClickAction(this)); middleWidget.setCheckClipping(false); - - dummyTop = new Widget(scene); - int extraTopHeight = - getFigure().getDiagram().isCFG() && getFigure().hasNamedInputSlot() ? - Figure.TOP_CFG_HEIGHT : 0; - dummyTop.setMinimumSize(new Dimension(Figure.INSET / 2, 1 + extraTopHeight)); - middleWidget.addChild(dummyTop); - - // This widget includes the node text and possibly a warning sign to the right. - Widget nodeInfoWidget = new Widget(scene); - nodeInfoWidget.setLayout(LayoutFactory.createAbsoluteLayout()); - middleWidget.addChild(nodeInfoWidget); + this.addChild(middleWidget); Widget textWidget = new Widget(scene); + SerialAlignment textAlign = scene.getModel().getShowCFG() ? + LayoutFactory.SerialAlignment.LEFT_TOP : + LayoutFactory.SerialAlignment.CENTER; textWidget.setLayout(LayoutFactory.createVerticalFlowLayout(textAlign, 0)); - nodeInfoWidget.addChild(textWidget); + middleWidget.addChild(textWidget); String[] strings = figure.getLines(); labelWidgets = new ArrayList<>(strings.length); @@ -157,32 +146,32 @@ public FigureWidget(final Figure f, DiagramScene scene) { lw.setFont(Diagram.FONT); lw.setAlignment(LabelWidget.Alignment.CENTER); lw.setVerticalAlignment(LabelWidget.VerticalAlignment.CENTER); - lw.setBorder(BorderFactory.createEmptyBorder()); lw.setCheckClipping(false); } formatExtraLabel(false); refreshColor(); + for (int i=1; i < labelWidgets.size(); i++) { + labelWidgets.get(i).setFont(Diagram.FONT.deriveFont(Font.ITALIC)); + labelWidgets.get(i).setForeground(Color.DARK_GRAY); + } + + + int textHeight = f.getHeight() - 2 * Figure.PADDING - f.getSlotsHeight(); if (getFigure().getWarning() != null) { ImageWidget warningWidget = new ImageWidget(scene, warningSign); - Point warningLocation = new Point(getFigure().getWidth() - Figure.WARNING_WIDTH - Figure.INSET / 2, 0); - warningWidget.setPreferredLocation(warningLocation); warningWidget.setToolTipText(getFigure().getWarning()); - nodeInfoWidget.addChild(warningWidget); + middleWidget.addChild(warningWidget); + int textWidth = f.getWidth() - 4 * Figure.BORDER; + textWidth -= Figure.WARNING_WIDTH + Figure.PADDING; + textWidget.setPreferredBounds(new Rectangle(0, 0, textWidth, textHeight)); + } else { + int textWidth = f.getWidth() - 4 * Figure.BORDER; + textWidget.setPreferredBounds(new Rectangle(0, 0, textWidth, textHeight)); } - Widget dummyBottom = new Widget(scene); - int extraBottomHeight = - getFigure().getDiagram().isCFG() && getFigure().hasNamedOutputSlot() ? - Figure.BOTTOM_CFG_HEIGHT : 0; - dummyBottom.setMinimumSize(new Dimension(Figure.INSET / 2, 1 + extraBottomHeight)); - middleWidget.addChild(dummyBottom); - - middleWidget.setPreferredBounds(new Rectangle(0, Figure.getVerticalOffset(), f.getWidth(), f.getHeight())); - this.addChild(middleWidget); - // Initialize node for property sheet - node = new AbstractNode(Children.LEAF) { + Node node = new AbstractNode(Children.LEAF) { @Override protected Sheet createSheet() { @@ -196,6 +185,38 @@ protected Sheet createSheet() { this.setToolTipText(PropertiesConverter.convertToHTML(f.getProperties())); } + public int getFigureHeight() { + return middleWidget.getPreferredBounds().height; + } + + public static class RoundedBorder extends LineBorder { + + final float RADIUS = 3f; + + public RoundedBorder(Color color, int thickness) { + super(color, thickness); + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if ((this.thickness > 0) && (g instanceof Graphics2D)) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Color oldColor = g2d.getColor(); + g2d.setColor(this.lineColor); + int offs = this.thickness; + int size = offs + offs; + Shape outer = new RoundRectangle2D.Float(x, y, width, height, RADIUS, RADIUS); + Shape inner = new RoundRectangle2D.Float(x + offs, y + offs, width - size, height - size, RADIUS, RADIUS); + Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD); + path.append(outer, false); + path.append(inner, false); + g2d.fill(path); + g2d.setColor(oldColor); + } + } + } + public void refreshColor() { middleWidget.setBackground(figure.getColor()); for (LabelWidget lw : labelWidgets) { @@ -208,19 +229,22 @@ protected void notifyStateChanged(ObjectState previousState, ObjectState state) super.notifyStateChanged(previousState, state); Font font = Diagram.FONT; - int thickness = 1; + Color borderColor = Color.BLACK; + Color innerBorderColor = getFigure().getColor(); if (state.isSelected()) { font = Diagram.BOLD_FONT; - thickness = 2; + innerBorderColor = Color.BLACK; } - Color borderColor = Color.BLACK; - Color innerBorderColor = getFigure().getColor(); if (state.isHighlighted()) { innerBorderColor = borderColor = Color.BLUE; } - middleWidget.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(borderColor, thickness), BorderFactory.createLineBorder(innerBorderColor, 1))); + Border innerBorder = new RoundedBorder(borderColor, Figure.BORDER); + Border outerBorder = new RoundedBorder(innerBorderColor, Figure.BORDER); + Border roundedBorder = BorderFactory.createCompoundBorder(innerBorder, outerBorder); + middleWidget.setBorder(roundedBorder); + for (LabelWidget labelWidget : labelWidgets) { labelWidget.setFont(font); } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/InputSlotWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/InputSlotWidget.java index 42bc7bb099f..b9b8c34b938 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/InputSlotWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/InputSlotWidget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -33,7 +33,6 @@ import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; -import java.util.List; import org.netbeans.api.visual.widget.Widget; /** @@ -45,8 +44,8 @@ public class InputSlotWidget extends SlotWidget { private InputSlot inputSlot; private DiagramScene scene; - public InputSlotWidget(InputSlot slot, DiagramScene scene, Widget parent, FigureWidget fw) { - super(slot, scene, parent, fw); + public InputSlotWidget(InputSlot slot, DiagramScene scene, FigureWidget fw) { + super(slot, scene, fw); inputSlot = slot; this.scene = scene; } @@ -55,17 +54,10 @@ public InputSlot getInputSlot() { return inputSlot; } - @Override - protected int calculateSlotWidth() { - List slots = getSlot().getFigure().getInputSlots(); - assert slots.contains(getSlot()); - return calculateWidth(slots.size()); - } - @Override protected int yOffset() { return getFigureWidget().getFigure().getDiagram().isCFG() ? - calculateClientArea().height - 1 : Figure.SLOT_START; + calculateClientArea().height / 2 : 0; } @Override diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java index 61b71453915..1a0448c380c 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -33,10 +33,8 @@ import com.sun.hotspot.igv.view.actions.CustomSelectAction; import java.awt.*; import java.awt.geom.Line2D; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.*; import java.util.List; -import java.util.Set; import javax.swing.JPopupMenu; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; @@ -61,22 +59,23 @@ public class LineWidget extends Widget implements PopupMenuProvider { private static final double ZOOM_FACTOR = 0.1; private final OutputSlot outputSlot; private final DiagramScene scene; - private final List connections; + private final List connections; private final Point from; private final Point to; - private final Rectangle clientArea; + private Rectangle clientArea; private final LineWidget predecessor; private final List successors; private boolean highlighted; private boolean popupVisible; private final boolean isBold; private final boolean isDashed; + private boolean needToInitToolTipText = true; - public LineWidget(DiagramScene scene, OutputSlot s, List connections, Point from, Point to, LineWidget predecessor, boolean isBold, boolean isDashed) { + public LineWidget(DiagramScene scene, OutputSlot s, List connections, Point from, Point to, LineWidget predecessor, boolean isBold, boolean isDashed) { super(scene); this.scene = scene; this.outputSlot = s; - this.connections = connections; + this.connections = Collections.unmodifiableList(connections); this.from = from; this.to = to; this.predecessor = predecessor; @@ -88,32 +87,14 @@ public LineWidget(DiagramScene scene, OutputSlot s, List connections this.isBold = isBold; this.isDashed = isDashed; - int minX = from.x; - int minY = from.y; - int maxX = to.x; - int maxY = to.y; - if (minX > maxX) { - int tmp = minX; - minX = maxX; - maxX = tmp; - } - - if (minY > maxY) { - int tmp = minY; - minY = maxY; - maxY = tmp; - } - - clientArea = new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); - clientArea.grow(BORDER, BORDER); + computeClientArea(); Color color = Color.BLACK; - if (connections.size() > 0) { + if (!connections.isEmpty()) { color = connections.get(0).getColor(); } - setToolTipText("" + generateToolTipText(this.connections) + ""); - setCheckClipping(true); + setCheckClipping(false); getActions().addAction(ActionFactory.createPopupMenuAction(this)); setBackground(color); @@ -144,7 +125,31 @@ public void select(Widget widget, Point localLocation, boolean invertSelection) })); } - private String generateToolTipText(List conn) { + private void computeClientArea() { + int minX = from.x; + int minY = from.y; + int maxX = to.x; + int maxY = to.y; + + // Ensure min and max values are correct + if (minX > maxX) { + int tmp = minX; + minX = maxX; + maxX = tmp; + } + + if (minY > maxY) { + int tmp = minY; + minY = maxY; + maxY = tmp; + } + + // Set client area to include the curve and add a BORDER for extra space + clientArea = new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); + clientArea.grow(BORDER, BORDER); + } + + private String generateToolTipText(List conn) { StringBuilder sb = new StringBuilder(); for (Connection c : conn) { sb.append(StringUtils.escapeHTML(c.getToolTipText())); @@ -176,7 +181,7 @@ protected void paintWidget() { return; } - Graphics2D g = getScene().getGraphics(); + Graphics2D g = this.getGraphics(); g.setPaint(this.getBackground()); float width = 1.0f; @@ -195,7 +200,7 @@ protected void paintWidget() { BasicStroke.JOIN_MITER, 10, dashPattern, 0)); } else { - g.setStroke(new BasicStroke(width)); + g.setStroke(new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER)); } g.drawLine(from.x, from.y, to.x, to.y); @@ -234,6 +239,7 @@ protected void paintWidget() { 3); } g.setStroke(oldStroke); + super.paintWidget(); } private void setPopupVisible(boolean b) { @@ -254,6 +260,10 @@ protected void notifyStateChanged(ObjectState previousState, ObjectState state) setHighlighted(enableHighlighting); recursiveHighlightSuccessors(enableHighlighting); highlightVertices(enableHighlighting); + if (enableHighlighting && needToInitToolTipText) { + setToolTipText("" + generateToolTipText(this.connections) + ""); + needToInitToolTipText = false; // Ensure it's only set once + } } } @@ -350,4 +360,5 @@ public void popupMenuCanceled(PopupMenuEvent e) { return menu; } + } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/OutputSlotWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/OutputSlotWidget.java index f1a03715d22..6f10cb5ef6f 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/OutputSlotWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/OutputSlotWidget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -23,11 +23,8 @@ */ package com.sun.hotspot.igv.view.widgets; -import com.sun.hotspot.igv.graph.Figure; import com.sun.hotspot.igv.graph.OutputSlot; import com.sun.hotspot.igv.view.DiagramScene; -import java.util.List; -import org.netbeans.api.visual.widget.Widget; /** * @@ -37,8 +34,8 @@ public class OutputSlotWidget extends SlotWidget { private OutputSlot outputSlot; - public OutputSlotWidget(OutputSlot slot, DiagramScene scene, Widget parent, FigureWidget fw) { - super(slot, scene, parent, fw); + public OutputSlotWidget(OutputSlot slot, DiagramScene scene, FigureWidget fw) { + super(slot, scene, fw); outputSlot = slot; } @@ -46,18 +43,10 @@ public OutputSlot getOutputSlot() { return outputSlot; } - @Override - protected int calculateSlotWidth() { - List slots = getSlot().getFigure().getOutputSlots(); - assert slots.contains(getSlot()); - return calculateWidth(slots.size()); - - } - @Override protected int yOffset() { int overlap = getFigureWidget().getFigure().getDiagram().isCFG() ? - calculateClientArea().height : Figure.SLOT_START; - return getSlot().getFigure().getHeight() - overlap; + calculateClientArea().height / 2 : 0; + return getFigureWidget().getFigureHeight() - overlap; } } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/SlotWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/SlotWidget.java index f6f7fa6b80b..6f9240e8ba4 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/SlotWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/SlotWidget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -25,7 +25,6 @@ import com.sun.hotspot.igv.graph.Diagram; import com.sun.hotspot.igv.graph.Figure; -import com.sun.hotspot.igv.graph.OutputSlot; import com.sun.hotspot.igv.graph.Slot; import com.sun.hotspot.igv.util.DoubleClickHandler; import com.sun.hotspot.igv.view.DiagramScene; @@ -50,7 +49,7 @@ public abstract class SlotWidget extends Widget implements DoubleClickHandler { protected static double ZOOM_FACTOR = 0.6; private DiagramScene diagramScene; - public SlotWidget(Slot slot, DiagramScene scene, Widget parent, FigureWidget fw) { + public SlotWidget(Slot slot, DiagramScene scene, FigureWidget fw) { super(scene); this.diagramScene = scene; this.slot = slot; @@ -60,12 +59,14 @@ public SlotWidget(Slot slot, DiagramScene scene, Widget parent, FigureWidget fw) } // No clipping, to let input slots draw gap markers outside their bounds. this.setCheckClipping(false); - parent.addChild(this); - - Point p = slot.getRelativePosition(); - p.x -= this.calculateClientArea().width / 2; - p.y += yOffset(); - this.setPreferredLocation(p); + fw.addChild(this); + if (slot.shouldShowName()) { + Point p = slot.getRelativePosition(); + p.x -= slot.getWidth() / 2; + p.y -= slot.getHeight() / 2; + p.y += yOffset(); + this.setPreferredLocation(p); + } } @Override @@ -140,30 +141,18 @@ protected void paintWidget() { g.setColor(Color.BLACK); } int r = 2; - if (slot instanceof OutputSlot) { - g.fillOval(w / 2 - r, Figure.SLOT_WIDTH - Figure.SLOT_START - r, 2 * r, 2 * r); - } else { - g.fillOval(w / 2 - r, Figure.SLOT_START - r, 2 * r, 2 * r); - } - } else { - // Do not paint a slot with connections. + g.fillOval(w / 2 - r, h / 2 - r, 2 * r, 2 * r); } } } @Override protected Rectangle calculateClientArea() { - return new Rectangle(0, 0, slot.getWidth(), Figure.SLOT_WIDTH); + return new Rectangle(0, 0, slot.getWidth(), slot.getHeight()); } - protected abstract int calculateSlotWidth(); - protected abstract int yOffset(); - protected int calculateWidth(int count) { - return getFigureWidget().getFigure().getWidth() / count; - } - @Override public void handleDoubleClick(Widget w, WidgetAction.WidgetMouseEvent e) { Set hiddenNodes = new HashSet<>(diagramScene.getModel().getHiddenNodes()); diff --git a/src/utils/IdealGraphVisualizer/igv.sh b/src/utils/IdealGraphVisualizer/igv.sh index 4d931e2815c..19f9383b0e7 100644 --- a/src/utils/IdealGraphVisualizer/igv.sh +++ b/src/utils/IdealGraphVisualizer/igv.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 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 @@ -20,4 +20,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. -mvn --batch-mode -f application/pom.xml nbm:run-platform >.igv.log 2>&1 +mvn --batch-mode -f application/pom.xml nbm:run-platform -Dnetbeans.run.params="-J-da" >.igv.log 2>&1 From 28b0f3eaa55a1718e8e725516e64c8e25734f97b Mon Sep 17 00:00:00 2001 From: Tobias Holenstein Date: Fri, 29 Nov 2024 15:16:53 +0000 Subject: [PATCH 026/171] 8343705: IGV: Interactive Node Moving in Hierarchical Layout Reviewed-by: chagedorn, thartmann, rcastanedalo --- .../HierarchicalLayoutManager.java | 58 +- .../igv/hierarchicallayout/LayoutEdge.java | 18 + .../igv/hierarchicallayout/LayoutGraph.java | 696 +++++++++++++++++- .../igv/hierarchicallayout/LayoutLayer.java | 109 ++- .../igv/hierarchicallayout/LayoutMover.java | 53 ++ .../igv/hierarchicallayout/LayoutNode.java | 81 +- .../sun/hotspot/igv/view/DiagramScene.java | 268 ++++++- .../igv/view/widgets/FigureWidget.java | 4 + .../hotspot/igv/view/widgets/LineWidget.java | 26 +- 9 files changed, 1298 insertions(+), 15 deletions(-) create mode 100644 src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java index 477cdce0c5b..e2d6bf18039 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/HierarchicalLayoutManager.java @@ -33,7 +33,7 @@ * * @author Thomas Wuerthinger */ -public class HierarchicalLayoutManager extends LayoutManager { +public class HierarchicalLayoutManager extends LayoutManager implements LayoutMover { int maxLayerLength; private LayoutGraph graph; @@ -77,6 +77,62 @@ public void doLayout(LayoutGraph layoutGraph) { graph = layoutGraph; } + @Override + public void moveLink(Point linkPos, int shiftX) { + int layerNr = graph.findLayer(linkPos.y); + for (LayoutNode node : graph.getLayer(layerNr)) { + if (node.isDummy() && linkPos.x == node.getX()) { + LayoutLayer layer = graph.getLayer(layerNr); + if (layer.contains(node)) { + node.setX(linkPos.x + shiftX); + layer.sortNodesByX(); + break; + } + } + } + writeBack(); + } + + @Override + public void moveVertices(Set movedVertices) { + for (Vertex vertex : movedVertices) { + moveVertex(vertex); + } + writeBack(); + } + + private void writeBack() { + graph.optimizeBackEdgeCrossings(); + graph.updateLayerMinXSpacing(); + graph.straightenEdges(); + WriteResult.apply(graph); + } + + @Override + public void moveVertex(Vertex movedVertex) { + Point newLoc = movedVertex.getPosition(); + LayoutNode movedNode = graph.getLayoutNode(movedVertex); + assert !movedNode.isDummy(); + + int layerNr = graph.findLayer(newLoc.y + movedNode.getOuterHeight() / 2); + if (movedNode.getLayer() == layerNr) { // we move the node in the same layer + LayoutLayer layer = graph.getLayer(layerNr); + if (layer.contains(movedNode)) { + movedNode.setX(newLoc.x); + layer.sortNodesByX(); + } + } else { // only remove edges if we moved the node to a new layer + if (maxLayerLength > 0) return; // TODO: not implemented + graph.removeNodeAndEdges(movedNode); + layerNr = graph.insertNewLayerIfNeeded(movedNode, layerNr); + graph.addNodeToLayer(movedNode, layerNr); + movedNode.setX(newLoc.x); + graph.getLayer(layerNr).sortNodesByX(); + graph.removeEmptyLayers(); + graph.addEdges(movedNode, maxLayerLength); + } + } + /** * Removes self-edges from nodes in the graph. If self-edges are to be included in the layout * (`layoutSelfEdges` is true), it stores them in the node for later processing and marks the graph diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java index f45e6268e2c..9c2806bab6d 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutEdge.java @@ -169,6 +169,24 @@ public void setTo(LayoutNode to) { this.to = to; } + /** + * Gets the absolute x-coordinate of the source node's connection point for this edge. + * + * @return The x-coordinate of the source node's connection point. + */ + public int getFromX() { + return from.getX() + getRelativeFromX(); + } + + /** + * Gets the absolute x-coordinate of the target node's connection point for this edge. + * + * @return The x-coordinate of the target node's connection point. + */ + public int getToX() { + return to.getX() + getRelativeToX(); + } + /** * Gets the relative horizontal position from the source node's left boundary to the edge's starting point. * diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java index bd72616c2e6..64d96412467 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutGraph.java @@ -23,6 +23,7 @@ */ package com.sun.hotspot.igv.hierarchicallayout; +import static com.sun.hotspot.igv.hierarchicallayout.LayoutNode.NODE_POS_COMPARATOR; import com.sun.hotspot.igv.layout.Link; import com.sun.hotspot.igv.layout.Port; import com.sun.hotspot.igv.layout.Vertex; @@ -52,6 +53,8 @@ public class LayoutGraph { private final Set links; private final SortedSet vertices; private final LinkedHashMap> inputPorts; + private final LinkedHashMap> outputPorts; + private final LinkedHashMap> portLinks; // Layout Management: LayoutNodes and LayoutLayers private final LinkedHashMap layoutNodes; @@ -69,9 +72,9 @@ public class LayoutGraph { public LayoutGraph(Collection links, Collection additionalVertices) { this.links = new HashSet<>(links); vertices = new TreeSet<>(additionalVertices); - LinkedHashMap> portLinks = new LinkedHashMap<>(links.size()); + portLinks = new LinkedHashMap<>(links.size()); inputPorts = new LinkedHashMap<>(links.size()); - LinkedHashMap> outputPorts = new LinkedHashMap<>(links.size()); + outputPorts = new LinkedHashMap<>(links.size()); for (Link link : links) { assert link.getFrom() != null; @@ -175,6 +178,114 @@ public List getAllNodes() { return Collections.unmodifiableList(allNodes); } + /** + * Creates a new layer at the specified index in the layers list. + * Adjusts the layer numbers of existing nodes in layers below the inserted layer. + * + * @param layerNr The index at which to insert the new layer. + * @return The newly created LayoutLayer. + */ + private LayoutLayer createNewLayer(int layerNr) { + LayoutLayer layer = new LayoutLayer(); + layers.add(layerNr, layer); + + // update layer field in nodes below layerNr + for (int l = layerNr + 1; l < getLayerCount(); l++) { + for (LayoutNode layoutNode : getLayer(l)) { + layoutNode.setLayer(l); + } + } + return layer; + } + + /** + * Deletes the layer at the specified index. + * Adjusts the layer numbers of existing nodes in layers below the deleted layer. + * + * @param layerNr The index of the layer to delete. + */ + private void deleteLayer(int layerNr) { + layers.remove(layerNr); + + // Update the layer field in nodes below the deleted layer + for (int l = layerNr; l < getLayerCount(); l++) { + for (LayoutNode layoutNode : getLayer(l)) { + layoutNode.setLayer(l); + } + } + } + + /** + * Ensures that no neighboring nodes of the specified node are in the same layer. + * If any neighbor is found in the specified layer, inserts a new layer to avoid conflicts. + * Returns the adjusted layer number where the node can be safely inserted. + * + * @param node The LayoutNode to check and possibly reposition. + * @param layerNr The proposed layer number for the node. + * @return The layer number where the node can be safely inserted after adjustments. + */ + public int insertNewLayerIfNeeded(LayoutNode node, int layerNr) { + for (Link inputLink : getInputLinks(node.getVertex())) { + if (inputLink.getFrom().getVertex() == inputLink.getTo().getVertex()) continue; + LayoutNode fromNode = getLayoutNode(inputLink.getFrom().getVertex()); + if (fromNode.getLayer() == layerNr) { + moveExpandLayerDown(layerNr + 1); + return layerNr + 1; + } + } + for (Link outputLink : getOutputLinks(node.getVertex())) { + if (outputLink.getFrom().getVertex() == outputLink.getTo().getVertex()) continue; + LayoutNode toNode = getLayoutNode(outputLink.getTo().getVertex()); + if (toNode.getLayer() == layerNr) { + moveExpandLayerDown(layerNr); + return layerNr; + } + } + return layerNr; + + } + + /** + * Inserts a new layer at the specified index and adjusts nodes and edges accordingly. + * Moves existing nodes and their successors down to accommodate the new layer. + * + * @param layerNr The index at which to insert the new layer. + */ + private void moveExpandLayerDown(int layerNr) { + LayoutLayer newLayer = createNewLayer(layerNr); + + if (layerNr == 0) return; + LayoutLayer layerAbove = getLayer(layerNr - 1); + + for (LayoutNode fromNode : layerAbove) { + int fromX = fromNode.getX(); + Map> successorsByX = fromNode.groupSuccessorsByX(); + fromNode.clearSuccessors(); + + for (Map.Entry> entry : successorsByX.entrySet()) { + Integer relativeFromX = entry.getKey(); + List edges = entry.getValue(); + LayoutNode dummyNode = new LayoutNode(); + dummyNode.setX(fromX + relativeFromX); + dummyNode.setLayer(layerNr); + for (LayoutEdge edge : edges) { + dummyNode.addSuccessor(edge); + } + LayoutEdge dummyEdge = new LayoutEdge(fromNode, dummyNode, relativeFromX, 0, edges.get(0).getLink()); + if (edges.get(0).isReversed()) dummyEdge.reverse(); + + fromNode.addSuccessor(dummyEdge); + dummyNode.addPredecessor(dummyEdge); + for (LayoutEdge edge : edges) { + edge.setFrom(dummyNode); + } + addDummyToLayer(dummyNode, layerNr); + } + } + + newLayer.sortNodesByX(); + } + /** * Retrieves an unmodifiable list of all layers in the graph. * @@ -193,6 +304,31 @@ public int getLayerCount() { return layers.size(); } + /** + * Retrieves the LayoutNode associated with the specified Vertex. + * + * @param vertex The vertex whose LayoutNode is to be retrieved. + * @return The LayoutNode corresponding to the given vertex, or null if not found. + */ + public LayoutNode getLayoutNode(Vertex vertex) { + return layoutNodes.get(vertex); + } + + /** + * Adds a LayoutNode to the specified layer and registers it in the graph. + * + * @param node The LayoutNode to add to the layer. + * @param layerNumber The index of the layer to which the node will be added. + */ + public void addNodeToLayer(LayoutNode node, int layerNumber) { + assert !node.isDummy(); + node.setLayer(layerNumber); + getLayer(layerNumber).add(node); + if (!layoutNodes.containsKey(node.getVertex())) { + layoutNodes.put(node.getVertex(), node); + } + } + /** * Adds a LayoutNode to the specified layer and registers it in the graph. * @@ -283,6 +419,148 @@ public Set findRootVertices() { .collect(Collectors.toSet()); } + /** + * Retrieves all incoming links to the specified vertex. + * + * @param vertex The vertex whose incoming links are to be retrieved. + * @return A set of links that are incoming to the vertex. + */ + public List getInputLinks(Vertex vertex) { + List inputLinks = new ArrayList<>(); + for (Port inputPort : inputPorts.getOrDefault(vertex, Collections.emptySet())) { + inputLinks.addAll(portLinks.getOrDefault(inputPort, Collections.emptySet())); + } + return inputLinks; + } + + /** + * Retrieves all outgoing links from the specified vertex. + * + * @param vertex The vertex whose outgoing links are to be retrieved. + * @return A set of links that are outgoing from the vertex. + */ + public List getOutputLinks(Vertex vertex) { + List outputLinks = new ArrayList<>(); + for (Port outputPort : outputPorts.getOrDefault(vertex, Collections.emptySet())) { + outputLinks.addAll(portLinks.getOrDefault(outputPort, Collections.emptySet())); + } + return outputLinks; + } + + public List getAllLinks(Vertex vertex) { + List allLinks = new ArrayList<>(); + + for (Port inputPort : inputPorts.getOrDefault(vertex, Collections.emptySet())) { + allLinks.addAll(portLinks.getOrDefault(inputPort, Collections.emptySet())); + } + + for (Port outputPort : outputPorts.getOrDefault(vertex, Collections.emptySet())) { + allLinks.addAll(portLinks.getOrDefault(outputPort, Collections.emptySet())); + } + + return allLinks; + } + + /** + * Removes the specified LayoutNode and all its connected edges from the graph. + * + * @param node The LayoutNode to remove along with its edges. + */ + public void removeNodeAndEdges(LayoutNode node) { + assert !node.isDummy(); + removeEdges(node); // a node can only be removed together with its edges + int layer = node.getLayer(); + layers.get(layer).remove(node); + layers.get(layer).updateNodeIndices(); + layoutNodes.remove(node.getVertex()); + } + + /** + * Removes all edges connected to the specified LayoutNode. + * Handles the removal of associated dummy nodes if they are no longer needed. + * Updates the graph structure accordingly after node movement. + * + * @param node The LayoutNode whose connected edges are to be removed. + */ + public void removeEdges(LayoutNode node) { + assert !node.isDummy(); + for (Link link : getAllLinks(node.getVertex())) { + removeEdge(link); + } + } + + public void removeEdge(Link link) { + Vertex from = link.getFrom().getVertex(); + Vertex to = link.getTo().getVertex(); + LayoutNode toNode = getLayoutNode(to); + LayoutNode fromNode = getLayoutNode(from); + + if (toNode.getLayer() < fromNode.getLayer()) { + // Reversed edge + toNode = fromNode; + } + + // Remove preds-edges bottom up, starting at "to" node + // Cannot start from "from" node since there might be joint edges + List toNodePredsEdges = List.copyOf(toNode.getPredecessors()); + for (LayoutEdge edge : toNodePredsEdges) { + LayoutNode predNode = edge.getFrom(); + LayoutEdge edgeToRemove; + + if (edge.getLink() != null && edge.getLink().equals(link)) { + toNode.removePredecessor(edge); + edgeToRemove = edge; + } else { + // Wrong edge, look at next + continue; + } + + if (!predNode.isDummy() && predNode.getVertex().equals(from)) { + // No dummy nodes inbetween 'from' and 'to' vertex + predNode.removeSuccessor(edgeToRemove); + break; + } else { + // Must remove edges between dummy nodes + boolean found = true; + LayoutNode succNode = toNode; + while (predNode.isDummy() && found) { + found = false; + + if (predNode.getSuccessors().size() <= 1 && predNode.getPredecessors().size() <= 1) { + // Dummy node used only for this link, remove if not already removed + assert predNode.isDummy(); + int layer = predNode.getLayer(); + layers.get(layer).remove(predNode); + layers.get(layer).updateNodeIndices(); + dummyNodes.remove(predNode); + } else { + // anchor node, should not be removed + break; + } + + if (predNode.getPredecessors().size() == 1) { + predNode.removeSuccessor(edgeToRemove); + succNode = predNode; + edgeToRemove = predNode.getPredecessors().get(0); + predNode = edgeToRemove.getFrom(); + found = true; + } + } + + predNode.removeSuccessor(edgeToRemove); + succNode.removePredecessor(edgeToRemove); + } + break; + } + + if (fromNode.getReversedLinkStartPoints().containsKey(link)) { + fromNode.computeReversedLinkPoints(false); + } + if (toNode.getReversedLinkStartPoints().containsKey(link)) { + toNode.computeReversedLinkPoints(false); + } + } + /** * Retrieves the LayoutLayer at the specified index. * @@ -293,6 +571,30 @@ public LayoutLayer getLayer(int layerNr) { return layers.get(layerNr); } + /** + * Finds the layer closest to the given y-coordinate. + * + * @param y the y-coordinate to check + * @return the index of the optimal layer, or -1 if no layers are found + */ + public int findLayer(int y) { + int optimalLayer = -1; + int minDistance = Integer.MAX_VALUE; + for (int l = 0; l < getLayerCount(); l++) { + // Check if y is within this layer's bounds + if (y >= getLayer(l).getTop() && y <= getLayer(l).getBottom()) { + return l; + } + + int distance = Math.abs(getLayer(l).getCenter() - y); + if (distance < minDistance) { + minDistance = distance; + optimalLayer = l; + } + } + return optimalLayer; + } + /** * Positions the layers vertically, calculating their heights and setting their positions. * Centers the nodes within each layer vertically. @@ -314,6 +616,380 @@ public void positionLayers() { } } + /** + * Optimizes routing of reversed (back) edges to reduce crossings. + */ + public void optimizeBackEdgeCrossings() { + for (LayoutNode node : getLayoutNodes()) { + node.optimizeBackEdgeCrossing(); + } + } + + /** + * Removes empty layers from the graph. + * Iteratively checks for and removes layers that contain only dummy nodes. + */ + public void removeEmptyLayers() { + int i = 0; + while (i < getLayerCount()) { + LayoutLayer layer = getLayer(i); + if (layer.containsOnlyDummyNodes()) { + removeEmptyLayer(i); + } else { + i++; // Move to the next layer only if no removal occurred + } + } + } + + /** + * Removes the layer at the specified index if it is empty or contains only dummy nodes. + * Adjusts the positions of nodes and edges accordingly. + * + * @param layerNr The index of the layer to remove. + */ + private void removeEmptyLayer(int layerNr) { + LayoutLayer layer = getLayer(layerNr); + if (!layer.containsOnlyDummyNodes()) return; + + for (LayoutNode dummyNode : layer) { + if (dummyNode.getSuccessors().isEmpty()) { + dummyNode.setLayer(layerNr + 1); + getLayer(layerNr + 1).add(dummyNode); + dummyNode.setX(dummyNode.calculateOptimalXFromPredecessors(true)); + getLayer(layerNr + 1).sortNodesByX(); + continue; + } else if (dummyNode.getPredecessors().isEmpty()) { + dummyNode.setLayer(layerNr - 1); + dummyNode.setX(dummyNode.calculateOptimalXFromSuccessors(true)); + getLayer(layerNr - 1).add(dummyNode); + getLayer(layerNr - 1).sortNodesByX(); + continue; + } + LayoutEdge layoutEdge = dummyNode.getPredecessors().get(0); + + // remove the layoutEdge + LayoutNode fromNode = layoutEdge.getFrom(); + fromNode.removeSuccessor(layoutEdge); + + List successorEdges = dummyNode.getSuccessors(); + for (LayoutEdge successorEdge : successorEdges) { + successorEdge.setRelativeFromX(layoutEdge.getRelativeFromX()); + successorEdge.setFrom(fromNode); + fromNode.addSuccessor(successorEdge); + } + dummyNode.clearPredecessors(); + dummyNode.clearSuccessors(); + dummyNodes.remove(dummyNode); + } + + deleteLayer(layerNr); + } + + /** + * Repositions the specified LayoutNode horizontally within its layer to the new x-coordinate. + * Ensures no overlap with adjacent nodes and maintains minimum spacing. + * + * @param layoutNode The LayoutNode to reposition. + * @param newX The new x-coordinate to set for the node. + */ + private void repositionLayoutNodeX(LayoutNode layoutNode, int newX) { + int currentX = layoutNode.getX(); + + // Early exit if the desired position is the same as the current position + if (newX == currentX) { + return; + } + + LayoutLayer layer = getLayer(layoutNode.getLayer()); + if (newX > currentX) { + layer.tryShiftNodeRight(layoutNode, newX); + } else { + layer.tryShiftNodeLeft(layoutNode, newX); + } + } + + /** + * Aligns the x-coordinate of a single dummy successor node for the given LayoutNode. + * If the node has exactly one successor and that successor is a dummy node, + * sets the dummy node's x-coordinate to align with the current node or the edge's starting point. + * + * @param node The LayoutNode whose dummy successor is to be aligned. + */ + private void alignSingleSuccessorDummyNodeX(LayoutNode node) { + // Retrieve the list of successor edges + List successors = node.getSuccessors(); + + // Proceed only if there is exactly one successor + if (successors.size() != 1) { + return; + } + + LayoutEdge successorEdge = successors.get(0); + LayoutNode successorNode = successorEdge.getTo(); + + // Proceed only if the successor node is a dummy node + if (!successorNode.isDummy()) { + return; + } + + // Determine the target x-coordinate based on whether the current node is a dummy + int targetX = node.isDummy() ? node.getX() : successorEdge.getStartX(); + + // Align the successor dummy node to the target x-coordinate + repositionLayoutNodeX(successorNode, targetX); + } + + /** + * Aligns the x-coordinates of dummy successor nodes within the specified layer. + * Performs alignment in both forward and backward directions to ensure consistency. + * + * @param layer The LayoutLayer whose nodes' dummy successors need alignment. + */ + private void alignLayerDummySuccessors(LayoutLayer layer) { + // Forward pass: Align dummy successors from the first node to the last. + for (LayoutNode node : layer) { + alignSingleSuccessorDummyNodeX(node); + } + + // Backward pass: Align dummy successors from the last node to the first. + for (int i = layer.size() - 1; i >= 0; i--) { + LayoutNode node = layer.get(i); + alignSingleSuccessorDummyNodeX(node); + } + } + + /** + * Straightens edges in the graph by aligning dummy nodes to reduce bends. + * Processes all layers to align dummy successor nodes. + */ + public void straightenEdges() { + // Forward pass: Align dummy successors from the first layer to the last. + for (int i = 0; i < getLayerCount(); i++) { + alignLayerDummySuccessors(getLayer(i)); + } + + // Backward pass: Align dummy successors from the last layer to the first. + for (int i = getLayerCount() - 1; i >= 0; i--) { + alignLayerDummySuccessors(getLayer(i)); + } + } + + /** + * Updates the minimum X spacing for all layers in the graph. + */ + public void updateLayerMinXSpacing() { + for (LayoutLayer layer : this.getLayers()) { + layer.updateMinXSpacing(false); + } + } + + /** + * Calculates the optimal horizontal position (index) for the specified node within the given layer, + * aiming to minimize the number of edge crossings. + * + * @param node The node to position. + * @param layerNr The index of the layer in which to position the node. + * @return The optimal position index within the layer for the node. + */ + private int optimalPosition(LayoutNode node, int layerNr) { + getLayer(layerNr).sort(NODE_POS_COMPARATOR); + int edgeCrossings = Integer.MAX_VALUE; + int optimalPos = -1; + + // Try each possible position in the layerNr + for (int i = 0; i < getLayer(layerNr).size() + 1; i++) { + int xCoord; + if (i == 0) { + xCoord = getLayer(layerNr).get(i).getX() - node.getWidth() - 1; + } else { + xCoord = getLayer(layerNr).get(i - 1).getX() + getLayer(layerNr).get(i - 1).getWidth() + 1; + } + + int currentCrossings = 0; + + if (0 <= layerNr - 1) { + // For each link with an end point in vertex, check how many edges cross it + for (LayoutEdge edge : node.getPredecessors()) { + if (edge.getFrom().getLayer() == layerNr - 1) { + int fromNodeXCoord = edge.getFromX(); + int toNodeXCoord = xCoord; + if (!node.isDummy()) { + toNodeXCoord += edge.getRelativeToX(); + } + for (LayoutNode n : getLayer(layerNr - 1)) { + for (LayoutEdge e : n.getSuccessors()) { + if (e.getTo() == null) { + continue; + } + int compFromXCoord = e.getFromX(); + int compToXCoord = e.getToX(); + if ((fromNodeXCoord > compFromXCoord && toNodeXCoord < compToXCoord) + || (fromNodeXCoord < compFromXCoord + && toNodeXCoord > compToXCoord)) { + currentCrossings += 1; + } + } + } + } + } + } + // Edge crossings across current layerNr and layerNr below + if (layerNr + 1 < getLayerCount()) { + // For each link with an end point in vertex, check how many edges cross it + for (LayoutEdge edge : node.getSuccessors()) { + if (edge.getTo().getLayer() == layerNr + 1) { + int toNodeXCoord = edge.getToX(); + int fromNodeXCoord = xCoord; + if (!node.isDummy()) { + fromNodeXCoord += edge.getRelativeFromX(); + } + for (LayoutNode n : getLayer(layerNr + 1)) { + for (LayoutEdge e : n.getPredecessors()) { + if (e.getFrom() == null) { + continue; + } + int compFromXCoord = e.getFromX(); + int compToXCoord = e.getToX(); + if ((fromNodeXCoord > compFromXCoord && toNodeXCoord < compToXCoord) + || (fromNodeXCoord < compFromXCoord + && toNodeXCoord > compToXCoord)) { + currentCrossings += 1; + } + } + } + } + } + } + if (currentCrossings <= edgeCrossings) { + edgeCrossings = currentCrossings; + optimalPos = i; + } + } + return optimalPos; + } + + /** + * Creates layout edges for the specified node and reverses edges as needed. + * Reverses edges that go from lower to higher layers to maintain proper layering. + * + * @param node The LayoutNode for which to create and reverse edges. + */ + public void createAndReverseLayoutEdges(LayoutNode node) { + List nodeLinks = new ArrayList<>(getInputLinks(node.getVertex())); + nodeLinks.addAll(getOutputLinks(node.getVertex())); + nodeLinks.sort(LINK_COMPARATOR); + + List reversedLayoutNodes = new ArrayList<>(); + for (Link link : nodeLinks) { + if (link.getFrom().getVertex() == link.getTo().getVertex()) continue; + LayoutEdge layoutEdge = createLayoutEdge(link); + + LayoutNode fromNode = layoutEdge.getFrom(); + LayoutNode toNode = layoutEdge.getTo(); + + if (fromNode.getLayer() > toNode.getLayer()) { + HierarchicalLayoutManager.ReverseEdges.reverseEdge(layoutEdge); + reversedLayoutNodes.add(fromNode); + reversedLayoutNodes.add(toNode); + } + } + + // ReverseEdges + for (LayoutNode layoutNode : reversedLayoutNodes) { + layoutNode.computeReversedLinkPoints(false); + } + } + + /** + * Inserts dummy nodes along the edges from predecessors of the specified node, + * for edges that span more than one layer. + * + * @param layoutNode The node for which to create predecessor dummy nodes. + */ + public void createDummiesForNodePredecessor(LayoutNode layoutNode) { + for (LayoutEdge predEdge : layoutNode.getPredecessors()) { + LayoutNode fromNode = predEdge.getFrom(); + LayoutNode toNode = predEdge.getTo(); + if (Math.abs(toNode.getLayer() - fromNode.getLayer()) <= 1) continue; + + boolean hasEdgeFromSamePort = false; + LayoutEdge edgeFromSamePort = new LayoutEdge(fromNode, toNode, predEdge.getLink()); + if (predEdge.isReversed()) edgeFromSamePort.reverse(); + + for (LayoutEdge succEdge : fromNode.getSuccessors()) { + if (succEdge.getRelativeFromX() == predEdge.getRelativeFromX() && succEdge.getTo().isDummy()) { + edgeFromSamePort = succEdge; + hasEdgeFromSamePort = true; + break; + } + } + + if (hasEdgeFromSamePort) { + LayoutEdge curEdge = edgeFromSamePort; + boolean newEdge = true; + while (curEdge.getTo().getLayer() < toNode.getLayer() - 1 && curEdge.getTo().isDummy() && newEdge) { + // Traverse down the chain of dummy nodes linking together the edges originating + // from the same port + newEdge = false; + if (curEdge.getTo().getSuccessors().size() == 1) { + curEdge = curEdge.getTo().getSuccessors().get(0); + newEdge = true; + } else { + for (LayoutEdge e : curEdge.getTo().getSuccessors()) { + if (e.getTo().isDummy()) { + curEdge = e; + newEdge = true; + break; + } + } + } + } + + LayoutNode prevDummy; + if (!curEdge.getTo().isDummy()) { + prevDummy = curEdge.getFrom(); + } else { + prevDummy = curEdge.getTo(); + } + + predEdge.setFrom(prevDummy); + predEdge.setRelativeFromX(prevDummy.getWidth() / 2); + fromNode.removeSuccessor(predEdge); + prevDummy.addSuccessor(predEdge); + } + + LayoutNode layoutNode1 = predEdge.getTo(); + if (predEdge.getTo().getLayer() - 1 > predEdge.getFrom().getLayer()) { + LayoutEdge prevEdge = predEdge; + for (int l = layoutNode1.getLayer() - 1; l > prevEdge.getFrom().getLayer(); l--) { + LayoutNode dummyNode = new LayoutNode(); + dummyNode.addSuccessor(prevEdge); + LayoutEdge result = new LayoutEdge(prevEdge.getFrom(), dummyNode, prevEdge.getRelativeFromX(), 0, prevEdge.getLink()); + if (prevEdge.isReversed()) result.reverse(); + dummyNode.addPredecessor(result); + prevEdge.setRelativeFromX(0); + prevEdge.getFrom().removeSuccessor(prevEdge); + prevEdge.getFrom().addSuccessor(result); + prevEdge.setFrom(dummyNode); + dummyNode.setLayer(l); + List layerNodes = getLayer(l); + if (layerNodes.isEmpty()) { + dummyNode.setPos(0); + } else { + dummyNode.setPos(optimalPosition(dummyNode, l)); + } + for (LayoutNode n : layerNodes) { + if (n.getPos() >= dummyNode.getPos()) { + n.setPos(n.getPos() + 1); + } + } + addDummyToLayer(dummyNode, l); + prevEdge = dummyNode.getPredecessors().get(0); + } + } + } + } + /** * Inserts dummy nodes along the edges to successors of the specified node, * for edges that span more than one layer. @@ -430,4 +1106,20 @@ public void createDummiesForNodeSuccessor(LayoutNode layoutNode, int maxLayerLen } } } + + /** + * Adds edges connected to the specified node, including any necessary dummy nodes. + * Handles edge reversal, dummy node insertion for both predecessors and successors, + * and updates node positions accordingly. + * + * @param node The LayoutNode to which edges will be added. + * @param maxLayerLength The maximum number of layers an edge can span without splitting it + */ + public void addEdges(LayoutNode node, int maxLayerLength) { + assert !node.isDummy(); + createAndReverseLayoutEdges(node); + createDummiesForNodeSuccessor(node, maxLayerLength); + createDummiesForNodePredecessor(node); + updatePositions(); + } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java index 2f87be1587d..34a8e32654b 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutLayer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -154,6 +154,10 @@ public void setTop(int top) { y = top; } + public int getCenter() { + return y + height / 2; + } + /** * Gets the bottom Y-coordinate of this layer. * @@ -181,6 +185,53 @@ public void setHeight(int height) { this.height = height; } + /** + * Checks if this layer contains only dummy nodes. + * + * @return true if all nodes in the layer are dummy nodes; false otherwise. + */ + public boolean containsOnlyDummyNodes() { + for (LayoutNode node : this) { + if (!node.isDummy()) { + return false; + } + } + return true; + } + + /** + * Sorts the nodes in this layer by their X-coordinate in increasing order. + * Assigns position indices to nodes based on the sorted order. + * Adjusts the X-coordinates of nodes to ensure minimum spacing between them. + */ + public void sortNodesByX() { + if (isEmpty()) return; + + sort(NODE_X_COMPARATOR); // Sort nodes in the layer increasingly by x + + updateNodeIndices(); + updateMinXSpacing(false); + } + + /** + * Ensures nodes have minimum horizontal spacing by adjusting their X positions. + * + * @param startFromZero if true, starts positioning from X = 0; otherwise, uses the first node's current X. + */ + public void updateMinXSpacing(boolean startFromZero) { + if (isEmpty()) { + return; // No nodes to adjust. + } + + int minX = startFromZero ? 0 : this.get(0).getX(); + + for (LayoutNode node : this) { + int x = Math.max(node.getX(), minX); + node.setX(x); + minX = x + node.getOuterWidth() + NODE_OFFSET; + } + } + /** * Initializes nodes' X positions with spacing. */ @@ -203,4 +254,60 @@ public void updateNodeIndices() { pos++; } } + + /** + * Attempts to move the specified node to the right within the layer to the given X-coordinate. + * Ensures that the node does not overlap with its right neighbor by checking required spacing. + * If movement is possible without causing overlap, the node's X-coordinate is updated. + * + * @param layoutNode The node to move. + * @param newX The desired new X-coordinate for the node. + */ + public void tryShiftNodeRight(LayoutNode layoutNode, int newX) { + int currentX = layoutNode.getX(); + int shiftAmount = newX - currentX; + int rightPos = layoutNode.getPos() + 1; + + if (rightPos < size()) { + // There is a right neighbor + LayoutNode rightNeighbor = get(rightPos); + int proposedRightEdge = layoutNode.getRight() + shiftAmount; + int requiredLeftEdge = rightNeighbor.getOuterLeft() - NODE_OFFSET; + + if (proposedRightEdge <= requiredLeftEdge) { + layoutNode.setX(newX); + } + } else { + // No right neighbor; safe to move freely to the right + layoutNode.setX(newX); + } + } + + /** + * Attempts to move the specified node to the left within the layer to the given X-coordinate. + * Ensures that the node does not overlap with its left neighbor by checking required spacing. + * If movement is possible without causing overlap, the node's X-coordinate is updated. + * + * @param layoutNode The node to move. + * @param newX The desired new X-coordinate for the node. + */ + public void tryShiftNodeLeft(LayoutNode layoutNode, int newX) { + int currentX = layoutNode.getX(); + int shiftAmount = currentX - newX; + int leftPos = layoutNode.getPos() - 1; + + if (leftPos >= 0) { + // There is a left neighbor + LayoutNode leftNeighbor = get(leftPos); + int proposedLeftEdge = layoutNode.getLeft() - shiftAmount; + int requiredRightEdge = leftNeighbor.getOuterRight() + NODE_OFFSET; + + if (requiredRightEdge <= proposedLeftEdge) { + layoutNode.setX(newX); + } + } else { + // No left neighbor; safe to move freely to the left + layoutNode.setX(newX); + } + } } diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java new file mode 100644 index 00000000000..19f24f1f7e6 --- /dev/null +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutMover.java @@ -0,0 +1,53 @@ +/* + * 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. + * + */ +package com.sun.hotspot.igv.hierarchicallayout; + +import com.sun.hotspot.igv.layout.Vertex; +import java.awt.Point; +import java.util.Set; + +public interface LayoutMover { + /** + * Moves a link by shifting its position along the X-axis. + * + * @param linkPos The current position of the link. + * @param shiftX The amount to shift the link along the X-axis. + */ + void moveLink(Point linkPos, int shiftX); + + /** + * Moves a set of vertices. + * + * @param movedVertices A set of vertices to be moved. + */ + void moveVertices(Set movedVertices); + + /** + * Moves a single vertex. + * + * @param movedVertex The vertex to be moved. + */ + void moveVertex(Vertex movedVertex); +} + diff --git a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java index 0997de46430..9ad0c70b912 100644 --- a/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java +++ b/src/utils/IdealGraphVisualizer/HierarchicalLayout/src/main/java/com/sun/hotspot/igv/hierarchicallayout/LayoutNode.java @@ -69,6 +69,7 @@ public class LayoutNode { private int rightMargin; private int leftMargin; private int pos = -1; // Position within its layer + private boolean reverseLeft = false; private int crossingNumber = 0; public boolean hasSelfEdge() { @@ -250,6 +251,15 @@ public int getLeft() { return x + leftMargin; } + /** + * Gets the outer left boundary (including left margin) of the node. + * + * @return The x-coordinate of the outer left boundary. + */ + public int getOuterLeft() { + return x; + } + /** * Gets the total width of the node, including left and right margins. * @@ -272,6 +282,15 @@ public int getHeight() { return height; } + /** + * Gets the right boundary (excluding right margin) of the node. + * + * @return The x-coordinate of the right boundary. + */ + public int getRight() { + return x + leftMargin + width; + } + /** * Gets the outer right boundary (including right margin) of the node. * @@ -436,6 +455,50 @@ public void setPos(int pos) { this.pos = pos; } + /** + * Groups the successor edges by their relative x-coordinate from the current node. + * + * @return A map of relative x-coordinate to list of successor edges. + */ + public Map> groupSuccessorsByX() { + Map> result = new HashMap<>(); + for (LayoutEdge succEdge : succs) { + result.computeIfAbsent(succEdge.getRelativeFromX(), k -> new ArrayList<>()).add(succEdge); + } + return result; + } + + private int getBackedgeCrossingScore() { + int score = 0; + for (LayoutEdge predEdge : preds) { + if (predEdge.isReversed()) { + List points = reversedLinkEndPoints.get(predEdge.getLink()); + if (points != null) { + int x0 = points.get(points.size() - 1).x; + int xn = points.get(0).x; + int startPoint = predEdge.getStartX(); + int endPoint = predEdge.getEndX(); + int win = (x0 < xn) ? (startPoint - endPoint) : (endPoint - startPoint); + score += win; + } + } + } + for (LayoutEdge succEdge : succs) { + if (succEdge.isReversed()) { + List points = reversedLinkStartPoints.get(succEdge.getLink()); + if (points != null) { + int x0 = points.get(points.size() - 1).x; + int xn = points.get(0).x; + int startPoint = succEdge.getStartX(); + int endPoint = succEdge.getEndX(); + int win = (x0 > xn) ? (startPoint - endPoint) : (endPoint - startPoint); + score += win; + } + } + } + return score; + } + private boolean computeReversedStartPoints(boolean left) { TreeMap> sortedDownMap = left ? new TreeMap<>() : new TreeMap<>(Collections.reverseOrder()); for (LayoutEdge succEdge : succs) { @@ -518,12 +581,28 @@ private boolean computeReversedEndPoints(boolean left) { } public void computeReversedLinkPoints(boolean reverseLeft) { + this.reverseLeft = reverseLeft; + initSize(); reversedLinkStartPoints.clear(); reversedLinkEndPoints.clear(); boolean hasReversedDown = computeReversedStartPoints(reverseLeft); - computeReversedEndPoints(hasReversedDown != reverseLeft); + boolean hasReversedUP = computeReversedEndPoints(hasReversedDown != reverseLeft); + } + + public boolean isReverseRight() { + return !reverseLeft; + } + + public void optimizeBackEdgeCrossing() { + if (reversedLinkStartPoints.isEmpty() && reversedLinkEndPoints.isEmpty()) return; + int orig_score = getBackedgeCrossingScore(); + computeReversedLinkPoints(isReverseRight()); + int reverse_score = getBackedgeCrossingScore(); + if (orig_score > reverse_score) { + computeReversedLinkPoints(isReverseRight()); + } } public ArrayList getSelfEdgePoints() { diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java index 297c7d9b998..88418338f34 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java @@ -81,13 +81,17 @@ public class DiagramScene extends ObjectScene implements DiagramViewer, DoubleCl private final LayerWidget mainLayer; private final LayerWidget blockLayer; private final LayerWidget connectionLayer; + private final Widget shadowWidget; + private final Widget pointerWidget; private final DiagramViewModel model; private ModelState modelState; private boolean rebuilding; + private final Map> outputSlotToLineWidget = new HashMap<>(); + private final Map> inputSlotToLineWidget = new HashMap<>(); private final HierarchicalStableLayoutManager hierarchicalStableLayoutManager; private HierarchicalLayoutManager seaLayoutManager; - + private LayoutMover layoutMover; /** * The alpha level of partially visible figures. @@ -341,6 +345,12 @@ public void select(Widget widget, Point localLocation, boolean invertSelection) mainLayer.setBorder(emptyBorder); addChild(mainLayer); + pointerWidget = new Widget(DiagramScene.this); + addChild(pointerWidget); + + shadowWidget = new Widget(DiagramScene.this); + addChild(shadowWidget); + setLayout(LayoutFactory.createAbsoluteLayout()); getActions().addAction(mouseZoomAction); getActions().addAction(ActionFactory.createPopupMenuAction((widget, localLocation) -> createPopupMenu())); @@ -596,6 +606,163 @@ private void updateFigureWidths() { } } + private MoveProvider getFigureMoveProvider() { + return new MoveProvider() { + + private boolean hasMoved = false; // Flag to track movement + private int startLayerY; + + private void setFigureShadow(Figure f) { + FigureWidget fw = getWidget(f); + Color c = f.getColor(); + Border border = new FigureWidget.RoundedBorder(new Color(0,0,0, 50), 1); + shadowWidget.setBorder(border); + shadowWidget.setBackground(new Color(c.getRed(), c.getGreen(), c.getBlue(), 50)); + shadowWidget.setPreferredLocation(fw.getPreferredLocation()); + shadowWidget.setPreferredSize(f.getSize()); + shadowWidget.setVisible(true); + shadowWidget.setOpaque(true); + shadowWidget.revalidate(); + shadowWidget.repaint(); + } + + private void setMovePointer(Figure f) { + Border border = new FigureWidget.RoundedBorder(Color.RED, 1); + pointerWidget.setBorder(border); + pointerWidget.setBackground(Color.RED); + pointerWidget.setPreferredBounds(new Rectangle(0, 0, 3, f.getSize().height)); + pointerWidget.setVisible(false); + pointerWidget.setOpaque(true); + } + + + @Override + public void movementStarted(Widget widget) { + if (layoutMover == null) return; // Do nothing if layoutMover is not available + + widget.bringToFront(); + startLayerY = widget.getLocation().y; + hasMoved = false; // Reset the movement flag + Set
selectedFigures = model.getSelectedFigures(); + if (selectedFigures.size() == 1) { + Figure selectedFigure = selectedFigures.iterator().next(); + setFigureShadow(selectedFigure); + setMovePointer(selectedFigure); + } + } + + @Override + public void movementFinished(Widget widget) { + shadowWidget.setVisible(false); + pointerWidget.setVisible(false); + if (layoutMover == null || !hasMoved) return; // Do nothing if layoutMover is not available or no movement occurred + rebuilding = true; + + Set
movedFigures = new HashSet<>(model.getSelectedFigures()); + for (Figure figure : movedFigures) { + FigureWidget fw = getWidget(figure); + figure.setPosition(new Point(fw.getLocation().x, fw.getLocation().y)); + } + + layoutMover.moveVertices(movedFigures); + rebuildConnectionLayer(); + + for (FigureWidget fw : getVisibleFigureWidgets()) { + fw.updatePosition(); + } + + validateAll(); + addUndo(); + rebuilding = false; + } + + private static final int MAGNET_SIZE = 5; + + private int magnetToStartLayerY(Widget widget, Point location) { + int shiftY = location.y - widget.getLocation().y; + if (Math.abs(location.y - startLayerY) <= MAGNET_SIZE) { + if (Math.abs(widget.getLocation().y - startLayerY) > MAGNET_SIZE) { + shiftY = startLayerY - widget.getLocation().y; + } else { + shiftY = 0; + } + } + return shiftY; + } + + @Override + public Point getOriginalLocation(Widget widget) { + if (layoutMover == null) return widget.getLocation(); // default behavior + return ActionFactory.createDefaultMoveProvider().getOriginalLocation(widget); + } + + @Override + public void setNewLocation(Widget widget, Point location) { + if (layoutMover == null) return; // Do nothing if layoutMover is not available + hasMoved = true; // Mark that a movement occurred + + int shiftX = location.x - widget.getLocation().x; + int shiftY = magnetToStartLayerY(widget, location); + + List
selectedFigures = new ArrayList<>( model.getSelectedFigures()); + selectedFigures.sort(Comparator.comparingInt(f -> f.getPosition().x)); + for (Figure figure : selectedFigures) { + FigureWidget fw = getWidget(figure); + for (InputSlot inputSlot : figure.getInputSlots()) { + assert inputSlot != null; + if (inputSlotToLineWidget.containsKey(inputSlot)) { + for (LineWidget lw : inputSlotToLineWidget.get(inputSlot)) { + assert lw != null; + Point toPt = lw.getTo(); + Point fromPt = lw.getFrom(); + if (toPt != null && fromPt != null) { + int xTo = toPt.x + shiftX; + int yTo = toPt.y + shiftY; + lw.setTo(new Point(xTo, yTo)); + lw.setFrom(new Point(fromPt.x + shiftX, fromPt.y)); + LineWidget pred = lw.getPredecessor(); + pred.setTo(new Point(pred.getTo().x + shiftX, pred.getTo().y)); + pred.revalidate(); + lw.revalidate(); + } + } + } + } + for (OutputSlot outputSlot : figure.getOutputSlots()) { + assert outputSlot != null; + if (outputSlotToLineWidget.containsKey(outputSlot)) { + for (LineWidget lw : outputSlotToLineWidget.get(outputSlot)) { + assert lw != null; + Point fromPt = lw.getFrom(); + Point toPt = lw.getTo(); + if (toPt != null && fromPt != null) { + int xFrom = fromPt.x + shiftX; + int yFrom = fromPt.y + shiftY; + lw.setFrom(new Point(xFrom, yFrom)); + lw.setTo(new Point(toPt.x + shiftX, toPt.y)); + for (LineWidget succ : lw.getSuccessors()) { + succ.setFrom(new Point(succ.getFrom().x + shiftX, succ.getFrom().y)); + succ.revalidate(); + } + lw.revalidate(); + } + } + } + } + Point newLocation = new Point(fw.getLocation().x + shiftX, fw.getLocation().y + shiftY); + ActionFactory.createDefaultMoveProvider().setNewLocation(fw, newLocation); + } + + FigureWidget fw = getWidget(selectedFigures.iterator().next()); + pointerWidget.setVisible(true); + Point newLocation = new Point(fw.getLocation().x + shiftX -3, fw.getLocation().y + shiftY); + ActionFactory.createDefaultMoveProvider().setNewLocation(pointerWidget, newLocation); + connectionLayer.revalidate(); + connectionLayer.repaint(); + } + }; + } + private void rebuildMainLayer() { mainLayer.removeChildren(); for (Figure figure : getModel().getDiagram().getFigures()) { @@ -604,6 +771,7 @@ private void rebuildMainLayer() { figureWidget.getActions().addAction(ActionFactory.createPopupMenuAction(figureWidget)); figureWidget.getActions().addAction(selectAction); figureWidget.getActions().addAction(hoverAction); + figureWidget.getActions().addAction(ActionFactory.createMoveAction(null, getFigureMoveProvider())); addObject(figure, figureWidget); mainLayer.addChild(figureWidget); @@ -737,6 +905,7 @@ private boolean isVisibleFigureConnection(FigureConnection figureConnection) { } private void doStableSeaLayout(Set
visibleFigures, Set visibleConnections) { + layoutMover = null; boolean enable = model.getCutEdges(); boolean previous = hierarchicalStableLayoutManager.getCutEdges(); hierarchicalStableLayoutManager.setCutEdges(enable); @@ -749,17 +918,20 @@ private void doStableSeaLayout(Set
visibleFigures, Set visib private void doSeaLayout(Set
visibleFigures, Set visibleConnections) { seaLayoutManager = new HierarchicalLayoutManager(); + layoutMover = seaLayoutManager; seaLayoutManager.setCutEdges(model.getCutEdges()); seaLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures)); } private void doClusteredLayout(Set
visibleFigures, Set visibleConnections) { + layoutMover = null; HierarchicalClusterLayoutManager clusterLayoutManager = new HierarchicalClusterLayoutManager(); clusterLayoutManager.setCutEdges(model.getCutEdges()); clusterLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures)); } private void doCFGLayout(Set
visibleFigures, Set visibleConnections) { + layoutMover = null; HierarchicalCFGLayoutManager cfgLayoutManager = new HierarchicalCFGLayoutManager(getVisibleBlockConnections(), getVisibleBlocks()); cfgLayoutManager.setCutEdges(model.getCutEdges()); cfgLayoutManager.doLayout(new LayoutGraph(visibleConnections, visibleFigures)); @@ -777,6 +949,84 @@ private boolean shouldAnimate() { private final Point specialNullPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); + private MoveProvider getFigureConnectionMoveProvider() { + return new MoveProvider() { + + Point startLocation; + Point originalPosition; + + @Override + public void movementStarted(Widget widget) { + if (layoutMover == null) return; // Do nothing if layoutMover is not available + LineWidget lw = (LineWidget) widget; + startLocation = lw.getClientAreaLocation(); + originalPosition = lw.getFrom(); + } + + @Override + public void movementFinished(Widget widget) { + if (layoutMover == null) return; // Do nothing if layoutMover is not available + LineWidget lineWidget = (LineWidget) widget; + if (lineWidget.getPredecessor() == null) return; + if (lineWidget.getSuccessors().isEmpty()) return; + if (lineWidget.getFrom().x != lineWidget.getTo().x) return; + + int shiftX = lineWidget.getClientAreaLocation().x - startLocation.x; + if (shiftX == 0) return; + + rebuilding = true; + layoutMover.moveLink(originalPosition, shiftX); + rebuildConnectionLayer(); + for (FigureWidget fw : getVisibleFigureWidgets()) { + fw.updatePosition(); + } + validateAll(); + addUndo(); + rebuilding = false; + } + + @Override + public Point getOriginalLocation(Widget widget) { + if (layoutMover == null) return widget.getLocation(); // default behavior + LineWidget lineWidget = (LineWidget) widget; + return lineWidget.getClientAreaLocation(); + } + + @Override + public void setNewLocation(Widget widget, Point location) { + if (layoutMover == null) return; // Do nothing if layoutMover is not available + LineWidget lineWidget = (LineWidget) widget; + if (lineWidget.getPredecessor() == null) return; + if (lineWidget.getSuccessors().isEmpty()) return; + if (lineWidget.getFrom().x != lineWidget.getTo().x) return; + + int shiftX = location.x - lineWidget.getClientAreaLocation().x; + if (shiftX == 0) return; + + Point oldFrom = lineWidget.getFrom(); + Point newFrom = new Point(oldFrom.x + shiftX, oldFrom.y); + + Point oldTo = lineWidget.getTo(); + Point newTo = new Point(oldTo.x + shiftX, oldTo.y); + + lineWidget.setTo(newTo); + lineWidget.setFrom(newFrom); + lineWidget.revalidate(); + + LineWidget predecessor = lineWidget.getPredecessor(); + Point toPt = predecessor.getTo(); + predecessor.setTo(new Point(toPt.x + shiftX, toPt.y)); + predecessor.revalidate(); + + for (LineWidget successor : lineWidget.getSuccessors()) { + Point fromPt = successor.getFrom(); + successor.setFrom(new Point(fromPt.x + shiftX, fromPt.y)); + successor.revalidate(); + } + } + }; + } + private void processOutputSlot(OutputSlot outputSlot, List connections, int controlPointIndex, Point lastPoint, LineWidget predecessor) { Map> pointMap = new HashMap<>(connections.size()); @@ -824,6 +1074,7 @@ private void processOutputSlot(OutputSlot outputSlot, List con newPredecessor.setVisible(isVisible); if (predecessor == null) { + assert outputSlot != null; if (outputSlotToLineWidget.containsKey(outputSlot)) { outputSlotToLineWidget.get(outputSlot).add(newPredecessor); } else { @@ -834,6 +1085,7 @@ private void processOutputSlot(OutputSlot outputSlot, List con newWidgets.add(newPredecessor); addObject(new ConnectionSet(connectionList), newPredecessor); newPredecessor.getActions().addAction(hoverAction); + newPredecessor.getActions().addAction(ActionFactory.createMoveAction(null, getFigureConnectionMoveProvider())); } processOutputSlot(outputSlot, connectionList, controlPointIndex + 1, currentPoint, newPredecessor); @@ -843,10 +1095,13 @@ private void processOutputSlot(OutputSlot outputSlot, List con for (FigureConnection connection : connections) { if (isVisibleFigureConnection(connection)) { InputSlot inputSlot = connection.getInputSlot(); - if (inputSlotToLineWidget.containsKey(inputSlot)) { - inputSlotToLineWidget.get(inputSlot).add(predecessor); - } else { - inputSlotToLineWidget.put(inputSlot, new HashSet<>(Collections.singleton(predecessor))); + if (predecessor != null) { + assert inputSlot != null; + if (inputSlotToLineWidget.containsKey(inputSlot)) { + inputSlotToLineWidget.get(inputSlot).add(predecessor); + } else { + inputSlotToLineWidget.put(inputSlot, new HashSet<>(Collections.singleton(predecessor))); + } } } } @@ -1212,9 +1467,6 @@ private void updateBlockWidgetBounds(Set oldVisibleBlockWidgets) { } } - Map> outputSlotToLineWidget = new HashMap<>(); - Map> inputSlotToLineWidget = new HashMap<>(); - public JPopupMenu createPopupMenu() { JPopupMenu menu = new JPopupMenu(); diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java index 6b9b8e548d1..24780d4d7d2 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java @@ -185,6 +185,10 @@ protected Sheet createSheet() { this.setToolTipText(PropertiesConverter.convertToHTML(f.getProperties())); } + public void updatePosition() { + setPreferredLocation(figure.getPosition()); + } + public int getFigureHeight() { return middleWidget.getPreferredBounds().height; } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java index 1a0448c380c..93d16d35914 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/LineWidget.java @@ -60,8 +60,8 @@ public class LineWidget extends Widget implements PopupMenuProvider { private final OutputSlot outputSlot; private final DiagramScene scene; private final List connections; - private final Point from; - private final Point to; + private Point from; + private Point to; private Rectangle clientArea; private final LineWidget predecessor; private final List successors; @@ -125,6 +125,10 @@ public void select(Widget widget, Point localLocation, boolean invertSelection) })); } + public Point getClientAreaLocation() { + return clientArea.getLocation(); + } + private void computeClientArea() { int minX = from.x; int minY = from.y; @@ -158,6 +162,16 @@ private String generateToolTipText(List conn) { return sb.toString(); } + public void setFrom(Point from) { + this.from = from; + computeClientArea(); + } + + public void setTo(Point to) { + this.to= to; + computeClientArea(); + } + public Point getFrom() { return from; } @@ -166,6 +180,14 @@ public Point getTo() { return to; } + public LineWidget getPredecessor() { + return predecessor; + } + + public List getSuccessors() { + return Collections.unmodifiableList(successors); + } + private void addSuccessor(LineWidget widget) { this.successors.add(widget); } From a80ccf2cd2792c24b51f1143cb0e6c5b036c5b28 Mon Sep 17 00:00:00 2001 From: Tobias Holenstein Date: Fri, 29 Nov 2024 15:21:39 +0000 Subject: [PATCH 027/171] 8345039: IGV: save user-defined node colors to XML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Roberto Castañeda Lozano Co-authored-by: Christian Hagedorn Reviewed-by: chagedorn, epeter, rcastanedalo --- .../com/sun/hotspot/igv/data/InputNode.java | 24 ++++++++++++++++ .../com/sun/hotspot/igv/graph/Diagram.java | 6 ++++ .../com/sun/hotspot/igv/graph/Figure.java | 20 ++++++++++++- .../sun/hotspot/igv/view/DiagramScene.java | 2 +- .../hotspot/igv/view/actions/ColorAction.java | 28 ++++++++++++++++--- .../igv/view/widgets/FigureWidget.java | 1 + 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java index 62b7aa4c034..48a000bf7b9 100644 --- a/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java +++ b/src/utils/IdealGraphVisualizer/Data/src/main/java/com/sun/hotspot/igv/data/InputNode.java @@ -23,6 +23,7 @@ */ package com.sun.hotspot.igv.data; +import java.awt.Color; import java.util.Objects; /** @@ -67,4 +68,27 @@ public boolean equals(Object obj) { public String toString() { return "Node " + id + " " + getProperties().toString(); } + + public void setCustomColor(Color color) { + if (color != null) { + String hexColor = String.format("#%08X", color.getRGB()); + getProperties().setProperty("color", hexColor); + } else { + getProperties().setProperty("color", null); + } + } + + public Color getCustomColor() { + String hexColor = getProperties().get("color"); + if (hexColor != null) { + try { + String hex = hexColor.startsWith("#") ? hexColor.substring(1) : hexColor; + int argb = (int) Long.parseLong(hex, 16); + return new Color(argb, true); + } catch (Exception ignored) { + return null; + } + } + return null; + } } diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java index bb5e3358a30..b42dec7eb39 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Diagram.java @@ -36,6 +36,7 @@ public class Diagram { private final Map figures; private final Map blocks; + private final InputGraph inputGraph; private final String nodeText; private final String shortNodeText; private final String tinyNodeText; @@ -66,6 +67,7 @@ public Diagram(InputGraph graph, String nodeText, String shortNodeText, this.figures = new LinkedHashMap<>(); this.blocks = new LinkedHashMap<>(8); this.blockConnections = new HashSet<>(); + this.inputGraph = graph; this.cfg = false; int curId = 0; @@ -128,6 +130,10 @@ public Diagram(InputGraph graph, String nodeText, String shortNodeText, } } + public InputGraph getInputGraph() { + return inputGraph; + } + public Block getBlock(InputBlock b) { assert blocks.containsKey(b); return blocks.get(b); diff --git a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java index 23c7d136d5b..d85e102f874 100644 --- a/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java +++ b/src/utils/IdealGraphVisualizer/Graph/src/main/java/com/sun/hotspot/igv/graph/Figure.java @@ -23,6 +23,7 @@ */ package com.sun.hotspot.igv.graph; +import com.sun.hotspot.igv.data.InputGraph; import com.sun.hotspot.igv.data.InputNode; import com.sun.hotspot.igv.data.Properties; import com.sun.hotspot.igv.layout.Cluster; @@ -153,7 +154,12 @@ public void setColor(Color color) { } public Color getColor() { - return color; + Color customColor = inputNode.getCustomColor(); + if (customColor != null) { + return customColor; + } else { + return color; + } } public void setWarning(String warning) { @@ -415,4 +421,16 @@ public boolean isRoot() { public int compareTo(Vertex f) { return toString().compareTo(f.toString()); } + + public void setCustomColor(Color color) { + // Apply custom color not just to this input node but to all + // corresponding input nodes in the group. + InputGraph graph = diagram.getInputGraph(); + for (InputGraph g : graph.getGroup().getGraphs()) { + InputNode n = g.getNode(inputNode.getId()); + if (n != null) { + n.setCustomColor(color); + } + } + } } diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java index 88418338f34..90d5e0ac424 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/DiagramScene.java @@ -230,7 +230,7 @@ public void filteredChanged(SelectionCoordinator coordinator) { public void colorSelectedFigures(Color color) { for (Figure figure : model.getSelectedFigures()) { - figure.setColor(color); + figure.setCustomColor(color); FigureWidget figureWidget = getWidget(figure); if (figureWidget != null) { figureWidget.refreshColor(); diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ColorAction.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ColorAction.java index 7a38587eb38..d0ace9f7710 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ColorAction.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/actions/ColorAction.java @@ -82,6 +82,7 @@ public String getName() { private static final JLabel selectedColorLabel = new JLabel("Preview"); private static final JColorChooser colorChooser = new JColorChooser(Color.WHITE); + private static final Color NO_COLOR = new Color(0, 0, 0, 0); public ColorAction() { initializeComponents(); @@ -89,8 +90,8 @@ public ColorAction() { private void initializeComponents() { selectedColorLabel.setPreferredSize(new Dimension(3 * 32, 32)); - selectedColorLabel.setOpaque(true); - selectedColorLabel.setBackground(Color.WHITE); + selectedColorLabel.setOpaque(false); // Allow transparency + selectedColorLabel.setBackground(NO_COLOR); // Set transparent background selectedColorLabel.setForeground(Color.BLACK); // Set text color selectedColorLabel.setHorizontalAlignment(SwingConstants.CENTER); // Center the text @@ -100,6 +101,7 @@ private void initializeComponents() { Color selectedColor = colorChooser.getColor(); if (selectedColor != null) { selectedColorLabel.setBackground(selectedColor); + selectedColorLabel.setOpaque(selectedColor.getAlpha() != 0); selectedColorLabel.setForeground(FigureWidget.getTextColor(selectedColor)); } }); @@ -118,10 +120,27 @@ private void initializeComponents() { colorButton.setPreferredSize(new Dimension(16, 16)); colorButton.addActionListener(e -> { selectedColorLabel.setBackground(color); + selectedColorLabel.setOpaque(color.getAlpha() != 0); selectedColorLabel.setForeground(FigureWidget.getTextColor(color)); }); colorsPanel.add(colorButton); } + + // Add "No Color" button + JButton noColorButton = new JButton("No Color"); + noColorButton.setOpaque(true); + noColorButton.setContentAreaFilled(true); + noColorButton.setBorderPainted(true); + noColorButton.setPreferredSize(new Dimension(90, 24)); + noColorButton.setFocusPainted(false); + noColorButton.addActionListener(e -> { + selectedColorLabel.setBackground(NO_COLOR); + selectedColorLabel.setOpaque(false); + selectedColorLabel.setForeground(Color.BLACK); + }); + colorsPanel.add(noColorButton); + + // Add the preview label colorsPanel.add(selectedColorLabel, 0); colorsPanel.revalidate(); colorsPanel.repaint(); @@ -148,9 +167,10 @@ public void performAction(DiagramViewModel model) { dialogLoc = dialogHolder[0].getLocation(); // OK button action Color selectedColor = selectedColorLabel.getBackground(); - if (selectedColor != null) { - editor.colorSelectedFigures(selectedColor); + if (selectedColor.equals(NO_COLOR)) { + selectedColor = null; } + editor.colorSelectedFigures(selectedColor); }, null // Cancel button action ); diff --git a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java index 24780d4d7d2..5578c4505cf 100644 --- a/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java +++ b/src/utils/IdealGraphVisualizer/View/src/main/java/com/sun/hotspot/igv/view/widgets/FigureWidget.java @@ -271,6 +271,7 @@ public Figure getFigure() { @Override protected void paintChildren() { + refreshColor(); Composite oldComposite = null; if (boundary) { oldComposite = getScene().getGraphics().getComposite(); From e9136b5e08abc20038c7b2089ab8fe320e4faef0 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Fri, 29 Nov 2024 15:58:57 +0000 Subject: [PATCH 028/171] 8345223: Remove stray doPrivileged in java.base java.net and sun.net classes after JEP 486 integration Reviewed-by: alanb, aefimov, michaelm --- .../classes/java/net/DefaultInterface.java | 8 +-- .../classes/java/net/SocketPermission.java | 4 +- .../classes/sun/net/ftp/impl/FtpClient.java | 10 ++-- .../net/dns/ResolverConfigurationImpl.java | 60 +++---------------- .../unix/classes/sun/net/sdp/SdpProvider.java | 4 +- .../http/ntlm/NTLMAuthentication.java | 36 ++++------- 6 files changed, 30 insertions(+), 92 deletions(-) diff --git a/src/java.base/macosx/classes/java/net/DefaultInterface.java b/src/java.base/macosx/classes/java/net/DefaultInterface.java index b46de34a7cc..3bc629b26af 100644 --- a/src/java.base/macosx/classes/java/net/DefaultInterface.java +++ b/src/java.base/macosx/classes/java/net/DefaultInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -25,8 +25,6 @@ package java.net; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Enumeration; import java.io.IOException; @@ -105,9 +103,7 @@ private static NetworkInterface chooseDefaultInterface() { continue; boolean ip4 = false, ip6 = false, isNonLinkLocal = false; - PrivilegedAction> pa = ni::getInetAddresses; - @SuppressWarnings("removal") - Enumeration addrs = AccessController.doPrivileged(pa); + Enumeration addrs = ni.getInetAddresses(); while (addrs.hasMoreElements()) { InetAddress addr = addrs.nextElement(); if (!addr.isAnyLocalAddress()) { diff --git a/src/java.base/share/classes/java/net/SocketPermission.java b/src/java.base/share/classes/java/net/SocketPermission.java index 0c49e6cafef..e3a85bbf856 100644 --- a/src/java.base/share/classes/java/net/SocketPermission.java +++ b/src/java.base/share/classes/java/net/SocketPermission.java @@ -42,7 +42,6 @@ import java.util.concurrent.ConcurrentHashMap; import sun.net.util.IPAddressUtil; import sun.net.PortConfig; -import sun.security.action.GetBooleanAction; import sun.security.util.RegisteredDomain; import sun.security.util.SecurityConstants; import sun.security.util.Debug; @@ -211,7 +210,8 @@ public final class SocketPermission extends Permission private transient boolean trusted; // true if the sun.net.trustNameService system property is set - private static final boolean trustNameService = GetBooleanAction.privilegedGetProperty("sun.net.trustNameService"); + private static final boolean trustNameService = + Boolean.getBoolean("sun.net.trustNameService"); private static Debug debug = null; private static boolean debugInit = false; diff --git a/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java b/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java index bfc10028ae0..d5c9a3feb44 100644 --- a/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java +++ b/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java @@ -710,13 +710,13 @@ private InetSocketAddress validatePasvAddress(int port, String s, InetAddress ad } else if (address.isLoopbackAddress() && s.startsWith("127.")) { // can be 127.0 return new InetSocketAddress(s, port); } else if (address.isLoopbackAddress()) { - if (privilegedLocalHost().getHostAddress().equals(s)) { + if (getLocalHost().getHostAddress().equals(s)) { return new InetSocketAddress(s, port); } else { throw new FtpProtocolException(ERROR_MSG); } } else if (s.startsWith("127.")) { - if (privilegedLocalHost().equals(address)) { + if (getLocalHost().equals(address)) { return new InetSocketAddress(s, port); } else { throw new FtpProtocolException(ERROR_MSG); @@ -724,7 +724,7 @@ private InetSocketAddress validatePasvAddress(int port, String s, InetAddress ad } String hostName = address.getHostName(); if (!(IPAddressUtil.isIPv4LiteralAddress(hostName) || IPAddressUtil.isIPv6LiteralAddress(hostName))) { - InetAddress[] names = privilegedGetAllByName(hostName); + InetAddress[] names = getAllByName(hostName); String resAddress = Arrays .stream(names) .map(InetAddress::getHostAddress) @@ -738,7 +738,7 @@ private InetSocketAddress validatePasvAddress(int port, String s, InetAddress ad throw new FtpProtocolException(ERROR_MSG); } - private static InetAddress privilegedLocalHost() throws FtpProtocolException { + private static InetAddress getLocalHost() throws FtpProtocolException { try { return InetAddress.getLocalHost(); } catch (Exception e) { @@ -748,7 +748,7 @@ private static InetAddress privilegedLocalHost() throws FtpProtocolException { } } - private static InetAddress[] privilegedGetAllByName(String hostName) throws FtpProtocolException { + private static InetAddress[] getAllByName(String hostName) throws FtpProtocolException { try { return InetAddress.getAllByName(hostName); } catch (Exception e) { diff --git a/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java b/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java index 6074d323fa5..a466331de93 100644 --- a/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java +++ b/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -117,7 +117,6 @@ private ArrayList resolvconf(String keyword, // Load DNS configuration from OS - @SuppressWarnings("removal") private void loadConfig() { assert Thread.holdsLock(lock); @@ -130,15 +129,9 @@ private void loadConfig() { } // get the name servers from /etc/resolv.conf - nameservers = - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction<>() { - public ArrayList run() { - // typically MAXNS is 3 but we've picked 5 here - // to allow for additional servers if required. - return resolvconf("nameserver", 1, 5); - } /* run */ - }); + // typically MAXNS is 3 but we've picked 5 here + // to allow for additional servers if required. + nameservers = resolvconf("nameserver", 1, 5); // get the search list (or domain) searchlist = getSearchList(); @@ -149,54 +142,19 @@ public ArrayList run() { // obtain search list or local domain - - @SuppressWarnings("removal") private ArrayList getSearchList() { - ArrayList sl; - // first try the search keyword in /etc/resolv.conf - sl = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction<>() { - public ArrayList run() { - ArrayList ll; - - // first try search keyword (max 6 domains) - ll = resolvconf("search", 6, 1); - if (ll.size() > 0) { - return ll; - } - - return null; - - } /* run */ - - }); - if (sl != null) { - return sl; - } + // first try search keyword (max 6 domains) + ArrayList sl = resolvconf("search", 6, 1); + if (sl.size() > 0) return sl; // No search keyword so use local domain // try domain keyword in /etc/resolv.conf - - sl = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction<>() { - public ArrayList run() { - ArrayList ll; - - ll = resolvconf("domain", 1, 1); - if (ll.size() > 0) { - return ll; - } - return null; - - } /* run */ - }); - if (sl != null) { - return sl; - } + sl = resolvconf("domain", 1, 1); + if (sl.size() > 0) return sl; // no local domain so try fallback (RPC) domain or // hostName diff --git a/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java b/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java index f9530cf7797..9c1b6b6ef9a 100644 --- a/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java +++ b/src/java.base/unix/classes/sun/net/sdp/SdpProvider.java @@ -35,8 +35,6 @@ import java.io.IOException; import java.io.PrintStream; -import sun.security.action.GetPropertyAction; - /** * A NetHooks provider that converts sockets from the TCP to SDP protocol prior * to binding or connecting. @@ -54,7 +52,7 @@ public class SdpProvider extends NetHooks.Provider { private PrintStream log; public SdpProvider() { - Properties props = GetPropertyAction.privilegedGetProperties(); + Properties props = System.getProperties(); // if this property is not defined then there is nothing to do. String file = props.getProperty("com.sun.sdp.conf"); if (file == null) { diff --git a/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java b/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java index c034cd4dcd7..dc56abb86df 100644 --- a/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java +++ b/src/java.base/unix/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java @@ -41,7 +41,6 @@ import sun.net.www.protocol.http.AuthenticationInfo; import sun.net.www.protocol.http.AuthScheme; import sun.net.www.protocol.http.HttpURLConnection; -import sun.security.action.GetPropertyAction; /** * NTLMAuthentication: @@ -72,21 +71,21 @@ public final class NTLMAuthentication extends AuthenticationInfo { private static final NTLMAuthenticationCallback NTLMAuthCallback = - NTLMAuthenticationCallback.getNTLMAuthenticationCallback(); + NTLMAuthenticationCallback.getNTLMAuthenticationCallback(); private String hostname; /* Domain to use if not specified by user */ private static final String defaultDomain; /* Whether cache is enabled for NTLM */ private static final boolean ntlmCache; + static { - Properties props = GetPropertyAction.privilegedGetProperties(); - defaultDomain = props.getProperty("http.auth.ntlm.domain", ""); - String ntlmCacheProp = props.getProperty("jdk.ntlm.cache", "true"); + defaultDomain = System.getProperty("http.auth.ntlm.domain", ""); + String ntlmCacheProp = System.getProperty("jdk.ntlm.cache", "true"); ntlmCache = Boolean.parseBoolean(ntlmCacheProp); } - public static boolean supportsTransparentAuth () { + public static boolean supportsTransparentAuth() { return false; } @@ -101,23 +100,6 @@ public static boolean isTrustedSite(URL url) { return false; } - @SuppressWarnings("removal") - private void init0() { - - hostname = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction<>() { - public String run() { - String localhost; - try { - localhost = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - localhost = "localhost"; - } - return localhost; - } - }); - }; - PasswordAuthentication pw; Client client; @@ -150,9 +132,13 @@ private void init (PasswordAuthentication pw) { username = s.substring (i+1); } password = pw.getPassword(); - init0(); try { - String version = GetPropertyAction.privilegedGetProperty("ntlm.version"); + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + hostname = "localhost"; + } + try { + String version = System.getProperty("ntlm.version"); client = new Client(version, hostname, username, ntdomain, password); } catch (NTLMException ne) { try { From ed03f0d9d10518242a3dc6e3685f1bdb0550c723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Lamp=C3=A9rth?= Date: Fri, 29 Nov 2024 16:24:22 +0000 Subject: [PATCH 029/171] 8345145: Display javap LineNumberTable and LocalVariableTable iff disassembled code output with `-c` or `-v` Reviewed-by: mcimadamore, liach --- .../com/sun/tools/javap/ClassWriter.java | 6 +- .../com/sun/tools/javap/CodeWriter.java | 7 +- .../com/sun/tools/javap/JavapTask.java | 4 + .../tools/javap/resources/javap.properties | 2 +- .../ClassWriterNoLineVariableTableTest.java | 101 ++++++++++++++++++ .../javap/ClassWriterTableIndentTest.java | 2 +- test/langtools/tools/javap/T4459541.java | 2 +- test/langtools/tools/javap/T8032814.java | 2 +- 8 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 test/langtools/tools/javap/ClassWriterNoLineVariableTableTest.java diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java index 2dbc411dd6e..3ac3d35b787 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java @@ -574,10 +574,8 @@ protected void writeMethod(MethodModel m) { if (options.showAllAttrs) { attrWriter.write(m.attributes()); - } else if (code != null) { - if (options.showDisassembled || options.showLineAndLocalVariableTables) { - codeWriter.writeMinimal(code); - } + } else if (code != null && options.showDisassembled) { + codeWriter.writeMinimal(code); } indent(-1); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java index 99d7e2c2686..9c884fff7ee 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java @@ -266,11 +266,8 @@ private void writeInternal(CodeAttribute attr, boolean minimal) { } private void writeMinimalMode(CodeAttribute attr) { - if (options.showDisassembled) { - writeInstrs(attr); - writeExceptionTable(attr); - } - + writeInstrs(attr); + writeExceptionTable(attr); if (options.showLineAndLocalVariableTables) { writeLineAndLocalVariableTables(attr); } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/JavapTask.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/JavapTask.java index b797ef73522..138a76b6f2e 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/JavapTask.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/JavapTask.java @@ -549,6 +549,10 @@ else if (allowClasses) { throw new BadArgs("err.incompatible.options", sb); } + if (!options.showDisassembled && !options.verbose && options.showLineAndLocalVariableTables) { + reportWarning("err.incompatible.options", "-l without -c, line number and local variable tables will not be printed"); + } + if ((classes == null || classes.size() == 0) && !(noArgs || options.help || options.version || options.fullVersion)) { throw new BadArgs("err.no.classes.specified"); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/resources/javap.properties b/src/jdk.jdeps/share/classes/com/sun/tools/javap/resources/javap.properties index 5a50662afa0..02e277e2235 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/resources/javap.properties +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/resources/javap.properties @@ -74,7 +74,7 @@ main.opt.v=\ \ -v -verbose Print additional information main.opt.l=\ -\ -l Print line number and local variable tables +\ -l Print line number and local variable tables, works in combination with -c main.opt.public=\ \ -public Show only public classes and members diff --git a/test/langtools/tools/javap/ClassWriterNoLineVariableTableTest.java b/test/langtools/tools/javap/ClassWriterNoLineVariableTableTest.java new file mode 100644 index 00000000000..102c3c1bde9 --- /dev/null +++ b/test/langtools/tools/javap/ClassWriterNoLineVariableTableTest.java @@ -0,0 +1,101 @@ +/* + * 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 8345145 + * @summary javap should not print LineNumberTable/LocalVariableTable (-l) without disassembled code (-c). + * @compile -g ClassWriterNoLineVariableTableTest.java + * @run junit ClassWriterNoLineVariableTableTest + * @modules jdk.jdeps/com.sun.tools.javap + */ + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; + +public class ClassWriterNoLineVariableTableTest { + String expectedErrorOutput = "Warning: bad combination of options: -l without -c, line number and local variable tables will not be printed"; + + @Test + public void testJavapWithoutCodeAttribute() { + String output = javap("-l"); + assertContains(output, expectedErrorOutput, + "javap should throw warning, when -l used without -c or -v"); + assertNotContains(output, "LineNumberTable", + "There should be no LineNumberTable output when javap is provided l without -c or -v"); + assertNotContains(output, "LocalVariableTable", + "There should be no LineNumberTable output when javap is provided l without -c or -v"); + } + + @ParameterizedTest(name = "Test javap with fixed option -l and varying option: {0}") + @ValueSource(strings = {"-v", "-c"}) + public void testJavapWithCodeAttribute(String addedOption) { + String output = javap("-l", addedOption); + assertNotContains(output, expectedErrorOutput, + "There should be no warning when javap is provided -l and " + addedOption); + assertContains(output, "LineNumberTable", + "There should be LineNumberTable output when javap is provided -l and " + addedOption); + assertContains(output, "LocalVariableTable", + "There should be LocalVariableTable output when javap is provided -l and " + addedOption); + } + + private static void assertContains(String actual, String expectedSubstring, String message) { + assertTrue(actual.contains(expectedSubstring), + message + " - Expected '" + actual + "' to contain '" + expectedSubstring + "'"); + } + + private static void assertNotContains(String actual, String expectedSubstring, String message) { + assertFalse(actual.contains(expectedSubstring), + message + " - Expected '" + actual + "' not to contain '" + expectedSubstring + "'"); + } + + private String javap(String... args) { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + + String[] fullArgs = new String[args.length + 1]; + System.arraycopy(args, 0, fullArgs, 0, args.length); + fullArgs[args.length] = System.getProperty("test.classes") + "/RandomLoop8345145.class"; + + int rc = com.sun.tools.javap.Main.run(fullArgs, out); + if (rc != 0) + throw new Error("javap failed. rc=" + rc); + out.close(); + System.out.println(sw); + return sw.toString(); + } +} + +class RandomLoop8345145 { + public void randomLoop() { + int x = 5; + for (int i = 0; i < 10; i++) { + x*=2; + } + } +} \ No newline at end of file diff --git a/test/langtools/tools/javap/ClassWriterTableIndentTest.java b/test/langtools/tools/javap/ClassWriterTableIndentTest.java index f4f4c23c826..ccdfd9d69e7 100644 --- a/test/langtools/tools/javap/ClassWriterTableIndentTest.java +++ b/test/langtools/tools/javap/ClassWriterTableIndentTest.java @@ -52,7 +52,7 @@ public void run() { * line 145: 14 * ... */ - List runArgsList = List.of(new String[]{"-c", "-l"}, new String[]{"-v"}, new String[]{"-l"}); + List runArgsList = List.of(new String[]{"-c", "-l"}, new String[]{"-v"}); for (String[] runArgs : runArgsList) { String output = javap(runArgs); int methodIntent = findNthMatchPrecedingSpaces(output, "public void emptyLoop();", 0); diff --git a/test/langtools/tools/javap/T4459541.java b/test/langtools/tools/javap/T4459541.java index 10887ad709f..7a05e9fc64c 100644 --- a/test/langtools/tools/javap/T4459541.java +++ b/test/langtools/tools/javap/T4459541.java @@ -90,7 +90,7 @@ File compileTestFile(File f) { String javap(File f) { StringWriter sw = new StringWriter(); PrintWriter out = new PrintWriter(sw); - int rc = com.sun.tools.javap.Main.run(new String[] { "-l", f.getPath() }, out); + int rc = com.sun.tools.javap.Main.run(new String[] { "-l", "-c", f.getPath() }, out); if (rc != 0) throw new Error("javap failed. rc=" + rc); out.close(); diff --git a/test/langtools/tools/javap/T8032814.java b/test/langtools/tools/javap/T8032814.java index 2c83fd3b71e..9472ff235dc 100644 --- a/test/langtools/tools/javap/T8032814.java +++ b/test/langtools/tools/javap/T8032814.java @@ -45,7 +45,7 @@ void run() throws Exception { + clazz.getDeclaredMethods().length; test(clazz, 0); test(clazz, count, "-v"); - test(clazz, count, "-l"); + test(clazz, count, "-c", "-l"); test(clazz, count, "-v", "-l"); if (errors > 0) From 2beb2b602bf20f1ec36e6244eca1a2eb50baccb4 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 29 Nov 2024 17:00:03 +0000 Subject: [PATCH 030/171] 8345234: Build system erroneously treats 32-bit x86 Zero as deprecated Reviewed-by: ihse --- make/autoconf/platform.m4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 5b363e0704a..937d8c37927 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -666,7 +666,10 @@ AC_DEFUN([PLATFORM_CHECK_DEPRECATION], [ AC_ARG_ENABLE(deprecated-ports, [AS_HELP_STRING([--enable-deprecated-ports@<:@=yes/no@:>@], [Suppress the error when configuring for a deprecated port @<:@no@:>@])]) - if test "x$OPENJDK_TARGET_CPU" = xx86; then + # Unfortunately, variants have not been parsed yet, so we have to check the configure option + # directly. Allow only the directly specified Zero variant, treat any other mix as containing + # something non-Zero. + if test "x$OPENJDK_TARGET_CPU" = xx86 && test "x$with_jvm_variants" != xzero; then if test "x$enable_deprecated_ports" = "xyes"; then AC_MSG_WARN([The 32-bit x86 port is deprecated and may be removed in a future release.]) else From 029ace0a1b2ff4f14965037eb56414c5c6168096 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 29 Nov 2024 18:25:44 +0000 Subject: [PATCH 031/171] 8336041: Doccheck: the jfr command doesn't show the correct command-line options Reviewed-by: dholmes --- src/jdk.jfr/share/man/jfr.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/jdk.jfr/share/man/jfr.md b/src/jdk.jfr/share/man/jfr.md index f942b97dd99..185d7cfc3b3 100644 --- a/src/jdk.jfr/share/man/jfr.md +++ b/src/jdk.jfr/share/man/jfr.md @@ -207,7 +207,7 @@ Use `jfr configure` to configure a .jfc settings file. The syntax is: `jfr configure` \[--interactive\] \[--verbose\] - \[--input \] [--output \] + \[--input <files>\] [--output <file>\] \[option=value\]* \[event-setting=value\]* `--interactive` @@ -244,9 +244,9 @@ names, categories and field layout within a flight recording file. The syntax is: -`jfr metadata` \[--categories \] - \[--events \] - \[\] +`jfr metadata` \[--categories <filter>\] + \[--events <filter>\] + \[<file>\] `--categories` <*filter*> : Select events matching a category name. The filter is a comma-separated @@ -259,7 +259,7 @@ list of names, simple and/or qualified, and/or quoted glob patterns. <*file*> : Location of the recording file (.jfr) -If the parameter is omitted, metadata from the JDK where +If the <file> parameter is omitted, metadata from the JDK where the 'jfr' tool is located will be used. #### `jfr summary` subcommand From 28ae281b42cd00f471e275db544a5d23a42df59c Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 29 Nov 2024 20:53:07 +0000 Subject: [PATCH 032/171] 8337511: Implement JEP 404: Generational Shenandoah (Experimental) Co-authored-by: Kelvin Nilsen Co-authored-by: Y. Srinivas Ramakrishna Co-authored-by: Bernd Mathiske Co-authored-by: Martin Doerr Co-authored-by: Fei Yang Reviewed-by: rkennke, shade, phh --- .../c1/shenandoahBarrierSetC1_aarch64.cpp | 8 + .../shenandoahBarrierSetAssembler_aarch64.cpp | 75 +- .../shenandoahBarrierSetAssembler_aarch64.hpp | 9 + .../c1/shenandoahBarrierSetC1_ppc.cpp | 8 + .../shenandoahBarrierSetAssembler_ppc.cpp | 72 +- .../shenandoahBarrierSetAssembler_ppc.hpp | 14 +- .../c1/shenandoahBarrierSetC1_riscv.cpp | 8 + .../shenandoahBarrierSetAssembler_riscv.cpp | 76 +- .../shenandoahBarrierSetAssembler_riscv.hpp | 9 + .../c1/shenandoahBarrierSetC1_x86.cpp | 8 + .../shenandoahBarrierSetAssembler_x86.cpp | 169 ++- .../shenandoahBarrierSetAssembler_x86.hpp | 9 + src/hotspot/share/gc/shared/cardTable.cpp | 2 +- .../share/gc/shared/gcConfiguration.cpp | 10 + src/hotspot/share/gc/shared/gcName.hpp | 4 + .../shenandoah/c1/shenandoahBarrierSetC1.cpp | 72 +- .../shenandoah/c1/shenandoahBarrierSetC1.hpp | 2 + .../shenandoah/c2/shenandoahBarrierSetC2.cpp | 138 +- .../shenandoah/c2/shenandoahBarrierSetC2.hpp | 12 + .../shenandoahAdaptiveHeuristics.cpp | 100 +- .../shenandoahAdaptiveHeuristics.hpp | 11 +- .../shenandoahAggressiveHeuristics.cpp | 4 +- .../shenandoahCompactHeuristics.cpp | 14 +- .../shenandoahGenerationalHeuristics.cpp | 290 ++++ .../shenandoahGenerationalHeuristics.hpp | 59 + .../heuristics/shenandoahGlobalHeuristics.cpp | 147 ++ .../heuristics/shenandoahGlobalHeuristics.hpp | 54 + .../heuristics/shenandoahHeuristics.cpp | 69 +- .../heuristics/shenandoahHeuristics.hpp | 93 +- .../heuristics/shenandoahOldHeuristics.cpp | 734 ++++++++++ .../heuristics/shenandoahOldHeuristics.hpp | 206 +++ .../shenandoahPassiveHeuristics.cpp | 2 +- .../heuristics/shenandoahSpaceInfo.hpp | 3 + .../heuristics/shenandoahStaticHeuristics.cpp | 4 +- .../heuristics/shenandoahYoungHeuristics.cpp | 224 +++ .../heuristics/shenandoahYoungHeuristics.hpp | 57 + .../mode/shenandoahGenerationalMode.cpp | 64 + .../mode/shenandoahGenerationalMode.hpp | 39 + .../gc/shenandoah/mode/shenandoahMode.cpp | 54 + .../gc/shenandoah/mode/shenandoahMode.hpp | 8 +- .../shenandoah/mode/shenandoahPassiveMode.cpp | 13 +- .../shenandoah/mode/shenandoahPassiveMode.hpp | 3 +- .../gc/shenandoah/mode/shenandoahSATBMode.cpp | 25 +- .../gc/shenandoah/mode/shenandoahSATBMode.hpp | 1 - .../gc/shenandoah/shenandoahAffiliation.hpp | 60 + .../gc/shenandoah/shenandoahAgeCensus.cpp | 383 +++++ .../gc/shenandoah/shenandoahAgeCensus.hpp | 229 +++ .../gc/shenandoah/shenandoahAllocRequest.hpp | 97 +- .../gc/shenandoah/shenandoahArguments.cpp | 19 +- .../share/gc/shenandoah/shenandoahAsserts.cpp | 52 +- .../share/gc/shenandoah/shenandoahAsserts.hpp | 23 + .../gc/shenandoah/shenandoahBarrierSet.cpp | 35 +- .../gc/shenandoah/shenandoahBarrierSet.hpp | 16 +- .../shenandoahBarrierSet.inline.hpp | 139 +- .../gc/shenandoah/shenandoahCardStats.cpp | 43 + .../gc/shenandoah/shenandoahCardStats.hpp | 132 ++ .../gc/shenandoah/shenandoahCardTable.cpp | 105 ++ .../gc/shenandoah/shenandoahCardTable.hpp | 87 ++ .../gc/shenandoah/shenandoahClosures.hpp | 9 +- .../shenandoah/shenandoahClosures.inline.hpp | 20 +- .../gc/shenandoah/shenandoahCollectionSet.cpp | 44 +- .../gc/shenandoah/shenandoahCollectionSet.hpp | 54 +- .../shenandoahCollectionSet.inline.hpp | 17 + .../shenandoahCollectionSetPreselector.hpp | 51 + .../shenandoah/shenandoahCollectorPolicy.cpp | 52 +- .../shenandoah/shenandoahCollectorPolicy.hpp | 27 +- .../gc/shenandoah/shenandoahConcurrentGC.cpp | 355 ++++- .../gc/shenandoah/shenandoahConcurrentGC.hpp | 41 +- .../shenandoah/shenandoahConcurrentMark.cpp | 142 +- .../shenandoah/shenandoahConcurrentMark.hpp | 8 +- .../gc/shenandoah/shenandoahControlThread.cpp | 24 +- .../gc/shenandoah/shenandoahDegeneratedGC.cpp | 146 +- .../gc/shenandoah/shenandoahDegeneratedGC.hpp | 6 +- .../gc/shenandoah/shenandoahEvacInfo.hpp | 120 ++ .../gc/shenandoah/shenandoahEvacTracker.cpp | 173 +++ .../gc/shenandoah/shenandoahEvacTracker.hpp | 81 ++ .../share/gc/shenandoah/shenandoahFreeSet.cpp | 1263 +++++++++++++---- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 165 ++- .../share/gc/shenandoah/shenandoahFullGC.cpp | 159 ++- .../share/gc/shenandoah/shenandoahGC.cpp | 2 + .../share/gc/shenandoah/shenandoahGC.hpp | 1 + .../gc/shenandoah/shenandoahGeneration.cpp | 1035 ++++++++++++++ .../gc/shenandoah/shenandoahGeneration.hpp | 244 ++++ .../shenandoah/shenandoahGenerationSizer.cpp | 209 +++ .../shenandoah/shenandoahGenerationSizer.hpp | 93 ++ .../shenandoah/shenandoahGenerationType.hpp | 11 +- .../shenandoahGenerationalControlThread.cpp | 841 +++++++++++ .../shenandoahGenerationalControlThread.hpp | 129 ++ .../shenandoahGenerationalEvacuationTask.cpp | 326 +++++ .../shenandoahGenerationalEvacuationTask.hpp | 58 + .../shenandoahGenerationalFullGC.cpp | 390 +++++ .../shenandoahGenerationalFullGC.hpp | 122 ++ .../shenandoah/shenandoahGenerationalHeap.cpp | 1140 +++++++++++++++ .../shenandoah/shenandoahGenerationalHeap.hpp | 169 +++ .../shenandoah/shenandoahGlobalGeneration.cpp | 146 ++ .../shenandoah/shenandoahGlobalGeneration.hpp | 73 + .../share/gc/shenandoah/shenandoahHeap.cpp | 740 ++++++---- .../share/gc/shenandoah/shenandoahHeap.hpp | 223 ++- .../gc/shenandoah/shenandoahHeap.inline.hpp | 170 ++- .../gc/shenandoah/shenandoahHeapRegion.cpp | 284 +++- .../gc/shenandoah/shenandoahHeapRegion.hpp | 105 +- .../shenandoahHeapRegion.inline.hpp | 113 +- .../shenandoahHeapRegionClosures.cpp | 89 ++ .../shenandoahHeapRegionClosures.hpp | 100 ++ .../shenandoahHeapRegionCounters.cpp | 114 +- .../shenandoahHeapRegionCounters.hpp | 49 +- .../share/gc/shenandoah/shenandoahMark.cpp | 68 +- .../share/gc/shenandoah/shenandoahMark.hpp | 38 +- .../gc/shenandoah/shenandoahMark.inline.hpp | 142 +- .../gc/shenandoah/shenandoahMarkBitMap.cpp | 24 + .../gc/shenandoah/shenandoahMarkBitMap.hpp | 3 + .../shenandoah/shenandoahMarkingContext.cpp | 57 +- .../shenandoah/shenandoahMarkingContext.hpp | 25 +- .../shenandoahMarkingContext.inline.hpp | 36 +- .../gc/shenandoah/shenandoahMemoryPool.cpp | 60 +- .../gc/shenandoah/shenandoahMemoryPool.hpp | 40 +- .../gc/shenandoah/shenandoahMmuTracker.cpp | 185 +++ .../gc/shenandoah/shenandoahMmuTracker.hpp | 106 ++ .../share/gc/shenandoah/shenandoahNMethod.cpp | 10 +- .../gc/shenandoah/shenandoahNumberSeq.cpp | 65 + .../gc/shenandoah/shenandoahNumberSeq.hpp | 2 + .../share/gc/shenandoah/shenandoahOldGC.cpp | 161 +++ .../share/gc/shenandoah/shenandoahOldGC.hpp | 48 + .../gc/shenandoah/shenandoahOldGeneration.cpp | 790 +++++++++++ .../gc/shenandoah/shenandoahOldGeneration.hpp | 324 +++++ .../gc/shenandoah/shenandoahPhaseTimings.cpp | 11 +- .../gc/shenandoah/shenandoahPhaseTimings.hpp | 24 +- .../shenandoahReferenceProcessor.cpp | 80 +- .../shenandoah/shenandoahRegulatorThread.cpp | 164 +++ .../shenandoah/shenandoahRegulatorThread.hpp | 85 ++ .../gc/shenandoah/shenandoahRootVerifier.cpp | 19 +- .../gc/shenandoah/shenandoahRootVerifier.hpp | 5 +- .../shenandoah/shenandoahSATBMarkQueueSet.hpp | 1 - .../share/gc/shenandoah/shenandoahSTWMark.cpp | 55 +- .../share/gc/shenandoah/shenandoahSTWMark.hpp | 4 +- .../shenandoah/shenandoahScanRemembered.cpp | 973 +++++++++++++ .../shenandoah/shenandoahScanRemembered.hpp | 1001 +++++++++++++ .../shenandoahScanRemembered.inline.hpp | 405 ++++++ .../shenandoah/shenandoahSharedVariables.hpp | 14 +- .../shenandoah/shenandoahStackWatermark.cpp | 23 +- .../shenandoahStringDedup.inline.hpp | 25 +- .../shenandoah/shenandoahThreadLocalData.cpp | 64 + .../shenandoah/shenandoahThreadLocalData.hpp | 133 +- .../share/gc/shenandoah/shenandoahTrace.cpp | 54 + .../share/gc/shenandoah/shenandoahTrace.hpp | 42 + .../share/gc/shenandoah/shenandoahUtils.cpp | 16 +- .../share/gc/shenandoah/shenandoahUtils.hpp | 20 +- .../gc/shenandoah/shenandoahVMOperations.cpp | 26 + .../gc/shenandoah/shenandoahVMOperations.hpp | 7 +- .../gc/shenandoah/shenandoahVerifier.cpp | 534 ++++++- .../gc/shenandoah/shenandoahVerifier.hpp | 62 +- .../gc/shenandoah/shenandoahWorkerPolicy.cpp | 4 + .../gc/shenandoah/shenandoahWorkerPolicy.hpp | 3 + .../shenandoah/shenandoahYoungGeneration.cpp | 119 ++ .../shenandoah/shenandoahYoungGeneration.hpp | 85 ++ .../gc/shenandoah/shenandoah_globals.hpp | 250 +++- .../gc/shenandoah/vmStructs_shenandoah.hpp | 8 +- src/hotspot/share/jfr/metadata/metadata.xml | 17 + src/hotspot/share/prims/whitebox.cpp | 3 +- .../gc/shenandoah/ShenandoahGeneration.java | 59 + .../ShenandoahGenerationalHeap.java | 33 + .../hotspot/gc/shenandoah/ShenandoahHeap.java | 8 +- .../sun/jvm/hotspot/memory/Universe.java | 2 + src/jdk.jfr/share/conf/jfr/default.jfc | 4 + src/jdk.jfr/share/conf/jfr/profile.jfc | 4 + .../shenandoah/test_shenandoahNumberSeq.cpp | 137 +- .../test_shenandoahOldGeneration.cpp | 200 +++ .../test_shenandoahOldHeuristic.cpp | 366 +++++ test/hotspot/jtreg/ProblemList.txt | 1 + .../jtreg/gc/TestAllocHumongousFragment.java | 18 + .../gc/shenandoah/TestAllocIntArrays.java | 23 + .../gc/shenandoah/TestAllocObjectArrays.java | 35 + .../jtreg/gc/shenandoah/TestAllocObjects.java | 16 + .../gc/shenandoah/TestArrayCopyCheckCast.java | 10 +- .../gc/shenandoah/TestArrayCopyStress.java | 12 +- .../TestDynamicSoftMaxHeapSize.java | 12 + .../jtreg/gc/shenandoah/TestEvilSyncBug.java | 20 +- .../gc/shenandoah/TestGCThreadGroups.java | 19 + .../jtreg/gc/shenandoah/TestHeapUncommit.java | 27 +- .../jtreg/gc/shenandoah/TestJcmdHeapDump.java | 13 + .../shenandoah/TestLargeObjectAlignment.java | 17 +- .../jtreg/gc/shenandoah/TestLotsOfCycles.java | 11 + .../gc/shenandoah/TestObjItrWithHeapDump.java | 6 +- .../jtreg/gc/shenandoah/TestPeriodicGC.java | 46 +- .../TestReferenceRefersToShenandoah.java | 37 +- .../TestReferenceShortcutCycle.java | 15 +- .../gc/shenandoah/TestRefprocSanity.java | 16 + .../gc/shenandoah/TestRegionSampling.java | 10 + .../shenandoah/TestRegionSamplingLogging.java | 68 + .../jtreg/gc/shenandoah/TestResizeTLAB.java | 21 + .../gc/shenandoah/TestRetainObjects.java | 26 + ....java => TestShenandoahRegionLogging.java} | 33 +- .../jtreg/gc/shenandoah/TestSieveObjects.java | 23 + .../jtreg/gc/shenandoah/TestSmallHeap.java | 14 +- .../jtreg/gc/shenandoah/TestStringDedup.java | 15 + .../gc/shenandoah/TestStringDedupStress.java | 17 + .../shenandoah/TestStringInternCleanup.java | 17 + .../gc/shenandoah/TestVerifyJCStress.java | 6 + .../jtreg/gc/shenandoah/TestVerifyLevels.java | 13 +- .../jtreg/gc/shenandoah/TestWithLogLevel.java | 14 +- .../gc/shenandoah/TestWrongArrayMember.java | 4 +- .../gc/shenandoah/compiler/TestClone.java | 125 ++ .../shenandoah/compiler/TestReferenceCAS.java | 27 + .../generational/TestConcurrentEvac.java | 185 +++ .../generational/TestOldGrowthTriggers.java | 110 ++ .../generational/TestSimpleGenerational.java | 120 ++ .../gc/shenandoah/jni/TestJNICritical.java | 13 +- .../gc/shenandoah/jni/TestJNIGlobalRefs.java | 20 + .../gc/shenandoah/jni/TestPinnedGarbage.java | 17 + .../gc/shenandoah/jvmti/TestHeapDump.java | 41 + .../mxbeans/TestChurnNotifications.java | 29 +- .../shenandoah/mxbeans/TestMemoryMXBeans.java | 16 +- .../shenandoah/mxbeans/TestMemoryPools.java | 12 +- .../mxbeans/TestPauseNotifications.java | 12 + .../gc/shenandoah/oom/TestAllocLargeObj.java | 81 -- .../oom/TestAllocLargerThanHeap.java | 76 - .../shenandoah/oom/TestAllocOutOfMemory.java | 147 ++ .../gc/shenandoah/oom/TestAllocSmallObj.java | 80 -- .../shenandoah/oom/TestClassLoaderLeak.java | 6 +- .../gc/shenandoah/oom/TestThreadFailure.java | 23 + .../gc/shenandoah/options/TestModeUnlock.java | 6 +- .../options/TestWrongBarrierDisable.java | 7 + .../options/TestWrongBarrierEnable.java | 11 +- .../gcbasher/TestGCBasherWithShenandoah.java | 38 + .../stress/gcold/TestGCOldWithShenandoah.java | 17 + .../systemgc/TestSystemGCWithShenandoah.java | 18 + ...tShenandoahEvacuationInformationEvent.java | 113 ++ test/lib/jdk/test/lib/jfr/EventNames.java | 1 + 228 files changed, 21932 insertions(+), 1723 deletions(-) create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp create mode 100644 src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp create mode 100644 src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp create mode 100644 src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.hpp create mode 100644 src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahAffiliation.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahCardStats.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahCardStats.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahCardTable.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahOldGC.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGeneration.java create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGenerationalHeap.java create mode 100644 test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp create mode 100644 test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp create mode 100644 test/hotspot/jtreg/gc/shenandoah/TestRegionSamplingLogging.java rename test/hotspot/jtreg/gc/shenandoah/{TestParallelRefprocSanity.java => TestShenandoahRegionLogging.java} (53%) create mode 100644 test/hotspot/jtreg/gc/shenandoah/generational/TestConcurrentEvac.java create mode 100644 test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java create mode 100644 test/hotspot/jtreg/gc/shenandoah/generational/TestSimpleGenerational.java delete mode 100644 test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargeObj.java delete mode 100644 test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargerThanHeap.java create mode 100644 test/hotspot/jtreg/gc/shenandoah/oom/TestAllocOutOfMemory.java delete mode 100644 test/hotspot/jtreg/gc/shenandoah/oom/TestAllocSmallObj.java create mode 100644 test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahEvacuationInformationEvent.java diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index 261502d5823..666335330ed 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -86,6 +87,10 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI LIR_Opr result = gen->new_register(T_INT); __ append(new LIR_OpShenandoahCompareAndSwap(addr, cmp_value.result(), new_value.result(), t1, t2, result)); + + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), new_value.result()); + } return result; } } @@ -113,6 +118,9 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr, result /* pre_val */); } + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), result); + } } return result; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 84d06dbcc7b..f682e86cdfb 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -31,6 +32,7 @@ #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interp_masm.hpp" #include "runtime/javaThread.hpp" @@ -77,6 +79,13 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec } } +void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, + Register start, Register count, Register tmp, RegSet saved_regs) { + if (ShenandoahCardBarrier && is_oop) { + gen_write_ref_array_post_barrier(masm, decorators, start, count, tmp, saved_regs); + } +} + void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -362,6 +371,26 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d } } +void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + __ lsr(obj, obj, CardTable::card_shift()); + + assert(CardTable::dirty_card_val() == 0, "must be"); + + __ load_byte_map_base(rscratch1); + + if (UseCondCardMark) { + Label L_already_dirty; + __ ldrb(rscratch2, Address(obj, rscratch1)); + __ cbz(rscratch2, L_already_dirty); + __ strb(zr, Address(obj, rscratch1)); + __ bind(L_already_dirty); + } else { + __ strb(zr, Address(obj, rscratch1)); + } +} + void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { bool on_oop = is_reference_type(type); @@ -387,18 +416,13 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet val != noreg /* tosca_live */, false /* expand_call */); - if (val == noreg) { - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); - } else { - // Barrier needs uncompressed oop for region cross check. - Register new_val = val; - if (UseCompressedOops) { - new_val = rscratch2; - __ mov(new_val, val); - } - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - } + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); + bool in_heap = (decorators & IN_HEAP) != 0; + bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; + if (needs_post_barrier) { + store_check(masm, tmp3); + } } void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, @@ -581,6 +605,35 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, } } +void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register start, Register count, Register scratch, RegSet saved_regs) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + Label L_loop, L_done; + const Register end = count; + + // Zero count? Nothing to do. + __ cbz(count, L_done); + + // end = start + count << LogBytesPerHeapOop + // last element address to make inclusive + __ lea(end, Address(start, count, Address::lsl(LogBytesPerHeapOop))); + __ sub(end, end, BytesPerHeapOop); + __ lsr(start, start, CardTable::card_shift()); + __ lsr(end, end, CardTable::card_shift()); + + // number of bytes to copy + __ sub(count, end, start); + + __ load_byte_map_base(scratch); + __ add(start, start, scratch); + __ bind(L_loop); + __ strb(zr, Address(start, count)); + __ subs(count, count, 1); + __ br(Assembler::GE, L_loop); + __ bind(L_done); +} + #undef __ #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index ee11b2e73f7..a12d4e2beec 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -55,10 +56,16 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool tosca_live, bool expand_call); + void store_check(MacroAssembler* masm, Register obj); + void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators); + void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register start, Register count, + Register scratch, RegSet saved_regs); + public: virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } @@ -71,6 +78,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, Register src, Register dst, Register count, RegSet saved_regs); + virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, + Register start, Register count, Register tmp, RegSet saved_regs); virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2); virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp index 5f87281bdf9..ce12d1fcf03 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp @@ -102,6 +102,10 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess &access, LI __ append(new LIR_OpShenandoahCompareAndSwap(addr, cmp_value.result(), new_value.result(), t1, t2, result)); + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), new_value.result()); + } + return result; } } @@ -132,6 +136,10 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess &access, LIRIt if (ShenandoahSATBBarrier) { pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr, result); } + + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), result); + } } return result; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 53150807212..796c32a58df 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -36,6 +36,7 @@ #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "interpreter/interpreter.hpp" #include "macroAssembler_ppc.hpp" #include "runtime/javaThread.hpp" @@ -76,8 +77,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, DecoratorSet decorators, BasicType type, Register src, Register dst, Register count, Register preserve1, Register preserve2) { - __ block_comment("arraycopy_prologue (shenandoahgc) {"); - Register R11_tmp = R11_scratch1; assert_different_registers(src, dst, count, R11_tmp, noreg); @@ -100,6 +99,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, Dec return; } + __ block_comment("arraycopy_prologue (shenandoahgc) {"); Label skip_prologue; // Fast path: Array is of length zero. @@ -173,6 +173,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, Dec __ block_comment("} arraycopy_prologue (shenandoahgc)"); } +void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register dst, Register count, + Register preserve) { + if (ShenandoahCardBarrier && is_reference_type(type)) { + __ block_comment("arraycopy_epilogue (shenandoahgc) {"); + gen_write_ref_array_post_barrier(masm, decorators, dst, count, preserve); + __ block_comment("} arraycopy_epilogue (shenandoahgc)"); + } +} + // The to-be-enqueued value can either be determined // - dynamically by passing the reference's address information (load mode) or // - statically by passing a register the value is stored in (preloaded mode) @@ -576,6 +586,25 @@ void ShenandoahBarrierSetAssembler::load_at( } } +void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + ShenandoahBarrierSet* ctbs = ShenandoahBarrierSet::barrier_set(); + CardTable* ct = ctbs->card_table(); + assert_different_registers(base, tmp, R0); + + if (ind_or_offs.is_constant()) { + __ add_const_optimized(base, base, ind_or_offs.as_constant(), tmp); + } else { + __ add(base, ind_or_offs.as_register(), base); + } + + __ load_const_optimized(tmp, (address)ct->byte_map_base(), R0); + __ srdi(base, base, CardTable::card_shift()); + __ li(R0, CardTable::dirty_card_val()); + __ stbx(R0, tmp, base); +} + // base: Base register of the reference's address. // ind_or_offs: Index or offset of the reference's address. // val: To-be-stored value/reference's new value. @@ -594,6 +623,11 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet val, tmp1, tmp2, tmp3, preservation_level); + + // No need for post barrier if storing NULL + if (ShenandoahCardBarrier && is_reference_type(type) && val != noreg) { + store_check(masm, base, ind_or_offs, tmp1); + } } void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler *masm, @@ -743,6 +777,40 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler *masm, Register b __ block_comment("} cmpxchg_oop (shenandoahgc)"); } +void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register addr, Register count, Register preserve) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); + CardTable* ct = bs->card_table(); + assert_different_registers(addr, count, R0); + + Label L_skip_loop, L_store_loop; + + __ sldi_(count, count, LogBytesPerHeapOop); + + // Zero length? Skip. + __ beq(CCR0, L_skip_loop); + + __ addi(count, count, -BytesPerHeapOop); + __ add(count, addr, count); + // Use two shifts to clear out those low order two bits! (Cannot opt. into 1.) + __ srdi(addr, addr, CardTable::card_shift()); + __ srdi(count, count, CardTable::card_shift()); + __ subf(count, addr, count); + __ add_const_optimized(addr, addr, (address)ct->byte_map_base(), R0); + __ addi(count, count, 1); + __ li(R0, 0); + __ mtctr(count); + + // Byte store loop + __ bind(L_store_loop); + __ stb(R0, 0, addr); + __ addi(addr, addr, 1); + __ bdnz(L_store_loop); + __ bind(L_skip_loop); +} + #undef __ #ifdef COMPILER1 diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 2e56187c169..6ee70b4b4ea 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -51,6 +51,10 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp1, Register tmp2, MacroAssembler::PreservationLevel preservation_level); + void store_check(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp); + void load_reference_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, @@ -60,6 +64,10 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { /* ==== Helper methods for barrier implementations ==== */ void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp); + void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register addr, Register count, + Register preserve); + public: virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } @@ -95,7 +103,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { /* ==== Access api ==== */ virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, - Register src, Register dst, Register count, Register preserve1, Register preserve2); + Register src, Register dst, Register count, + Register preserve1, Register preserve2); + virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register dst, Register count, + Register preserve); virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register base, RegisterOrConstant ind_or_offs, Register val, diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp index f503cb762e7..d15b3aa31f9 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp @@ -78,9 +78,14 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI LIR_Opr result = gen->new_register(T_INT); __ append(new LIR_OpShenandoahCompareAndSwap(addr, cmp_value.result(), new_value.result(), tmp1, tmp2, result)); + + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), new_value.result()); + } return result; } } + return BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); } @@ -105,6 +110,9 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr, result /* pre_val */); } + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), result); + } } return result; diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index cc73d14a756..257d445f011 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -32,6 +32,7 @@ #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interp_masm.hpp" #include "runtime/javaThread.hpp" @@ -81,6 +82,13 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec } } +void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, + Register start, Register count, Register tmp, RegSet saved_regs) { + if (ShenandoahCardBarrier && is_oop) { + gen_write_ref_array_post_barrier(masm, decorators, start, count, tmp, saved_regs); + } +} + void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -382,6 +390,27 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, } } +void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { + assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); + + __ srli(obj, obj, CardTable::card_shift()); + + assert(CardTable::dirty_card_val() == 0, "must be"); + + __ load_byte_map_base(t1); + __ add(t1, obj, t1); + + if (UseCondCardMark) { + Label L_already_dirty; + __ lbu(t0, Address(t1)); + __ beqz(t0, L_already_dirty); + __ sb(zr, Address(t1)); + __ bind(L_already_dirty); + } else { + __ sb(zr, Address(t1)); + } +} + void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { bool on_oop = is_reference_type(type); @@ -407,16 +436,12 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet val != noreg /* tosca_live */, false /* expand_call */); - if (val == noreg) { - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); - } else { - // Barrier needs uncompressed oop for region cross check. - Register new_val = val; - if (UseCompressedOops) { - new_val = t1; - __ mv(new_val, val); - } - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); + + bool in_heap = (decorators & IN_HEAP) != 0; + bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; + if (needs_post_barrier) { + store_check(masm, tmp3); } } @@ -524,6 +549,37 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, __ bind(done); } +void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register start, Register count, Register tmp, RegSet saved_regs) { + assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); + + Label L_loop, L_done; + const Register end = count; + + // Zero count? Nothing to do. + __ beqz(count, L_done); + + // end = start + count << LogBytesPerHeapOop + // last element address to make inclusive + __ shadd(end, count, start, tmp, LogBytesPerHeapOop); + __ sub(end, end, BytesPerHeapOop); + __ srli(start, start, CardTable::card_shift()); + __ srli(end, end, CardTable::card_shift()); + + // number of bytes to copy + __ sub(count, end, start); + + __ load_byte_map_base(tmp); + __ add(start, start, tmp); + + __ bind(L_loop); + __ add(tmp, start, count); + __ sb(zr, Address(tmp)); + __ sub(count, count, 1); + __ bgez(count, L_loop); + __ bind(L_done); +} + #undef __ #ifdef COMPILER1 diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index bfdea7f607e..7d12cc8cbb6 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -57,10 +57,16 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool tosca_live, bool expand_call); + void store_check(MacroAssembler* masm, Register obj); + void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators); + void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register start, Register count, + Register tmp, RegSet saved_regs); + public: virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } @@ -75,6 +81,9 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, Register src, Register dst, Register count, RegSet saved_regs); + virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, + Register start, Register count, Register tmp, RegSet saved_regs); + virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2); virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 032b33f40e0..eb6da25d1bc 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -84,6 +85,10 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI LIR_Opr result = gen->new_register(T_INT); __ append(new LIR_OpShenandoahCompareAndSwap(addr, cmp_value.result(), new_value.result(), t1, t2, result)); + + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), new_value.result()); + } return result; } } @@ -113,6 +118,9 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr, result /* pre_val */); } + if (ShenandoahCardBarrier) { + post_barrier(access, access.resolved_addr(), result); + } } return result; diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index a7682fe0c38..a452850b1e8 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -31,6 +32,7 @@ #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "interpreter/interpreter.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" @@ -120,6 +122,29 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; if (is_reference_type(type)) { + if (ShenandoahCardBarrier) { + bool checkcast = (decorators & ARRAYCOPY_CHECKCAST) != 0; + bool disjoint = (decorators & ARRAYCOPY_DISJOINT) != 0; + bool obj_int = type == T_OBJECT LP64_ONLY(&& UseCompressedOops); + + // We need to save the original element count because the array copy stub + // will destroy the value and we need it for the card marking barrier. +#ifdef _LP64 + if (!checkcast) { + if (!obj_int) { + // Save count for barrier + __ movptr(r11, count); + } else if (disjoint) { + // Save dst in r11 in the disjoint case + __ movq(r11, dst); + } + } +#else + if (disjoint) { + __ mov(rdx, dst); // save 'to' + } +#endif + } if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { #ifdef _LP64 @@ -140,10 +165,10 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec #endif assert_different_registers(src, dst, count, thread); - Label done; + Label L_done; // Short-circuit if count == 0. __ testptr(count, count); - __ jcc(Assembler::zero, done); + __ jcc(Assembler::zero, L_done); // Avoid runtime call when not active. Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); @@ -154,7 +179,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec flags = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING; } __ testb(gc_state, flags); - __ jcc(Assembler::zero, done); + __ jcc(Assembler::zero, L_done); save_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ false); @@ -174,13 +199,43 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ false); - __ bind(done); + __ bind(L_done); NOT_LP64(__ pop(thread);) } } } +void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register src, Register dst, Register count) { + + if (ShenandoahCardBarrier && is_reference_type(type)) { + bool checkcast = (decorators & ARRAYCOPY_CHECKCAST) != 0; + bool disjoint = (decorators & ARRAYCOPY_DISJOINT) != 0; + bool obj_int = type == T_OBJECT LP64_ONLY(&& UseCompressedOops); + Register tmp = rax; + +#ifdef _LP64 + if (!checkcast) { + if (!obj_int) { + // Save count for barrier + count = r11; + } else if (disjoint) { + // Use the saved dst in the disjoint case + dst = r11; + } + } else { + tmp = rscratch1; + } +#else + if (disjoint) { + __ mov(dst, rdx); // restore 'to' + } +#endif + gen_write_ref_array_post_barrier(masm, decorators, dst, count, tmp); + } +} + void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -555,6 +610,49 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d } } +void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + // Does a store check for the oop in register obj. The content of + // register obj is destroyed afterwards. + + ShenandoahBarrierSet* ctbs = ShenandoahBarrierSet::barrier_set(); + CardTable* ct = ctbs->card_table(); + + __ shrptr(obj, CardTable::card_shift()); + + Address card_addr; + + // The calculation for byte_map_base is as follows: + // byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift); + // So this essentially converts an address to a displacement and it will + // never need to be relocated. On 64-bit however the value may be too + // large for a 32-bit displacement. + intptr_t byte_map_base = (intptr_t)ct->byte_map_base(); + if (__ is_simm32(byte_map_base)) { + card_addr = Address(noreg, obj, Address::times_1, byte_map_base); + } else { + // By doing it as an ExternalAddress 'byte_map_base' could be converted to a rip-relative + // displacement and done in a single instruction given favorable mapping and a + // smarter version of as_Address. However, 'ExternalAddress' generates a relocation + // entry and that entry is not properly handled by the relocation code. + AddressLiteral cardtable((address)byte_map_base, relocInfo::none); + Address index(noreg, obj, Address::times_1); + card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch1); + } + + int dirty = CardTable::dirty_card_val(); + if (UseCondCardMark) { + Label L_already_dirty; + __ cmpb(card_addr, dirty); + __ jccb(Assembler::equal, L_already_dirty); + __ movb(card_addr, dirty); + __ bind(L_already_dirty); + } else { + __ movb(card_addr, dirty); + } +} + void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { @@ -592,7 +690,13 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet val != noreg /* tosca_live */, false /* expand_call */); } + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); + if (val != noreg) { + if (ShenandoahCardBarrier) { + store_check(masm, tmp1); + } + } NOT_LP64(imasm->restore_bcp()); } else { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); @@ -787,6 +891,63 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm, } } +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +#define TIMES_OOP (UseCompressedOops ? Address::times_4 : Address::times_8) + +void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register addr, Register count, + Register tmp) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); + CardTable* ct = bs->card_table(); + intptr_t disp = (intptr_t) ct->byte_map_base(); + + Label L_loop, L_done; + const Register end = count; + assert_different_registers(addr, end); + + // Zero count? Nothing to do. + __ testl(count, count); + __ jccb(Assembler::zero, L_done); + +#ifdef _LP64 + __ leaq(end, Address(addr, count, TIMES_OOP, 0)); // end == addr+count*oop_size + __ subptr(end, BytesPerHeapOop); // end - 1 to make inclusive + __ shrptr(addr, CardTable::card_shift()); + __ shrptr(end, CardTable::card_shift()); + __ subptr(end, addr); // end --> cards count + + __ mov64(tmp, disp); + __ addptr(addr, tmp); + + __ BIND(L_loop); + __ movb(Address(addr, count, Address::times_1), 0); + __ decrement(count); + __ jccb(Assembler::greaterEqual, L_loop); +#else + __ lea(end, Address(addr, count, Address::times_ptr, -wordSize)); + __ shrptr(addr, CardTable::card_shift()); + __ shrptr(end, CardTable::card_shift()); + __ subptr(end, addr); // end --> count + + __ BIND(L_loop); + Address cardtable(addr, count, Address::times_1, disp); + __ movb(cardtable, 0); + __ decrement(count); + __ jccb(Assembler::greaterEqual, L_loop); +#endif + + __ BIND(L_done); +} + #undef __ #ifdef COMPILER1 diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index c8140f4a76a..ae0ad3533e1 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -56,6 +57,12 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool tosca_live, bool expand_call); + void store_check(MacroAssembler* masm, Register obj); + + void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, + Register addr, Register count, + Register tmp); + public: #ifdef COMPILER1 void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); @@ -71,6 +78,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool exchange, Register tmp1, Register tmp2); virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register src, Register dst, Register count); + virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, + Register src, Register dst, Register count); virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp_thread); virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/share/gc/shared/cardTable.cpp b/src/hotspot/share/gc/shared/cardTable.cpp index acd4bda6e10..53d98ba817e 100644 --- a/src/hotspot/share/gc/shared/cardTable.cpp +++ b/src/hotspot/share/gc/shared/cardTable.cpp @@ -44,7 +44,7 @@ uint CardTable::_card_size = 0; uint CardTable::_card_size_in_words = 0; void CardTable::initialize_card_size() { - assert(UseG1GC || UseParallelGC || UseSerialGC, + assert(UseG1GC || UseParallelGC || UseSerialGC || UseShenandoahGC, "Initialize card size should only be called by card based collectors."); _card_size = GCCardSizeInBytes; diff --git a/src/hotspot/share/gc/shared/gcConfiguration.cpp b/src/hotspot/share/gc/shared/gcConfiguration.cpp index 824e119e696..ba977c75627 100644 --- a/src/hotspot/share/gc/shared/gcConfiguration.cpp +++ b/src/hotspot/share/gc/shared/gcConfiguration.cpp @@ -47,6 +47,11 @@ GCName GCConfiguration::young_collector() const { } if (UseShenandoahGC) { +#if INCLUDE_SHENANDOAHGC + if (ShenandoahCardBarrier) { + return ShenandoahYoung; + } +#endif return NA; } @@ -67,6 +72,11 @@ if (UseZGC) { } if (UseShenandoahGC) { +#if INCLUDE_SHENANDOAHGC + if (ShenandoahCardBarrier) { + return ShenandoahOld; + } +#endif return Shenandoah; } diff --git a/src/hotspot/share/gc/shared/gcName.hpp b/src/hotspot/share/gc/shared/gcName.hpp index b9b87c231ca..642d734f673 100644 --- a/src/hotspot/share/gc/shared/gcName.hpp +++ b/src/hotspot/share/gc/shared/gcName.hpp @@ -38,6 +38,8 @@ enum GCName { ZMinor, ZMajor, Shenandoah, + ShenandoahYoung, + ShenandoahOld, NA, GCNameEndSentinel }; @@ -56,6 +58,8 @@ class GCNameHelper { case ZMinor: return "ZGC Minor"; case ZMajor: return "ZGC Major"; case Shenandoah: return "Shenandoah"; + case ShenandoahYoung: return "Shenandoah Young"; + case ShenandoahOld: return "Shenandoah Old"; case NA: return "N/A"; default: ShouldNotReachHere(); return nullptr; } diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 0e8b02d247e..ade0504b973 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2024, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,6 +26,7 @@ #include "precompiled.hpp" #include "c1/c1_IR.hpp" #include "gc/shared/satbMarkQueue.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" @@ -72,7 +74,6 @@ void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, __ load(gc_state_addr, flag_val); // Create a mask to test if the marking bit is set. - // TODO: can we directly test if bit is set? LIR_Opr mask = LIR_OprFact::intConst(ShenandoahHeap::MARKING); LIR_Opr mask_reg = gen->new_register(T_INT); __ move(mask, mask_reg); @@ -190,6 +191,16 @@ void ShenandoahBarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) } } BarrierSetC1::store_at_resolved(access, value); + + if (ShenandoahCardBarrier && access.is_oop()) { + DecoratorSet decorators = access.decorators(); + bool is_array = (decorators & IS_ARRAY) != 0; + bool on_anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; + + bool precise = is_array || on_anonymous; + LIR_Opr post_addr = precise ? access.resolved_addr() : access.base().opr(); + post_barrier(access, post_addr, value); + } } LIR_Opr ShenandoahBarrierSetC1::resolve_address(LIRAccess& access, bool resolve_in_register) { @@ -288,3 +299,62 @@ void ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) false, &lrb_phantom_code_gen_cl); } } + +void ShenandoahBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Opr new_val) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + DecoratorSet decorators = access.decorators(); + LIRGenerator* gen = access.gen(); + bool in_heap = (decorators & IN_HEAP) != 0; + if (!in_heap) { + return; + } + + BarrierSet* bs = BarrierSet::barrier_set(); + ShenandoahBarrierSet* ctbs = barrier_set_cast(bs); + CardTable* ct = ctbs->card_table(); + LIR_Const* card_table_base = new LIR_Const(ct->byte_map_base()); + if (addr->is_address()) { + LIR_Address* address = addr->as_address_ptr(); + // ptr cannot be an object because we use this barrier for array card marks + // and addr can point in the middle of an array. + LIR_Opr ptr = gen->new_pointer_register(); + if (!address->index()->is_valid() && address->disp() == 0) { + __ move(address->base(), ptr); + } else { + assert(address->disp() != max_jint, "lea doesn't support patched addresses!"); + __ leal(addr, ptr); + } + addr = ptr; + } + assert(addr->is_register(), "must be a register at this point"); + + LIR_Opr tmp = gen->new_pointer_register(); + if (two_operand_lir_form) { + __ move(addr, tmp); + __ unsigned_shift_right(tmp, CardTable::card_shift(), tmp); + } else { + __ unsigned_shift_right(addr, CardTable::card_shift(), tmp); + } + + LIR_Address* card_addr; + if (gen->can_inline_as_constant(card_table_base)) { + card_addr = new LIR_Address(tmp, card_table_base->as_jint(), T_BYTE); + } else { + card_addr = new LIR_Address(tmp, gen->load_constant(card_table_base), T_BYTE); + } + + LIR_Opr dirty = LIR_OprFact::intConst(CardTable::dirty_card_val()); + if (UseCondCardMark) { + LIR_Opr cur_value = gen->new_register(T_INT); + __ move(card_addr, cur_value); + + LabelObj* L_already_dirty = new LabelObj(); + __ cmp(lir_cond_equal, cur_value, dirty); + __ branch(lir_cond_equal, L_already_dirty->label()); + __ move(dirty, card_addr); + __ branch_destination(L_already_dirty->label()); + } else { + __ move(dirty, card_addr); + } +} diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 98b2aad8871..6c7ef49080f 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -243,6 +243,8 @@ class ShenandoahBarrierSetC1 : public BarrierSetC1 { virtual LIR_Opr atomic_xchg_at_resolved(LIRAccess& access, LIRItem& value); + void post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Opr new_val); + public: virtual void generate_c1_runtime_stubs(BufferBlob* buffer_blob); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 691c78cd024..b906ae2ca0b 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2023, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,6 +27,7 @@ #include "classfile/javaClasses.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" @@ -432,6 +434,90 @@ void ShenandoahBarrierSetC2::insert_pre_barrier(GraphKit* kit, Node* base_oop, N kit->final_sync(ideal); } +Node* ShenandoahBarrierSetC2::byte_map_base_node(GraphKit* kit) const { + BarrierSet* bs = BarrierSet::barrier_set(); + ShenandoahBarrierSet* ctbs = barrier_set_cast(bs); + CardTable::CardValue* card_table_base = ctbs->card_table()->byte_map_base(); + if (card_table_base != nullptr) { + return kit->makecon(TypeRawPtr::make((address)card_table_base)); + } else { + return kit->null(); + } +} + +void ShenandoahBarrierSetC2::post_barrier(GraphKit* kit, + Node* ctl, + Node* oop_store, + Node* obj, + Node* adr, + uint adr_idx, + Node* val, + BasicType bt, + bool use_precise) const { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + // No store check needed if we're storing a null. + if (val != nullptr && val->is_Con()) { + // must be either an oop or NULL + const Type* t = val->bottom_type(); + if (t == TypePtr::NULL_PTR || t == Type::TOP) + return; + } + + if (ReduceInitialCardMarks && obj == kit->just_allocated_object(kit->control())) { + // We can skip marks on a freshly-allocated object in Eden. + // Keep this code in sync with new_deferred_store_barrier() in runtime.cpp. + // That routine informs GC to take appropriate compensating steps, + // upon a slow-path allocation, so as to make this card-mark + // elision safe. + return; + } + + if (!use_precise) { + // All card marks for a (non-array) instance are in one place: + adr = obj; + } + // (Else it's an array (or unknown), and we want more precise card marks.) + assert(adr != nullptr, ""); + + IdealKit ideal(kit, true); + + // Convert the pointer to an int prior to doing math on it + Node* cast = __ CastPX(__ ctrl(), adr); + + // Divide by card size + Node* card_offset = __ URShiftX( cast, __ ConI(CardTable::card_shift()) ); + + // Combine card table base and card offset + Node* card_adr = __ AddP(__ top(), byte_map_base_node(kit), card_offset ); + + // Get the alias_index for raw card-mark memory + int adr_type = Compile::AliasIdxRaw; + Node* zero = __ ConI(0); // Dirty card value + + if (UseCondCardMark) { + // The classic GC reference write barrier is typically implemented + // as a store into the global card mark table. Unfortunately + // unconditional stores can result in false sharing and excessive + // coherence traffic as well as false transactional aborts. + // UseCondCardMark enables MP "polite" conditional card mark + // stores. In theory we could relax the load from ctrl() to + // no_ctrl, but that doesn't buy much latitude. + Node* card_val = __ load( __ ctrl(), card_adr, TypeInt::BYTE, T_BYTE, adr_type); + __ if_then(card_val, BoolTest::ne, zero); + } + + // Smash zero into card + __ store(__ ctrl(), card_adr, zero, T_BYTE, adr_type, MemNode::unordered); + + if (UseCondCardMark) { + __ end_if(); + } + + // Final sync IdealKit and GraphKit. + kit->final_sync(ideal); +} + #undef __ const TypeFunc* ShenandoahBarrierSetC2::write_ref_field_pre_Type() { @@ -499,8 +585,22 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); shenandoah_write_barrier_pre(kit, true /* do_load */, /*kit->control(),*/ access.base(), adr, adr_idx, val.node(), static_cast(val.type()), nullptr /* pre_val */, access.type()); + + Node* result = BarrierSetC2::store_at_resolved(access, val); + + if (ShenandoahCardBarrier) { + const bool anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; + const bool is_array = (decorators & IS_ARRAY) != 0; + const bool use_precise = is_array || anonymous; + post_barrier(kit, kit->control(), access.raw_access(), access.base(), + adr, adr_idx, val.node(), access.type(), use_precise); + } + return result; + } else { + assert(access.is_opt_access(), "only for optimization passes"); + assert(((decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0 || !ShenandoahSATBBarrier) && (decorators & C2_ARRAY_COPY) != 0, "unexpected caller of this code"); + return BarrierSetC2::store_at_resolved(access, val); } - return BarrierSetC2::store_at_resolved(access, val); } Node* ShenandoahBarrierSetC2::load_at_resolved(C2Access& access, const Type* val_type) const { @@ -571,7 +671,7 @@ Node* ShenandoahBarrierSetC2::load_at_resolved(C2Access& access, const Type* val } Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, - Node* new_val, const Type* value_type) const { + Node* new_val, const Type* value_type) const { GraphKit* kit = access.kit(); if (access.is_oop()) { shenandoah_write_barrier_pre(kit, false /* do_load */, @@ -612,6 +712,10 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess } #endif load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(nullptr, load_store, access.decorators())); + if (ShenandoahCardBarrier) { + post_barrier(kit, kit->control(), access.raw_access(), access.base(), + access.addr().node(), access.alias_idx(), new_val, T_OBJECT, true); + } return load_store; } return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); @@ -666,6 +770,10 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAcces } access.set_raw_access(load_store); pin_atomic_op(access); + if (ShenandoahCardBarrier) { + post_barrier(kit, kit->control(), access.raw_access(), access.base(), + access.addr().node(), access.alias_idx(), new_val, T_OBJECT, true); + } return load_store; } return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); @@ -679,6 +787,10 @@ Node* ShenandoahBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& acces shenandoah_write_barrier_pre(kit, false /* do_load */, nullptr, nullptr, max_juint, nullptr, nullptr, result /* pre_val */, T_OBJECT); + if (ShenandoahCardBarrier) { + post_barrier(kit, kit->control(), access.raw_access(), access.base(), + access.addr().node(), access.alias_idx(), val, T_OBJECT, true); + } } return result; } @@ -852,9 +964,25 @@ void ShenandoahBarrierSetC2::unregister_potential_barrier_node(Node* node) const } } -void ShenandoahBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* n) const { - if (is_shenandoah_wb_pre_call(n)) { - shenandoah_eliminate_wb_pre(n, ¯o->igvn()); +void ShenandoahBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { + if (is_shenandoah_wb_pre_call(node)) { + shenandoah_eliminate_wb_pre(node, ¯o->igvn()); + } + if (ShenandoahCardBarrier && node->Opcode() == Op_CastP2X) { + Node* shift = node->unique_out(); + Node* addp = shift->unique_out(); + for (DUIterator_Last jmin, j = addp->last_outs(jmin); j >= jmin; --j) { + Node* mem = addp->last_out(j); + if (UseCondCardMark && mem->is_Load()) { + assert(mem->Opcode() == Op_LoadB, "unexpected code shape"); + // The load is checking if the card has been written so + // replace it with zero to fold the test. + macro->replace_node(mem, macro->intcon(0)); + continue; + } + assert(mem->is_Store(), "store required"); + macro->replace_node(mem, mem->in(MemNode::Memory)); + } } } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 6e241b39ce9..c1acde2118e 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -67,6 +67,18 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { Node* pre_val, BasicType bt) const; + Node* byte_map_base_node(GraphKit* kit) const; + + void post_barrier(GraphKit* kit, + Node* ctl, + Node* store, + Node* obj, + Node* adr, + uint adr_idx, + Node* val, + BasicType bt, + bool use_precise) const; + void insert_pre_barrier(GraphKit* kit, Node* base_oop, Node* offset, Node* pre_val, bool need_mem_bar) const; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 0bcb236ccbd..94c544a7ea3 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -24,13 +25,18 @@ #include "precompiled.hpp" +#include "gc/shared/gcCause.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" +#include "runtime/globals.hpp" #include "utilities/quickSort.hpp" // These constants are used to adjust the margin of error for the moving @@ -58,7 +64,8 @@ ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* ShenandoahHeuristics(space_info), _margin_of_error_sd(ShenandoahAdaptiveInitialConfidence), _spike_threshold_sd(ShenandoahAdaptiveInitialSpikeThreshold), - _last_trigger(OTHER) { } + _last_trigger(OTHER), + _available(Moving_Average_Samples, ShenandoahAdaptiveDecayFactor) { } ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() {} @@ -90,7 +97,7 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand size_t min_garbage = (free_target > actual_free ? (free_target - actual_free) : 0); log_info(gc, ergo)("Adaptive CSet Selection. Target Free: " SIZE_FORMAT "%s, Actual Free: " - SIZE_FORMAT "%s, Max CSet: " SIZE_FORMAT "%s, Min Garbage: " SIZE_FORMAT "%s", + SIZE_FORMAT "%s, Max Evacuation: " SIZE_FORMAT "%s, Min Garbage: " SIZE_FORMAT "%s", byte_size_in_proper_unit(free_target), proper_unit_for_byte_size(free_target), byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free), byte_size_in_proper_unit(max_cset), proper_unit_for_byte_size(max_cset), @@ -103,7 +110,7 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand size_t cur_garbage = 0; for (size_t idx = 0; idx < size; idx++) { - ShenandoahHeapRegion* r = data[idx]._region; + ShenandoahHeapRegion* r = data[idx].get_region(); size_t new_cset = cur_cset + r->get_live_data_bytes(); size_t new_garbage = cur_garbage + r->garbage(); @@ -130,17 +137,19 @@ void ShenandoahAdaptiveHeuristics::record_success_concurrent() { size_t available = _space_info->available(); - _available.add(available); double z_score = 0.0; - if (_available.sd() > 0) { - z_score = (available - _available.avg()) / _available.sd(); + double available_sd = _available.sd(); + if (available_sd > 0) { + double available_avg = _available.avg(); + z_score = (double(available) - available_avg) / available_sd; + log_debug(gc, ergo)("Available: " SIZE_FORMAT " %sB, z-score=%.3f. Average available: %.1f %sB +/- %.1f %sB.", + byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), + z_score, + byte_size_in_proper_unit(available_avg), proper_unit_for_byte_size(available_avg), + byte_size_in_proper_unit(available_sd), proper_unit_for_byte_size(available_sd)); } - log_debug(gc, ergo)("Available: " SIZE_FORMAT " %sB, z-score=%.3f. Average available: %.1f %sB +/- %.1f %sB.", - byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), - z_score, - byte_size_in_proper_unit(_available.avg()), proper_unit_for_byte_size(_available.avg()), - byte_size_in_proper_unit(_available.sd()), proper_unit_for_byte_size(_available.sd())); + _available.add(double(available)); // In the case when a concurrent GC cycle completes successfully but with an // unusually small amount of available memory we will adjust our trigger @@ -195,42 +204,68 @@ static double saturate(double value, double min, double max) { return MAX2(MIN2(value, max), min); } +// Rationale: +// The idea is that there is an average allocation rate and there are occasional abnormal bursts (or spikes) of +// allocations that exceed the average allocation rate. What do these spikes look like? +// +// 1. At certain phase changes, we may discard large amounts of data and replace it with large numbers of newly +// allocated objects. This "spike" looks more like a phase change. We were in steady state at M bytes/sec +// allocation rate and now we're in a "reinitialization phase" that looks like N bytes/sec. We need the "spike" +// accommodation to give us enough runway to recalibrate our "average allocation rate". +// +// 2. The typical workload changes. "Suddenly", our typical workload of N TPS increases to N+delta TPS. This means +// our average allocation rate needs to be adjusted. Once again, we need the "spike" accomodation to give us +// enough runway to recalibrate our "average allocation rate". +// +// 3. Though there is an "average" allocation rate, a given workload's demand for allocation may be very bursty. We +// allocate a bunch of LABs during the 5 ms that follow completion of a GC, then we perform no more allocations for +// the next 150 ms. It seems we want the "spike" to represent the maximum divergence from average within the +// period of time between consecutive evaluation of the should_start_gc() service. Here's the thinking: +// +// a) Between now and the next time I ask whether should_start_gc(), we might experience a spike representing +// the anticipated burst of allocations. If that would put us over budget, then we should start GC immediately. +// b) Between now and the anticipated depletion of allocation pool, there may be two or more bursts of allocations. +// If there are more than one of these bursts, we can "approximate" that these will be separated by spans of +// time with very little or no allocations so the "average" allocation rate should be a suitable approximation +// of how this will behave. +// +// For cases 1 and 2, we need to "quickly" recalibrate the average allocation rate whenever we detect a change +// in operation mode. We want some way to decide that the average rate has changed, while keeping average +// allocation rate computation independent. bool ShenandoahAdaptiveHeuristics::should_start_gc() { - size_t max_capacity = _space_info->max_capacity(); size_t capacity = _space_info->soft_max_capacity(); - size_t available = _space_info->available(); + size_t available = _space_info->soft_available(); size_t allocated = _space_info->bytes_allocated_since_gc_start(); - // Make sure the code below treats available without the soft tail. - size_t soft_tail = max_capacity - capacity; - available = (available > soft_tail) ? (available - soft_tail) : 0; + log_debug(gc)("should_start_gc? available: " SIZE_FORMAT ", soft_max_capacity: " SIZE_FORMAT + ", allocated: " SIZE_FORMAT, available, capacity, allocated); // Track allocation rate even if we decide to start a cycle for other reasons. double rate = _allocation_rate.sample(allocated); _last_trigger = OTHER; - size_t min_threshold = capacity / 100 * ShenandoahMinFreeThreshold; + size_t min_threshold = min_free_threshold(); if (available < min_threshold) { - log_info(gc)("Trigger: Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)", - byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), + log_trigger("Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)", + byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold)); return true; } + // Check if we need to learn a bit about the application const size_t max_learn = ShenandoahLearningSteps; if (_gc_times_learned < max_learn) { size_t init_threshold = capacity / 100 * ShenandoahInitFreeThreshold; if (available < init_threshold) { - log_info(gc)("Trigger: Learning " SIZE_FORMAT " of " SIZE_FORMAT ". Free (" SIZE_FORMAT "%s) is below initial threshold (" SIZE_FORMAT "%s)", + log_trigger("Learning " SIZE_FORMAT " of " SIZE_FORMAT ". Free (" SIZE_FORMAT "%s) is below initial threshold (" SIZE_FORMAT "%s)", _gc_times_learned + 1, max_learn, - byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), + byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), byte_size_in_proper_unit(init_threshold), proper_unit_for_byte_size(init_threshold)); return true; } } - // Check if allocation headroom is still okay. This also factors in: - // 1. Some space to absorb allocation spikes + // 1. Some space to absorb allocation spikes (ShenandoahAllocSpikeFactor) // 2. Accumulated penalties from Degenerated and Full GC size_t allocation_headroom = available; @@ -240,28 +275,30 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() { allocation_headroom -= MIN2(allocation_headroom, spike_headroom); allocation_headroom -= MIN2(allocation_headroom, penalties); - double avg_cycle_time = _gc_time_history->davg() + (_margin_of_error_sd * _gc_time_history->dsd()); + double avg_cycle_time = _gc_cycle_time_history->davg() + (_margin_of_error_sd * _gc_cycle_time_history->dsd()); double avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd); + + log_debug(gc)("average GC time: %.2f ms, allocation rate: %.0f %s/s", + avg_cycle_time * 1000, byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate)); if (avg_cycle_time * avg_alloc_rate > allocation_headroom) { - log_info(gc)("Trigger: Average GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (margin of error = %.2f)", + log_trigger("Average GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s)" + " to deplete free headroom (" SIZE_FORMAT "%s) (margin of error = %.2f)", avg_cycle_time * 1000, byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate), byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom), _margin_of_error_sd); - log_info(gc, ergo)("Free headroom: " SIZE_FORMAT "%s (free) - " SIZE_FORMAT "%s (spike) - " SIZE_FORMAT "%s (penalties) = " SIZE_FORMAT "%s", byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), byte_size_in_proper_unit(spike_headroom), proper_unit_for_byte_size(spike_headroom), byte_size_in_proper_unit(penalties), proper_unit_for_byte_size(penalties), byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom)); - _last_trigger = RATE; return true; } bool is_spiking = _allocation_rate.is_spiking(rate, _spike_threshold_sd); if (is_spiking && avg_cycle_time > allocation_headroom / rate) { - log_info(gc)("Trigger: Average GC time (%.2f ms) is above the time for instantaneous allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (spike threshold = %.2f)", + log_trigger("Average GC time (%.2f ms) is above the time for instantaneous allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (spike threshold = %.2f)", avg_cycle_time * 1000, byte_size_in_proper_unit(rate), proper_unit_for_byte_size(rate), byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom), @@ -299,6 +336,13 @@ void ShenandoahAdaptiveHeuristics::adjust_spike_threshold(double amount) { log_debug(gc, ergo)("Spike threshold now: %.2f", _spike_threshold_sd); } +size_t ShenandoahAdaptiveHeuristics::min_free_threshold() { + // Note that soft_max_capacity() / 100 * min_free_threshold is smaller than max_capacity() / 100 * min_free_threshold. + // We want to behave conservatively here, so use max_capacity(). By returning a larger value, we cause the GC to + // trigger when the remaining amount of free shrinks below the larger threshold. + return _space_info->max_capacity() / 100 * ShenandoahMinFreeThreshold; +} + ShenandoahAllocationRate::ShenandoahAllocationRate() : _last_sample_time(os::elapsedTime()), _last_sample_value(0), diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index be86b7297b0..5ee10c6bebf 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,9 +26,10 @@ #ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP #define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP +#include "memory/allocation.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" +#include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "utilities/numberSeq.hpp" class ShenandoahAllocationRate : public CHeapObj { @@ -39,7 +41,6 @@ class ShenandoahAllocationRate : public CHeapObj { double upper_bound(double sds) const; bool is_spiking(double rate, double threshold) const; - private: double instantaneous_rate(double time, size_t allocated) const; @@ -138,6 +139,12 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics { // establishes what is 'normal' for the application and is used as a // source of feedback to adjust trigger parameters. TruncatedSeq _available; + + // A conservative minimum threshold of free space that we'll try to maintain when possible. + // For example, we might trigger a concurrent gc if we are likely to drop below + // this threshold, or we might consider this when dynamically resizing generations + // in the generational case. Controlled by global flag ShenandoahMinFreeThreshold. + size_t min_free_threshold(); }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp index fa6b3e67fee..cdae9bb1285 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp @@ -44,7 +44,7 @@ void ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(Shena RegionData* data, size_t size, size_t free) { for (size_t idx = 0; idx < size; idx++) { - ShenandoahHeapRegion* r = data[idx]._region; + ShenandoahHeapRegion* r = data[idx].get_region(); if (r->garbage() > 0) { cset->add_region(r); } @@ -52,7 +52,7 @@ void ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(Shena } bool ShenandoahAggressiveHeuristics::should_start_gc() { - log_info(gc)("Trigger: Start next cycle immediately"); + log_trigger("Start next cycle immediately"); return true; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp index c8e882a0f64..2c7594e10dc 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp @@ -58,17 +58,17 @@ bool ShenandoahCompactHeuristics::should_start_gc() { size_t min_threshold = capacity / 100 * ShenandoahMinFreeThreshold; if (available < min_threshold) { - log_info(gc)("Trigger: Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)", - byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), - byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold)); + log_trigger("Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)", + byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), + byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold)); return true; } size_t bytes_allocated = _space_info->bytes_allocated_since_gc_start(); if (bytes_allocated > threshold_bytes_allocated) { - log_info(gc)("Trigger: Allocated since last cycle (" SIZE_FORMAT "%s) is larger than allocation threshold (" SIZE_FORMAT "%s)", - byte_size_in_proper_unit(bytes_allocated), proper_unit_for_byte_size(bytes_allocated), - byte_size_in_proper_unit(threshold_bytes_allocated), proper_unit_for_byte_size(threshold_bytes_allocated)); + log_trigger("Allocated since last cycle (" SIZE_FORMAT "%s) is larger than allocation threshold (" SIZE_FORMAT "%s)", + byte_size_in_proper_unit(bytes_allocated), proper_unit_for_byte_size(bytes_allocated), + byte_size_in_proper_unit(threshold_bytes_allocated), proper_unit_for_byte_size(threshold_bytes_allocated)); return true; } @@ -89,7 +89,7 @@ void ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(Shenando size_t live_cset = 0; for (size_t idx = 0; idx < size; idx++) { - ShenandoahHeapRegion* r = data[idx]._region; + ShenandoahHeapRegion* r = data[idx].get_region(); size_t new_cset = live_cset + r->get_live_data_bytes(); if (new_cset < max_cset && r->garbage() > threshold) { live_cset = new_cset; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp new file mode 100644 index 00000000000..5b6d82d97a4 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -0,0 +1,290 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" +#include "gc/shenandoah/shenandoahCollectionSet.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahEvacInfo.hpp" +#include "gc/shenandoah/shenandoahTrace.hpp" + +#include "logging/log.hpp" + +ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation) + : ShenandoahAdaptiveHeuristics(generation), _generation(generation) { +} + +void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { + assert(collection_set->is_empty(), "Must be empty"); + + auto heap = ShenandoahGenerationalHeap::heap(); + size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + + + // Check all pinned regions have updated status before choosing the collection set. + heap->assert_pinned_region_status(); + + // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away. + + size_t num_regions = heap->num_regions(); + + RegionData* candidates = _region_data; + + size_t cand_idx = 0; + size_t preselected_candidates = 0; + + size_t total_garbage = 0; + + size_t immediate_garbage = 0; + size_t immediate_regions = 0; + + size_t free = 0; + size_t free_regions = 0; + + const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); + + // This counts number of humongous regions that we intend to promote in this cycle. + size_t humongous_regions_promoted = 0; + // This counts number of regular regions that will be promoted in place. + size_t regular_regions_promoted_in_place = 0; + // This counts bytes of memory used by regular regions to be promoted in place. + size_t regular_regions_promoted_usage = 0; + // This counts bytes of memory free in regular regions to be promoted in place. + size_t regular_regions_promoted_free = 0; + // This counts bytes of garbage memory in regular regions to be promoted in place. + size_t regular_regions_promoted_garbage = 0; + + for (size_t i = 0; i < num_regions; i++) { + ShenandoahHeapRegion* region = heap->get_region(i); + if (!_generation->contains(region)) { + continue; + } + size_t garbage = region->garbage(); + total_garbage += garbage; + if (region->is_empty()) { + free_regions++; + free += region_size_bytes; + } else if (region->is_regular()) { + if (!region->has_live()) { + // We can recycle it right away and put it in the free set. + immediate_regions++; + immediate_garbage += garbage; + region->make_trash_immediate(); + } else { + bool is_candidate; + // This is our candidate for later consideration. + if (collection_set->is_preselected(i)) { + assert(region->age() >= tenuring_threshold, "Preselection filter"); + is_candidate = true; + preselected_candidates++; + // Set garbage value to maximum value to force this into the sorted collection set. + garbage = region_size_bytes; + } else if (region->is_young() && (region->age() >= tenuring_threshold)) { + // Note that for GLOBAL GC, region may be OLD, and OLD regions do not qualify for pre-selection + + // This region is old enough to be promoted but it was not preselected, either because its garbage is below + // ShenandoahOldGarbageThreshold so it will be promoted in place, or because there is not sufficient room + // in old gen to hold the evacuated copies of this region's live data. In both cases, we choose not to + // place this region into the collection set. + if (region->get_top_before_promote() != nullptr) { + // Region was included for promotion-in-place + regular_regions_promoted_in_place++; + regular_regions_promoted_usage += region->used_before_promote(); + regular_regions_promoted_free += region->free(); + regular_regions_promoted_garbage += region->garbage(); + } + is_candidate = false; + } else { + is_candidate = true; + } + if (is_candidate) { + candidates[cand_idx].set_region_and_garbage(region, garbage); + cand_idx++; + } + } + } else if (region->is_humongous_start()) { + // Reclaim humongous regions here, and count them as the immediate garbage +#ifdef ASSERT + bool reg_live = region->has_live(); + bool bm_live = heap->complete_marking_context()->is_marked(cast_to_oop(region->bottom())); + assert(reg_live == bm_live, + "Humongous liveness and marks should agree. Region live: %s; Bitmap live: %s; Region Live Words: " SIZE_FORMAT, + BOOL_TO_STR(reg_live), BOOL_TO_STR(bm_live), region->get_live_data_words()); +#endif + if (!region->has_live()) { + heap->trash_humongous_region_at(region); + + // Count only the start. Continuations would be counted on "trash" path + immediate_regions++; + immediate_garbage += garbage; + } else { + if (region->is_young() && region->age() >= tenuring_threshold) { + oop obj = cast_to_oop(region->bottom()); + size_t humongous_regions = ShenandoahHeapRegion::required_regions(obj->size() * HeapWordSize); + humongous_regions_promoted += humongous_regions; + } + } + } else if (region->is_trash()) { + // Count in just trashed collection set, during coalesced CM-with-UR + immediate_regions++; + immediate_garbage += garbage; + } + } + heap->old_generation()->set_expected_humongous_region_promotions(humongous_regions_promoted); + heap->old_generation()->set_expected_regular_region_promotions(regular_regions_promoted_in_place); + log_info(gc, ergo)("Planning to promote in place " SIZE_FORMAT " humongous regions and " SIZE_FORMAT + " regular regions, spanning a total of " SIZE_FORMAT " used bytes", + humongous_regions_promoted, regular_regions_promoted_in_place, + humongous_regions_promoted * ShenandoahHeapRegion::region_size_bytes() + + regular_regions_promoted_usage); + + // Step 2. Look back at garbage statistics, and decide if we want to collect anything, + // given the amount of immediately reclaimable garbage. If we do, figure out the collection set. + + assert (immediate_garbage <= total_garbage, + "Cannot have more immediate garbage than total garbage: " SIZE_FORMAT "%s vs " SIZE_FORMAT "%s", + byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage), + byte_size_in_proper_unit(total_garbage), proper_unit_for_byte_size(total_garbage)); + + size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); + + bool doing_promote_in_place = (humongous_regions_promoted + regular_regions_promoted_in_place > 0); + if (doing_promote_in_place || (preselected_candidates > 0) || (immediate_percent <= ShenandoahImmediateThreshold)) { + // Only young collections need to prime the collection set. + if (_generation->is_young()) { + heap->old_generation()->heuristics()->prime_collection_set(collection_set); + } + + // Call the subclasses to add young-gen regions into the collection set. + choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); + } + + if (collection_set->has_old_regions()) { + heap->shenandoah_policy()->record_mixed_cycle(); + } + + size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage); + size_t collectable_garbage = collection_set->garbage() + immediate_garbage; + size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage); + + log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " + "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " SIZE_FORMAT " regions, " + "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " SIZE_FORMAT " regions", + + byte_size_in_proper_unit(collectable_garbage), + proper_unit_for_byte_size(collectable_garbage), + collectable_garbage_percent, + + byte_size_in_proper_unit(immediate_garbage), + proper_unit_for_byte_size(immediate_garbage), + immediate_percent, + immediate_regions, + + byte_size_in_proper_unit(collection_set->garbage()), + proper_unit_for_byte_size(collection_set->garbage()), + cset_percent, + collection_set->count()); + + if (collection_set->garbage() > 0) { + size_t young_evac_bytes = collection_set->get_young_bytes_reserved_for_evacuation(); + size_t promote_evac_bytes = collection_set->get_young_bytes_to_be_promoted(); + size_t old_evac_bytes = collection_set->get_old_bytes_reserved_for_evacuation(); + size_t total_evac_bytes = young_evac_bytes + promote_evac_bytes + old_evac_bytes; + log_info(gc, ergo)("Evacuation Targets: YOUNG: " SIZE_FORMAT "%s, " + "PROMOTE: " SIZE_FORMAT "%s, " + "OLD: " SIZE_FORMAT "%s, " + "TOTAL: " SIZE_FORMAT "%s", + byte_size_in_proper_unit(young_evac_bytes), proper_unit_for_byte_size(young_evac_bytes), + byte_size_in_proper_unit(promote_evac_bytes), proper_unit_for_byte_size(promote_evac_bytes), + byte_size_in_proper_unit(old_evac_bytes), proper_unit_for_byte_size(old_evac_bytes), + byte_size_in_proper_unit(total_evac_bytes), proper_unit_for_byte_size(total_evac_bytes)); + + ShenandoahEvacuationInformation evacInfo; + evacInfo.set_collection_set_regions(collection_set->count()); + evacInfo.set_collection_set_used_before(collection_set->used()); + evacInfo.set_collection_set_used_after(collection_set->live()); + evacInfo.set_collected_old(old_evac_bytes); + evacInfo.set_collected_promoted(promote_evac_bytes); + evacInfo.set_collected_young(young_evac_bytes); + evacInfo.set_regions_promoted_humongous(humongous_regions_promoted); + evacInfo.set_regions_promoted_regular(regular_regions_promoted_in_place); + evacInfo.set_regular_promoted_garbage(regular_regions_promoted_garbage); + evacInfo.set_regular_promoted_free(regular_regions_promoted_free); + evacInfo.set_regions_immediate(immediate_regions); + evacInfo.set_immediate_size(immediate_garbage); + evacInfo.set_regions_freed(free_regions); + + ShenandoahTracer().report_evacuation_info(&evacInfo); + } +} + + +size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset, + const RegionData* data, + size_t size) const { +#ifdef ASSERT + const uint tenuring_threshold = ShenandoahGenerationalHeap::heap()->age_census()->tenuring_threshold(); +#endif + + // cur_young_garbage represents the amount of memory to be reclaimed from young-gen. In the case that live objects + // are known to be promoted out of young-gen, we count this as cur_young_garbage because this memory is reclaimed + // from young-gen and becomes available to serve future young-gen allocation requests. + size_t cur_young_garbage = 0; + for (size_t idx = 0; idx < size; idx++) { + ShenandoahHeapRegion* r = data[idx].get_region(); + if (cset->is_preselected(r->index())) { + assert(r->age() >= tenuring_threshold, "Preselected regions must have tenure age"); + // Entire region will be promoted, This region does not impact young-gen or old-gen evacuation reserve. + // This region has been pre-selected and its impact on promotion reserve is already accounted for. + + // r->used() is r->garbage() + r->get_live_data_bytes() + // Since all live data in this region is being evacuated from young-gen, it is as if this memory + // is garbage insofar as young-gen is concerned. Counting this as garbage reduces the need to + // reclaim highly utilized young-gen regions just for the sake of finding min_garbage to reclaim + // within young-gen memory. + + cur_young_garbage += r->garbage(); + cset->add_region(r); + } + } + return cur_young_garbage; +} + +void ShenandoahGenerationalHeuristics::log_cset_composition(ShenandoahCollectionSet* cset) const { + size_t collected_old = cset->get_old_bytes_reserved_for_evacuation(); + size_t collected_promoted = cset->get_young_bytes_to_be_promoted(); + size_t collected_young = cset->get_young_bytes_reserved_for_evacuation(); + + log_info(gc, ergo)( + "Chosen CSet evacuates young: " SIZE_FORMAT "%s (of which at least: " SIZE_FORMAT "%s are to be promoted), " + "old: " SIZE_FORMAT "%s", + byte_size_in_proper_unit(collected_young), proper_unit_for_byte_size(collected_young), + byte_size_in_proper_unit(collected_promoted), proper_unit_for_byte_size(collected_promoted), + byte_size_in_proper_unit(collected_old), proper_unit_for_byte_size(collected_old)); +} diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp new file mode 100644 index 00000000000..6708c63f042 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -0,0 +1,59 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGENERATIONALHEURISTICS_HPP +#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGENERATIONALHEURISTICS_HPP + + +#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" + +class ShenandoahGeneration; + +/* + * This class serves as the base class for heuristics used to trigger and + * choose the collection sets for young and global collections. It leans + * heavily on the existing functionality of ShenandoahAdaptiveHeuristics. + * + * It differs from the base class primarily in that choosing the collection + * set is responsible for mixed collections and in-place promotions of tenured + * regions. + */ +class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { + +public: + explicit ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation); + + void choose_collection_set(ShenandoahCollectionSet* collection_set) override; +protected: + ShenandoahGeneration* _generation; + + size_t add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset, + const RegionData* data, + size_t size) const; + + void log_cset_composition(ShenandoahCollectionSet* cset) const; +}; + + +#endif //SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGENERATIONALHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp new file mode 100644 index 00000000000..4c1e6b7bdff --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -0,0 +1,147 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahGlobalGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" + +#include "utilities/quickSort.hpp" + +ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation) + : ShenandoahGenerationalHeuristics(generation) { +} + + +void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { + // Better select garbage-first regions + QuickSort::sort(data, (int) size, compare_by_garbage); + + choose_global_collection_set(cset, data, size, actual_free, 0 /* cur_young_garbage */); + + log_cset_composition(cset); +} + + +void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollectionSet* cset, + const ShenandoahHeuristics::RegionData* data, + size_t size, size_t actual_free, + size_t cur_young_garbage) const { + auto heap = ShenandoahGenerationalHeap::heap(); + size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + size_t capacity = heap->young_generation()->max_capacity(); + size_t garbage_threshold = region_size_bytes * ShenandoahGarbageThreshold / 100; + size_t ignore_threshold = region_size_bytes * ShenandoahIgnoreGarbageThreshold / 100; + const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); + + size_t young_evac_reserve = heap->young_generation()->get_evacuation_reserve(); + size_t old_evac_reserve = heap->old_generation()->get_evacuation_reserve(); + size_t max_young_cset = (size_t) (young_evac_reserve / ShenandoahEvacWaste); + size_t young_cur_cset = 0; + size_t max_old_cset = (size_t) (old_evac_reserve / ShenandoahOldEvacWaste); + size_t old_cur_cset = 0; + + // Figure out how many unaffiliated young regions are dedicated to mutator and to evacuator. Allow the young + // collector's unaffiliated regions to be transferred to old-gen if old-gen has more easily reclaimed garbage + // than young-gen. At the end of this cycle, any excess regions remaining in old-gen will be transferred back + // to young. Do not transfer the mutator's unaffiliated regions to old-gen. Those must remain available + // to the mutator as it needs to be able to consume this memory during concurrent GC. + + size_t unaffiliated_young_regions = heap->young_generation()->free_unaffiliated_regions(); + size_t unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes; + + if (unaffiliated_young_memory > max_young_cset) { + size_t unaffiliated_mutator_memory = unaffiliated_young_memory - max_young_cset; + unaffiliated_young_memory -= unaffiliated_mutator_memory; + unaffiliated_young_regions = unaffiliated_young_memory / region_size_bytes; // round down + unaffiliated_young_memory = unaffiliated_young_regions * region_size_bytes; + } + + // We'll affiliate these unaffiliated regions with either old or young, depending on need. + max_young_cset -= unaffiliated_young_memory; + + // Keep track of how many regions we plan to transfer from young to old. + size_t regions_transferred_to_old = 0; + + size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_young_cset; + size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0; + + log_info(gc, ergo)("Adaptive CSet Selection for GLOBAL. Max Young Evacuation: " SIZE_FORMAT + "%s, Max Old Evacuation: " SIZE_FORMAT "%s, Actual Free: " SIZE_FORMAT "%s.", + byte_size_in_proper_unit(max_young_cset), proper_unit_for_byte_size(max_young_cset), + byte_size_in_proper_unit(max_old_cset), proper_unit_for_byte_size(max_old_cset), + byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + + for (size_t idx = 0; idx < size; idx++) { + ShenandoahHeapRegion* r = data[idx].get_region(); + assert(!cset->is_preselected(r->index()), "There should be no preselected regions during GLOBAL GC"); + bool add_region = false; + if (r->is_old() || (r->age() >= tenuring_threshold)) { + size_t new_cset = old_cur_cset + r->get_live_data_bytes(); + if ((r->garbage() > garbage_threshold)) { + while ((new_cset > max_old_cset) && (unaffiliated_young_regions > 0)) { + unaffiliated_young_regions--; + regions_transferred_to_old++; + max_old_cset += region_size_bytes / ShenandoahOldEvacWaste; + } + } + if ((new_cset <= max_old_cset) && (r->garbage() > garbage_threshold)) { + add_region = true; + old_cur_cset = new_cset; + } + } else { + assert(r->is_young() && (r->age() < tenuring_threshold), "DeMorgan's law (assuming r->is_affiliated)"); + size_t new_cset = young_cur_cset + r->get_live_data_bytes(); + size_t region_garbage = r->garbage(); + size_t new_garbage = cur_young_garbage + region_garbage; + bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage); + + if (add_regardless || (r->garbage() > garbage_threshold)) { + while ((new_cset > max_young_cset) && (unaffiliated_young_regions > 0)) { + unaffiliated_young_regions--; + max_young_cset += region_size_bytes / ShenandoahEvacWaste; + } + } + if ((new_cset <= max_young_cset) && (add_regardless || (region_garbage > garbage_threshold))) { + add_region = true; + young_cur_cset = new_cset; + cur_young_garbage = new_garbage; + } + } + if (add_region) { + cset->add_region(r); + } + } + + if (regions_transferred_to_old > 0) { + heap->generation_sizer()->force_transfer_to_old(regions_transferred_to_old); + heap->young_generation()->set_evacuation_reserve(young_evac_reserve - regions_transferred_to_old * region_size_bytes); + heap->old_generation()->set_evacuation_reserve(old_evac_reserve + regions_transferred_to_old * region_size_bytes); + } +} diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp new file mode 100644 index 00000000000..1f95f75c521 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP +#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP + + +#include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" + +class ShenandoahGlobalGeneration; + +/* + * This is a specialization of the generational heuristics which is aware + * of old and young regions and respects the configured evacuation parameters + * for such regions during a global collection of a generational heap. + */ +class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { +public: + ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); + + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; + +private: + void choose_global_collection_set(ShenandoahCollectionSet* cset, + const ShenandoahHeuristics::RegionData* data, + size_t size, size_t actual_free, + size_t cur_young_garbage) const; +}; + + +#endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 2d5af892c80..c2ad809f43a 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -24,38 +25,44 @@ #include "precompiled.hpp" #include "gc/shared/gcCause.hpp" -#include "gc/shenandoah/shenandoahCollectionSet.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" -#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/quickSort.hpp" +// sort by decreasing garbage (so most garbage comes first) int ShenandoahHeuristics::compare_by_garbage(RegionData a, RegionData b) { - if (a._garbage > b._garbage) + if (a.get_garbage() > b.get_garbage()) { return -1; - else if (a._garbage < b._garbage) + } else if (a.get_garbage() < b.get_garbage()) { return 1; - else return 0; + } else { + return 0; + } } ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) : _space_info(space_info), _region_data(nullptr), + _guaranteed_gc_interval(0), _cycle_start(os::elapsedTime()), _last_cycle_end(0), _gc_times_learned(0), _gc_time_penalties(0), - _gc_time_history(new TruncatedSeq(10, ShenandoahAdaptiveDecayFactor)), + _gc_cycle_time_history(new TruncatedSeq(Moving_Average_Samples, ShenandoahAdaptiveDecayFactor)), _metaspace_oom() { size_t num_regions = ShenandoahHeap::heap()->num_regions(); assert(num_regions > 0, "Sanity"); _region_data = NEW_C_HEAP_ARRAY(RegionData, num_regions, mtGC); + for (size_t i = 0; i < num_regions; i++) { + _region_data[i].clear(); + } } ShenandoahHeuristics::~ShenandoahHeuristics() { @@ -63,7 +70,7 @@ ShenandoahHeuristics::~ShenandoahHeuristics() { } void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { - assert(collection_set->count() == 0, "Must be empty"); + assert(collection_set->is_empty(), "Must be empty"); ShenandoahHeap* heap = ShenandoahHeap::heap(); @@ -105,8 +112,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec region->make_trash_immediate(); } else { // This is our candidate for later consideration. - candidates[cand_idx]._region = region; - candidates[cand_idx]._garbage = garbage; + candidates[cand_idx].set_region_and_garbage(region, garbage); cand_idx++; } } else if (region->is_humongous_start()) { @@ -147,13 +153,12 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec } size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage); - size_t collectable_garbage = collection_set->garbage() + immediate_garbage; size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage); log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " - "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " - "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%)", + "Immediate: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " SIZE_FORMAT " regions, " + "CSet: " SIZE_FORMAT "%s (" SIZE_FORMAT "%%), " SIZE_FORMAT " regions", byte_size_in_proper_unit(collectable_garbage), proper_unit_for_byte_size(collectable_garbage), @@ -162,10 +167,12 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec byte_size_in_proper_unit(immediate_garbage), proper_unit_for_byte_size(immediate_garbage), immediate_percent, + immediate_regions, byte_size_in_proper_unit(collection_set->garbage()), proper_unit_for_byte_size(collection_set->garbage()), - cset_percent); + cset_percent, + collection_set->count()); } void ShenandoahHeuristics::record_cycle_start() { @@ -180,15 +187,15 @@ bool ShenandoahHeuristics::should_start_gc() { // Perform GC to cleanup metaspace if (has_metaspace_oom()) { // Some of vmTestbase/metaspace tests depend on following line to count GC cycles - log_info(gc)("Trigger: %s", GCCause::to_string(GCCause::_metadata_GC_threshold)); + log_trigger("%s", GCCause::to_string(GCCause::_metadata_GC_threshold)); return true; } - if (ShenandoahGuaranteedGCInterval > 0) { + if (_guaranteed_gc_interval > 0) { double last_time_ms = (os::elapsedTime() - _last_cycle_end) * 1000; - if (last_time_ms > ShenandoahGuaranteedGCInterval) { - log_info(gc)("Trigger: Time since last GC (%.0f ms) is larger than guaranteed interval (" UINTX_FORMAT " ms)", - last_time_ms, ShenandoahGuaranteedGCInterval); + if (last_time_ms > _guaranteed_gc_interval) { + log_trigger("Time since last GC (%.0f ms) is larger than guaranteed interval (" UINTX_FORMAT " ms)", + last_time_ms, _guaranteed_gc_interval); return true; } } @@ -202,7 +209,7 @@ bool ShenandoahHeuristics::should_degenerate_cycle() { void ShenandoahHeuristics::adjust_penalty(intx step) { assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100, - "In range before adjustment: " INTX_FORMAT, _gc_time_penalties); + "In range before adjustment: " INTX_FORMAT, _gc_time_penalties); intx new_val = _gc_time_penalties + step; if (new_val < 0) { @@ -214,11 +221,29 @@ void ShenandoahHeuristics::adjust_penalty(intx step) { _gc_time_penalties = new_val; assert(0 <= _gc_time_penalties && _gc_time_penalties <= 100, - "In range after adjustment: " INTX_FORMAT, _gc_time_penalties); + "In range after adjustment: " INTX_FORMAT, _gc_time_penalties); +} + +void ShenandoahHeuristics::log_trigger(const char* fmt, ...) { + LogTarget(Info, gc) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + ls.print_raw("Trigger", 7); + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ls.print(" (%s)", _space_info->name()); + } + ls.print_raw(": ", 2); + va_list va; + va_start(va, fmt); + ls.vprint(fmt, va); + va_end(va); + ls.cr(); + } } void ShenandoahHeuristics::record_success_concurrent() { - _gc_time_history->add(time_since_last_gc()); + _gc_cycle_time_history->add(elapsed_cycle_time()); _gc_times_learned++; adjust_penalty(Concurrent_Adjust); @@ -256,6 +281,6 @@ void ShenandoahHeuristics::initialize() { // Nothing to do by default. } -double ShenandoahHeuristics::time_since_last_gc() const { +double ShenandoahHeuristics::elapsed_cycle_time() const { return os::elapsedTime() - _cycle_start; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index c4bfaed400d..8bd5c7775c4 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,11 +27,10 @@ #define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHHEURISTICS_HPP #include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" -#include "gc/shenandoah/shenandoahHeap.hpp" -#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "memory/allocation.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/numberSeq.hpp" #define SHENANDOAH_ERGO_DISABLE_FLAG(name) \ do { \ @@ -69,23 +69,92 @@ class ShenandoahHeuristics : public CHeapObj { static const intx Degenerated_Penalty = 10; // how much to penalize average GC duration history on Degenerated GC static const intx Full_Penalty = 20; // how much to penalize average GC duration history on Full GC +#ifdef ASSERT + enum UnionTag { + is_uninitialized, is_garbage, is_live_data + }; +#endif + protected: - typedef struct { + static const uint Moving_Average_Samples = 10; // Number of samples to store in moving averages + + class RegionData { + private: ShenandoahHeapRegion* _region; - size_t _garbage; - } RegionData; + union { + size_t _garbage; // Not used by old-gen heuristics. + size_t _live_data; // Only used for old-gen heuristics, which prioritizes retention of _live_data over garbage reclaim + } _region_union; +#ifdef ASSERT + UnionTag _union_tag; +#endif + public: + + inline void clear() { + _region = nullptr; + _region_union._garbage = 0; +#ifdef ASSERT + _union_tag = is_uninitialized; +#endif + } + + inline void set_region_and_garbage(ShenandoahHeapRegion* region, size_t garbage) { + _region = region; + _region_union._garbage = garbage; +#ifdef ASSERT + _union_tag = is_garbage; +#endif + } + + inline void set_region_and_livedata(ShenandoahHeapRegion* region, size_t live) { + _region = region; + _region_union._live_data = live; +#ifdef ASSERT + _union_tag = is_live_data; +#endif + } + + inline ShenandoahHeapRegion* get_region() const { + assert(_union_tag != is_uninitialized, "Cannot fetch region from uninitialized RegionData"); + return _region; + } + + inline size_t get_garbage() const { + assert(_union_tag == is_garbage, "Invalid union fetch"); + return _region_union._garbage; + } + + inline size_t get_livedata() const { + assert(_union_tag == is_live_data, "Invalid union fetch"); + return _region_union._live_data; + } + }; // Source of information about the memory space managed by this heuristic ShenandoahSpaceInfo* _space_info; + // Depending on generation mode, region data represents the results of the relevant + // most recently completed marking pass: + // - in GLOBAL mode, global marking pass + // - in OLD mode, old-gen marking pass + // - in YOUNG mode, young-gen marking pass + // + // Note that there is some redundancy represented in region data because + // each instance is an array large enough to hold all regions. However, + // any region in young-gen is not in old-gen. And any time we are + // making use of the GLOBAL data, there is no need to maintain the + // YOUNG or OLD data. Consider this redundancy of data structure to + // have negligible cost unless proven otherwise. RegionData* _region_data; + size_t _guaranteed_gc_interval; + double _cycle_start; double _last_cycle_end; size_t _gc_times_learned; intx _gc_time_penalties; - TruncatedSeq* _gc_time_history; + TruncatedSeq* _gc_cycle_time_history; // There may be many threads that contend to set this flag ShenandoahSharedFlag _metaspace_oom; @@ -106,6 +175,10 @@ class ShenandoahHeuristics : public CHeapObj { void clear_metaspace_oom() { _metaspace_oom.unset(); } bool has_metaspace_oom() const { return _metaspace_oom.is_set(); } + void set_guaranteed_gc_interval(size_t guaranteed_gc_interval) { + _guaranteed_gc_interval = guaranteed_gc_interval; + } + virtual void record_cycle_start(); virtual void record_cycle_end(); @@ -127,6 +200,9 @@ class ShenandoahHeuristics : public CHeapObj { virtual void choose_collection_set(ShenandoahCollectionSet* collection_set); virtual bool can_unload_classes(); + + // This indicates whether or not the current cycle should unload classes. + // It does NOT indicate that a cycle should be started. virtual bool should_unload_classes(); virtual const char* name() = 0; @@ -134,7 +210,10 @@ class ShenandoahHeuristics : public CHeapObj { virtual bool is_experimental() = 0; virtual void initialize(); - double time_since_last_gc() const; + double elapsed_cycle_time() const; + + // Format prefix and emit log message indicating a GC cycle hs been triggered + void log_trigger(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3); }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp new file mode 100644 index 00000000000..abb2b7b266a --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -0,0 +1,734 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" +#include "gc/shenandoah/shenandoahCollectionSet.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "logging/log.hpp" +#include "utilities/quickSort.hpp" + +uint ShenandoahOldHeuristics::NOT_FOUND = -1U; + +// sort by increasing live (so least live comes first) +int ShenandoahOldHeuristics::compare_by_live(RegionData a, RegionData b) { + if (a.get_livedata() < b.get_livedata()) { + return -1; + } else if (a.get_livedata() > b.get_livedata()) { + return 1; + } else { + return 0; + } +} + +// sort by increasing index +int ShenandoahOldHeuristics::compare_by_index(RegionData a, RegionData b) { + if (a.get_region()->index() < b.get_region()->index()) { + return -1; + } else if (a.get_region()->index() > b.get_region()->index()) { + return 1; + } else { + // quicksort may compare to self during search for pivot + return 0; + } +} + +ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap) : + ShenandoahHeuristics(generation), + _heap(gen_heap), + _old_gen(generation), + _first_pinned_candidate(NOT_FOUND), + _last_old_collection_candidate(0), + _next_old_collection_candidate(0), + _last_old_region(0), + _live_bytes_in_unprocessed_candidates(0), + _old_generation(generation), + _cannot_expand_trigger(false), + _fragmentation_trigger(false), + _growth_trigger(false), + _fragmentation_density(0.0), + _fragmentation_first_old_region(0), + _fragmentation_last_old_region(0) +{ +} + +bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* collection_set) { + if (unprocessed_old_collection_candidates() == 0) { + return false; + } + + if (_old_generation->is_preparing_for_mark()) { + // We have unprocessed old collection candidates, but the heuristic has given up on evacuating them. + // This is most likely because they were _all_ pinned at the time of the last mixed evacuation (and + // this in turn is most likely because there are just one or two candidate regions remaining). + log_info(gc, ergo)("Remaining " UINT32_FORMAT " old regions are being coalesced and filled", unprocessed_old_collection_candidates()); + return false; + } + + _first_pinned_candidate = NOT_FOUND; + + uint included_old_regions = 0; + size_t evacuated_old_bytes = 0; + size_t collected_old_bytes = 0; + + // If a region is put into the collection set, then this region's free (not yet used) bytes are no longer + // "available" to hold the results of other evacuations. This may cause a decrease in the remaining amount + // of memory that can still be evacuated. We address this by reducing the evacuation budget by the amount + // of live memory in that region and by the amount of unallocated memory in that region if the evacuation + // budget is constrained by availability of free memory. + const size_t old_evacuation_reserve = _old_generation->get_evacuation_reserve(); + const size_t old_evacuation_budget = (size_t) ((double) old_evacuation_reserve / ShenandoahOldEvacWaste); + size_t unfragmented_available = _old_generation->free_unaffiliated_regions() * ShenandoahHeapRegion::region_size_bytes(); + size_t fragmented_available; + size_t excess_fragmented_available; + + if (unfragmented_available > old_evacuation_budget) { + unfragmented_available = old_evacuation_budget; + fragmented_available = 0; + excess_fragmented_available = 0; + } else { + assert(_old_generation->available() >= old_evacuation_budget, "Cannot budget more than is available"); + fragmented_available = _old_generation->available() - unfragmented_available; + assert(fragmented_available + unfragmented_available >= old_evacuation_budget, "Budgets do not add up"); + if (fragmented_available + unfragmented_available > old_evacuation_budget) { + excess_fragmented_available = (fragmented_available + unfragmented_available) - old_evacuation_budget; + fragmented_available -= excess_fragmented_available; + } + } + + size_t remaining_old_evacuation_budget = old_evacuation_budget; + log_debug(gc)("Choose old regions for mixed collection: old evacuation budget: " SIZE_FORMAT "%s, candidates: %u", + byte_size_in_proper_unit(old_evacuation_budget), proper_unit_for_byte_size(old_evacuation_budget), + unprocessed_old_collection_candidates()); + + size_t lost_evacuation_capacity = 0; + + // The number of old-gen regions that were selected as candidates for collection at the end of the most recent old-gen + // concurrent marking phase and have not yet been collected is represented by unprocessed_old_collection_candidates(). + // Candidate regions are ordered according to increasing amount of live data. If there is not sufficient room to + // evacuate region N, then there is no need to even consider evacuating region N+1. + while (unprocessed_old_collection_candidates() > 0) { + // Old collection candidates are sorted in order of decreasing garbage contained therein. + ShenandoahHeapRegion* r = next_old_collection_candidate(); + if (r == nullptr) { + break; + } + assert(r->is_regular(), "There should be no humongous regions in the set of mixed-evac candidates"); + + // If region r is evacuated to fragmented memory (to free memory within a partially used region), then we need + // to decrease the capacity of the fragmented memory by the scaled loss. + + size_t live_data_for_evacuation = r->get_live_data_bytes(); + size_t lost_available = r->free(); + + if ((lost_available > 0) && (excess_fragmented_available > 0)) { + if (lost_available < excess_fragmented_available) { + excess_fragmented_available -= lost_available; + lost_evacuation_capacity -= lost_available; + lost_available = 0; + } else { + lost_available -= excess_fragmented_available; + lost_evacuation_capacity -= excess_fragmented_available; + excess_fragmented_available = 0; + } + } + size_t scaled_loss = (size_t) ((double) lost_available / ShenandoahOldEvacWaste); + if ((lost_available > 0) && (fragmented_available > 0)) { + if (scaled_loss + live_data_for_evacuation < fragmented_available) { + fragmented_available -= scaled_loss; + scaled_loss = 0; + } else { + // We will have to allocate this region's evacuation memory from unfragmented memory, so don't bother + // to decrement scaled_loss + } + } + if (scaled_loss > 0) { + // We were not able to account for the lost free memory within fragmented memory, so we need to take this + // allocation out of unfragmented memory. Unfragmented memory does not need to account for loss of free. + if (live_data_for_evacuation > unfragmented_available) { + // There is not room to evacuate this region or any that come after it in within the candidates array. + break; + } else { + unfragmented_available -= live_data_for_evacuation; + } + } else { + // Since scaled_loss == 0, we have accounted for the loss of free memory, so we can allocate from either + // fragmented or unfragmented available memory. Use up the fragmented memory budget first. + size_t evacuation_need = live_data_for_evacuation; + + if (evacuation_need > fragmented_available) { + evacuation_need -= fragmented_available; + fragmented_available = 0; + } else { + fragmented_available -= evacuation_need; + evacuation_need = 0; + } + if (evacuation_need > unfragmented_available) { + // There is not room to evacuate this region or any that come after it in within the candidates array. + break; + } else { + unfragmented_available -= evacuation_need; + // dead code: evacuation_need == 0; + } + } + collection_set->add_region(r); + included_old_regions++; + evacuated_old_bytes += live_data_for_evacuation; + collected_old_bytes += r->garbage(); + consume_old_collection_candidate(); + } + + if (_first_pinned_candidate != NOT_FOUND) { + // Need to deal with pinned regions + slide_pinned_regions_to_front(); + } + decrease_unprocessed_old_collection_candidates_live_memory(evacuated_old_bytes); + if (included_old_regions > 0) { + log_info(gc, ergo)("Old-gen piggyback evac (" UINT32_FORMAT " regions, evacuating " PROPERFMT ", reclaiming: " PROPERFMT ")", + included_old_regions, PROPERFMTARGS(evacuated_old_bytes), PROPERFMTARGS(collected_old_bytes)); + } + + if (unprocessed_old_collection_candidates() == 0) { + // We have added the last of our collection candidates to a mixed collection. + // Any triggers that occurred during mixed evacuations may no longer be valid. They can retrigger if appropriate. + clear_triggers(); + + _old_generation->complete_mixed_evacuations(); + } else if (included_old_regions == 0) { + // We have candidates, but none were included for evacuation - are they all pinned? + // or did we just not have enough room for any of them in this collection set? + // We don't want a region with a stuck pin to prevent subsequent old collections, so + // if they are all pinned we transition to a state that will allow us to make these uncollected + // (pinned) regions parsable. + if (all_candidates_are_pinned()) { + log_info(gc, ergo)("All candidate regions " UINT32_FORMAT " are pinned", unprocessed_old_collection_candidates()); + _old_generation->abandon_mixed_evacuations(); + } else { + log_info(gc, ergo)("No regions selected for mixed collection. " + "Old evacuation budget: " PROPERFMT ", Remaining evacuation budget: " PROPERFMT + ", Lost capacity: " PROPERFMT + ", Next candidate: " UINT32_FORMAT ", Last candidate: " UINT32_FORMAT, + PROPERFMTARGS(old_evacuation_reserve), + PROPERFMTARGS(remaining_old_evacuation_budget), + PROPERFMTARGS(lost_evacuation_capacity), + _next_old_collection_candidate, _last_old_collection_candidate); + } + } + + return (included_old_regions > 0); +} + +bool ShenandoahOldHeuristics::all_candidates_are_pinned() { +#ifdef ASSERT + if (uint(os::random()) % 100 < ShenandoahCoalesceChance) { + return true; + } +#endif + + for (uint i = _next_old_collection_candidate; i < _last_old_collection_candidate; ++i) { + ShenandoahHeapRegion* region = _region_data[i].get_region(); + if (!region->is_pinned()) { + return false; + } + } + return true; +} + +void ShenandoahOldHeuristics::slide_pinned_regions_to_front() { + // Find the first unpinned region to the left of the next region that + // will be added to the collection set. These regions will have been + // added to the cset, so we can use them to hold pointers to regions + // that were pinned when the cset was chosen. + // [ r p r p p p r r ] + // ^ ^ ^ + // | | | pointer to next region to add to a mixed collection is here. + // | | first r to the left should be in the collection set now. + // | first pinned region, we don't need to look past this + uint write_index = NOT_FOUND; + for (uint search = _next_old_collection_candidate - 1; search > _first_pinned_candidate; --search) { + ShenandoahHeapRegion* region = _region_data[search].get_region(); + if (!region->is_pinned()) { + write_index = search; + assert(region->is_cset(), "Expected unpinned region to be added to the collection set."); + break; + } + } + + // If we could not find an unpinned region, it means there are no slots available + // to move up the pinned regions. In this case, we just reset our next index in the + // hopes that some of these regions will become unpinned before the next mixed + // collection. We may want to bailout of here instead, as it should be quite + // rare to have so many pinned regions and may indicate something is wrong. + if (write_index == NOT_FOUND) { + assert(_first_pinned_candidate != NOT_FOUND, "Should only be here if there are pinned regions."); + _next_old_collection_candidate = _first_pinned_candidate; + return; + } + + // Find pinned regions to the left and move their pointer into a slot + // that was pointing at a region that has been added to the cset (or was pointing + // to a pinned region that we've already moved up). We are done when the leftmost + // pinned region has been slid up. + // [ r p r x p p p r ] + // ^ ^ + // | | next region for mixed collections + // | Write pointer is here. We know this region is already in the cset + // | so we can clobber it with the next pinned region we find. + for (int32_t search = (int32_t)write_index - 1; search >= (int32_t)_first_pinned_candidate; --search) { + RegionData& skipped = _region_data[search]; + if (skipped.get_region()->is_pinned()) { + RegionData& available_slot = _region_data[write_index]; + available_slot.set_region_and_livedata(skipped.get_region(), skipped.get_livedata()); + --write_index; + } + } + + // Update to read from the leftmost pinned region. Plus one here because we decremented + // the write index to hold the next found pinned region. We are just moving it back now + // to point to the first pinned region. + _next_old_collection_candidate = write_index + 1; +} + +void ShenandoahOldHeuristics::prepare_for_old_collections() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + const size_t num_regions = heap->num_regions(); + size_t cand_idx = 0; + size_t immediate_garbage = 0; + size_t immediate_regions = 0; + size_t live_data = 0; + + RegionData* candidates = _region_data; + for (size_t i = 0; i < num_regions; i++) { + ShenandoahHeapRegion* region = heap->get_region(i); + if (!region->is_old()) { + continue; + } + + size_t garbage = region->garbage(); + size_t live_bytes = region->get_live_data_bytes(); + live_data += live_bytes; + + if (region->is_regular() || region->is_regular_pinned()) { + // Only place regular or pinned regions with live data into the candidate set. + // Pinned regions cannot be evacuated, but we are not actually choosing candidates + // for the collection set here. That happens later during the next young GC cycle, + // by which time, the pinned region may no longer be pinned. + if (!region->has_live()) { + assert(!region->is_pinned(), "Pinned region should have live (pinned) objects."); + region->make_trash_immediate(); + immediate_regions++; + immediate_garbage += garbage; + } else { + region->begin_preemptible_coalesce_and_fill(); + candidates[cand_idx].set_region_and_livedata(region, live_bytes); + cand_idx++; + } + } else if (region->is_humongous_start()) { + // This will handle humongous start regions whether they are also pinned, or not. + // If they are pinned, we expect them to hold live data, so they will not be + // turned into immediate garbage. + if (!region->has_live()) { + assert(!region->is_pinned(), "Pinned region should have live (pinned) objects."); + // The humongous object is dead, we can just return this region and the continuations + // immediately to the freeset - no evacuations are necessary here. The continuations + // will be made into trash by this method, so they'll be skipped by the 'is_regular' + // check above, but we still need to count the start region. + immediate_regions++; + immediate_garbage += garbage; + size_t region_count = heap->trash_humongous_region_at(region); + log_debug(gc)("Trashed " SIZE_FORMAT " regions for humongous object.", region_count); + } + } else if (region->is_trash()) { + // Count humongous objects made into trash here. + immediate_regions++; + immediate_garbage += garbage; + } + } + + _old_generation->set_live_bytes_after_last_mark(live_data); + + // Unlike young, we are more interested in efficiently packing OLD-gen than in reclaiming garbage first. We sort by live-data. + // Some regular regions may have been promoted in place with no garbage but also with very little live data. When we "compact" + // old-gen, we want to pack these underutilized regions together so we can have more unaffiliated (unfragmented) free regions + // in old-gen. + + QuickSort::sort(candidates, cand_idx, compare_by_live); + + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + + // The convention is to collect regions that have more than this amount of garbage. + const size_t garbage_threshold = region_size_bytes * ShenandoahOldGarbageThreshold / 100; + + // Enlightened interpretation: collect regions that have less than this amount of live. + const size_t live_threshold = region_size_bytes - garbage_threshold; + + _last_old_region = (uint)cand_idx; + _last_old_collection_candidate = (uint)cand_idx; + _next_old_collection_candidate = 0; + + size_t unfragmented = 0; + size_t candidates_garbage = 0; + + for (size_t i = 0; i < cand_idx; i++) { + size_t live = candidates[i].get_livedata(); + if (live > live_threshold) { + // Candidates are sorted in increasing order of live data, so no regions after this will be below the threshold. + _last_old_collection_candidate = (uint)i; + break; + } + ShenandoahHeapRegion* r = candidates[i].get_region(); + size_t region_garbage = r->garbage(); + size_t region_free = r->free(); + candidates_garbage += region_garbage; + unfragmented += region_free; + } + + // defrag_count represents regions that are placed into the old collection set in order to defragment the memory + // that we try to "reserve" for humongous allocations. + size_t defrag_count = 0; + size_t total_uncollected_old_regions = _last_old_region - _last_old_collection_candidate; + + if (cand_idx > _last_old_collection_candidate) { + // Above, we have added into the set of mixed-evacuation candidates all old-gen regions for which the live memory + // that they contain is below a particular old-garbage threshold. Regions that were not selected for the collection + // set hold enough live memory that it is not considered efficient (by "garbage-first standards") to compact these + // at the current time. + // + // However, if any of these regions that were rejected from the collection set reside within areas of memory that + // might interfere with future humongous allocation requests, we will prioritize them for evacuation at this time. + // Humongous allocations target the bottom of the heap. We want old-gen regions to congregate at the top of the + // heap. + // + // Sort the regions that were initially rejected from the collection set in order of index. This allows us to + // focus our attention on the regions that have low index value (i.e. the old-gen regions at the bottom of the heap). + QuickSort::sort(candidates + _last_old_collection_candidate, cand_idx - _last_old_collection_candidate, + compare_by_index); + + const size_t first_unselected_old_region = candidates[_last_old_collection_candidate].get_region()->index(); + const size_t last_unselected_old_region = candidates[cand_idx - 1].get_region()->index(); + size_t span_of_uncollected_regions = 1 + last_unselected_old_region - first_unselected_old_region; + + // Add no more than 1/8 of the existing old-gen regions to the set of mixed evacuation candidates. + const int MAX_FRACTION_OF_HUMONGOUS_DEFRAG_REGIONS = 8; + const size_t bound_on_additional_regions = cand_idx / MAX_FRACTION_OF_HUMONGOUS_DEFRAG_REGIONS; + + // The heuristic old_is_fragmented trigger may be seeking to achieve up to 75% density. Allow ourselves to overshoot + // that target (at 7/8) so we will not have to do another defragmenting old collection right away. + while ((defrag_count < bound_on_additional_regions) && + (total_uncollected_old_regions < 7 * span_of_uncollected_regions / 8)) { + ShenandoahHeapRegion* r = candidates[_last_old_collection_candidate].get_region(); + assert(r->is_regular() || r->is_regular_pinned(), "Region " SIZE_FORMAT " has wrong state for collection: %s", + r->index(), ShenandoahHeapRegion::region_state_to_string(r->state())); + const size_t region_garbage = r->garbage(); + const size_t region_free = r->free(); + candidates_garbage += region_garbage; + unfragmented += region_free; + defrag_count++; + _last_old_collection_candidate++; + + // We now have one fewer uncollected regions, and our uncollected span shrinks because we have removed its first region. + total_uncollected_old_regions--; + span_of_uncollected_regions = + 1 + last_unselected_old_region - candidates[_last_old_collection_candidate].get_region()->index(); + } + } + + // Note that we do not coalesce and fill occupied humongous regions + // HR: humongous regions, RR: regular regions, CF: coalesce and fill regions + const size_t collectable_garbage = immediate_garbage + candidates_garbage; + const size_t old_candidates = _last_old_collection_candidate; + const size_t mixed_evac_live = old_candidates * region_size_bytes - (candidates_garbage + unfragmented); + set_unprocessed_old_collection_candidates_live_memory(mixed_evac_live); + + log_info(gc, ergo)("Old-Gen Collectable Garbage: " PROPERFMT " consolidated with free: " PROPERFMT ", over " SIZE_FORMAT " regions", + PROPERFMTARGS(collectable_garbage), PROPERFMTARGS(unfragmented), old_candidates); + log_info(gc, ergo)("Old-Gen Immediate Garbage: " PROPERFMT " over " SIZE_FORMAT " regions", + PROPERFMTARGS(immediate_garbage), immediate_regions); + log_info(gc, ergo)("Old regions selected for defragmentation: " SIZE_FORMAT, defrag_count); + log_info(gc, ergo)("Old regions not selected: " SIZE_FORMAT, total_uncollected_old_regions); + + if (unprocessed_old_collection_candidates() > 0) { + _old_generation->transition_to(ShenandoahOldGeneration::EVACUATING); + } else if (has_coalesce_and_fill_candidates()) { + _old_generation->transition_to(ShenandoahOldGeneration::FILLING); + } else { + _old_generation->transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + } +} + +size_t ShenandoahOldHeuristics::unprocessed_old_collection_candidates_live_memory() const { + return _live_bytes_in_unprocessed_candidates; +} + +void ShenandoahOldHeuristics::set_unprocessed_old_collection_candidates_live_memory(size_t initial_live) { + _live_bytes_in_unprocessed_candidates = initial_live; +} + +void ShenandoahOldHeuristics::decrease_unprocessed_old_collection_candidates_live_memory(size_t evacuated_live) { + assert(evacuated_live <= _live_bytes_in_unprocessed_candidates, "Cannot evacuate more than was present"); + _live_bytes_in_unprocessed_candidates -= evacuated_live; +} + +// Used by unit test: test_shenandoahOldHeuristic.cpp +uint ShenandoahOldHeuristics::last_old_collection_candidate_index() const { + return _last_old_collection_candidate; +} + +uint ShenandoahOldHeuristics::unprocessed_old_collection_candidates() const { + return _last_old_collection_candidate - _next_old_collection_candidate; +} + +ShenandoahHeapRegion* ShenandoahOldHeuristics::next_old_collection_candidate() { + while (_next_old_collection_candidate < _last_old_collection_candidate) { + ShenandoahHeapRegion* next = _region_data[_next_old_collection_candidate].get_region(); + if (!next->is_pinned()) { + return next; + } else { + assert(next->is_pinned(), "sanity"); + if (_first_pinned_candidate == NOT_FOUND) { + _first_pinned_candidate = _next_old_collection_candidate; + } + } + + _next_old_collection_candidate++; + } + return nullptr; +} + +void ShenandoahOldHeuristics::consume_old_collection_candidate() { + _next_old_collection_candidate++; +} + +unsigned int ShenandoahOldHeuristics::get_coalesce_and_fill_candidates(ShenandoahHeapRegion** buffer) { + uint end = _last_old_region; + uint index = _next_old_collection_candidate; + while (index < end) { + *buffer++ = _region_data[index++].get_region(); + } + return (_last_old_region - _next_old_collection_candidate); +} + +void ShenandoahOldHeuristics::abandon_collection_candidates() { + _last_old_collection_candidate = 0; + _next_old_collection_candidate = 0; + _last_old_region = 0; +} + +void ShenandoahOldHeuristics::record_cycle_end() { + this->ShenandoahHeuristics::record_cycle_end(); + clear_triggers(); +} + +void ShenandoahOldHeuristics::clear_triggers() { + // Clear any triggers that were set during mixed evacuations. Conditions may be different now that this phase has finished. + _cannot_expand_trigger = false; + _fragmentation_trigger = false; + _growth_trigger = false; +} + +// This triggers old-gen collection if the number of regions "dedicated" to old generation is much larger than +// is required to represent the memory currently used within the old generation. This trigger looks specifically +// at density of the old-gen spanned region. A different mechanism triggers old-gen GC if the total number of +// old-gen regions (regardless of how close the regions are to one another) grows beyond an anticipated growth target. +void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_region, size_t last_old_region, + size_t old_region_count, size_t num_regions) { + if (ShenandoahGenerationalHumongousReserve > 0) { + // Our intent is to pack old-gen memory into the highest-numbered regions of the heap. Count all memory + // above first_old_region as the "span" of old generation. + size_t old_region_span = (first_old_region <= last_old_region)? (num_regions - first_old_region): 0; + // Given that memory at the bottom of the heap is reserved to represent humongous objects, the number of + // regions that old_gen is "allowed" to consume is less than the total heap size. The restriction on allowed + // span is not strictly enforced. This is a heuristic designed to reduce the likelihood that a humongous + // allocation request will require a STW full GC. + size_t allowed_old_gen_span = num_regions - (ShenandoahGenerationalHumongousReserve * num_regions) / 100; + + size_t old_available = _old_gen->available() / HeapWordSize; + size_t region_size_words = ShenandoahHeapRegion::region_size_words(); + size_t old_unaffiliated_available = _old_gen->free_unaffiliated_regions() * region_size_words; + assert(old_available >= old_unaffiliated_available, "sanity"); + size_t old_fragmented_available = old_available - old_unaffiliated_available; + + size_t old_words_consumed = old_region_count * region_size_words - old_fragmented_available; + size_t old_words_spanned = old_region_span * region_size_words; + double old_density = ((double) old_words_consumed) / old_words_spanned; + + double old_span_percent = ((double) old_region_span) / allowed_old_gen_span; + if (old_span_percent > 0.50) { + // Squaring old_span_percent in the denominator below allows more aggressive triggering when we are + // above desired maximum span and less aggressive triggering when we are far below the desired maximum span. + double old_span_percent_squared = old_span_percent * old_span_percent; + if (old_density / old_span_percent_squared < 0.75) { + // We trigger old defragmentation, for example, if: + // old_span_percent is 110% and old_density is below 90.8%, or + // old_span_percent is 100% and old_density is below 75.0%, or + // old_span_percent is 90% and old_density is below 60.8%, or + // old_span_percent is 80% and old_density is below 48.0%, or + // old_span_percent is 70% and old_density is below 36.8%, or + // old_span_percent is 60% and old_density is below 27.0%, or + // old_span_percent is 50% and old_density is below 18.8%. + + // Set the fragmentation trigger and related attributes + _fragmentation_trigger = true; + _fragmentation_density = old_density; + _fragmentation_first_old_region = first_old_region; + _fragmentation_last_old_region = last_old_region; + } + } + } +} + +void ShenandoahOldHeuristics::set_trigger_if_old_is_overgrown() { + size_t old_used = _old_gen->used() + _old_gen->get_humongous_waste(); + size_t trigger_threshold = _old_gen->usage_trigger_threshold(); + // Detects unsigned arithmetic underflow + assert(old_used <= _heap->capacity(), + "Old used (" SIZE_FORMAT ", " SIZE_FORMAT") must not be more than heap capacity (" SIZE_FORMAT ")", + _old_gen->used(), _old_gen->get_humongous_waste(), _heap->capacity()); + if (old_used > trigger_threshold) { + _growth_trigger = true; + } +} + +void ShenandoahOldHeuristics::evaluate_triggers(size_t first_old_region, size_t last_old_region, + size_t old_region_count, size_t num_regions) { + set_trigger_if_old_is_fragmented(first_old_region, last_old_region, old_region_count, num_regions); + set_trigger_if_old_is_overgrown(); +} + +bool ShenandoahOldHeuristics::should_start_gc() { + // Cannot start a new old-gen GC until previous one has finished. + // + // Future refinement: under certain circumstances, we might be more sophisticated about this choice. + // For example, we could choose to abandon the previous old collection before it has completed evacuations. + ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (!_old_generation->can_start_gc() || heap->collection_set()->has_old_regions()) { + return false; + } + + if (_cannot_expand_trigger) { + const size_t old_gen_capacity = _old_generation->max_capacity(); + const size_t heap_capacity = heap->capacity(); + const double percent = percent_of(old_gen_capacity, heap_capacity); + log_trigger("Expansion failure, current size: " SIZE_FORMAT "%s which is %.1f%% of total heap size", + byte_size_in_proper_unit(old_gen_capacity), proper_unit_for_byte_size(old_gen_capacity), percent); + return true; + } + + if (_fragmentation_trigger) { + const size_t used = _old_generation->used(); + const size_t used_regions_size = _old_generation->used_regions_size(); + + // used_regions includes humongous regions + const size_t used_regions = _old_generation->used_regions(); + assert(used_regions_size > used_regions, "Cannot have more used than used regions"); + + size_t first_old_region, last_old_region; + double density; + get_fragmentation_trigger_reason_for_log_message(density, first_old_region, last_old_region); + const size_t span_of_old_regions = (last_old_region >= first_old_region)? last_old_region + 1 - first_old_region: 0; + const size_t fragmented_free = used_regions_size - used; + + log_trigger("Old has become fragmented: " + SIZE_FORMAT "%s available bytes spread between range spanned from " + SIZE_FORMAT " to " SIZE_FORMAT " (" SIZE_FORMAT "), density: %.1f%%", + byte_size_in_proper_unit(fragmented_free), proper_unit_for_byte_size(fragmented_free), + first_old_region, last_old_region, span_of_old_regions, density * 100); + return true; + } + + if (_growth_trigger) { + // Growth may be falsely triggered during mixed evacuations, before the mixed-evacuation candidates have been + // evacuated. Before acting on a false trigger, we check to confirm the trigger condition is still satisfied. + const size_t current_usage = _old_generation->used() + _old_generation->get_humongous_waste(); + const size_t trigger_threshold = _old_generation->usage_trigger_threshold(); + const size_t heap_size = heap->capacity(); + const size_t ignore_threshold = (ShenandoahIgnoreOldGrowthBelowPercentage * heap_size) / 100; + size_t consecutive_young_cycles; + if ((current_usage < ignore_threshold) && + ((consecutive_young_cycles = heap->shenandoah_policy()->consecutive_young_gc_count()) + < ShenandoahDoNotIgnoreGrowthAfterYoungCycles)) { + log_debug(gc)("Ignoring Trigger: Old has overgrown: usage (" SIZE_FORMAT "%s) is below threshold (" + SIZE_FORMAT "%s) after " SIZE_FORMAT " consecutive completed young GCs", + byte_size_in_proper_unit(current_usage), proper_unit_for_byte_size(current_usage), + byte_size_in_proper_unit(ignore_threshold), proper_unit_for_byte_size(ignore_threshold), + consecutive_young_cycles); + _growth_trigger = false; + } else if (current_usage > trigger_threshold) { + const size_t live_at_previous_old = _old_generation->get_live_bytes_after_last_mark(); + const double percent_growth = percent_of(current_usage - live_at_previous_old, live_at_previous_old); + log_trigger("Old has overgrown, live at end of previous OLD marking: " + SIZE_FORMAT "%s, current usage: " SIZE_FORMAT "%s, percent growth: %.1f%%", + byte_size_in_proper_unit(live_at_previous_old), proper_unit_for_byte_size(live_at_previous_old), + byte_size_in_proper_unit(current_usage), proper_unit_for_byte_size(current_usage), percent_growth); + return true; + } else { + // Mixed evacuations have decreased current_usage such that old-growth trigger is no longer relevant. + _growth_trigger = false; + } + } + + // Otherwise, defer to inherited heuristic for gc trigger. + return this->ShenandoahHeuristics::should_start_gc(); +} + +void ShenandoahOldHeuristics::record_success_concurrent() { + // Forget any triggers that occurred while OLD GC was ongoing. If we really need to start another, it will retrigger. + clear_triggers(); + this->ShenandoahHeuristics::record_success_concurrent(); +} + +void ShenandoahOldHeuristics::record_success_degenerated() { + // Forget any triggers that occurred while OLD GC was ongoing. If we really need to start another, it will retrigger. + clear_triggers(); + this->ShenandoahHeuristics::record_success_degenerated(); +} + +void ShenandoahOldHeuristics::record_success_full() { + // Forget any triggers that occurred while OLD GC was ongoing. If we really need to start another, it will retrigger. + clear_triggers(); + this->ShenandoahHeuristics::record_success_full(); +} + +const char* ShenandoahOldHeuristics::name() { + return "Old"; +} + +bool ShenandoahOldHeuristics::is_diagnostic() { + return false; +} + +bool ShenandoahOldHeuristics::is_experimental() { + return true; +} + +void ShenandoahOldHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + ShenandoahHeuristics::RegionData* data, + size_t data_size, size_t free) { + ShouldNotReachHere(); +} diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp new file mode 100644 index 00000000000..d77380926b6 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -0,0 +1,206 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHOLDHEURISTICS_HPP +#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHOLDHEURISTICS_HPP + + +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" + +class ShenandoahCollectionSet; +class ShenandoahHeapRegion; +class ShenandoahOldGeneration; + +/* + * This heuristic is responsible for choosing a set of candidates for inclusion + * in mixed collections. These candidates are chosen when marking of the old + * generation is complete. Note that this list of candidates may live through + * several mixed collections. + * + * This heuristic is also responsible for triggering old collections. It has its + * own collection of triggers to decide whether to start an old collection. It does + * _not_ use any of the functionality from the adaptive heuristics for triggers. + * It also does not use any of the functionality from the heuristics base classes + * to choose the collection set. For these reasons, it does not extend from + * ShenandoahGenerationalHeuristics. + */ +class ShenandoahOldHeuristics : public ShenandoahHeuristics { + +private: + + static uint NOT_FOUND; + + ShenandoahGenerationalHeap* _heap; + ShenandoahOldGeneration* _old_gen; + + // After final marking of the old generation, this heuristic will select + // a set of candidate regions to be included in subsequent mixed collections. + // The regions are sorted into a `_region_data` array (declared in base + // class) in decreasing order of garbage. The heuristic will give priority + // to regions containing more garbage. + + // The following members are used to keep track of which candidate regions + // have yet to be added to a mixed collection. There is also some special + // handling for pinned regions, described further below. + + // Pinned regions may not be included in the collection set. Any old regions + // which were pinned at the time when old regions were added to the mixed + // collection will have been skipped. These regions are still contain garbage, + // so we want to include them at the start of the list of candidates for the + // _next_ mixed collection cycle. This variable is the index of the _first_ + // old region which is pinned when the mixed collection set is formed. + uint _first_pinned_candidate; + + // This is the index of the last region which is above the garbage threshold. + // No regions after this will be considered for inclusion in a mixed collection + // set. + uint _last_old_collection_candidate; + + // This index points to the first candidate in line to be added to the mixed + // collection set. It is updated as regions are added to the collection set. + uint _next_old_collection_candidate; + + // This is the last index in the array of old regions which were active at + // the end of old final mark. + uint _last_old_region; + + // How much live data must be evacuated from within the unprocessed mixed evacuation candidates? + size_t _live_bytes_in_unprocessed_candidates; + + // Keep a pointer to our generation that we can use without down casting a protected member from the base class. + ShenandoahOldGeneration* _old_generation; + + // Flags are set when promotion failure is detected (by gc thread), and cleared when + // old generation collection begins (by control thread). Flags are set and cleared at safepoints. + bool _cannot_expand_trigger; + bool _fragmentation_trigger; + bool _growth_trigger; + + // Motivation for a fragmentation_trigger + double _fragmentation_density; + size_t _fragmentation_first_old_region; + size_t _fragmentation_last_old_region; + + // Compare by live is used to prioritize compaction of old-gen regions. With old-gen compaction, the goal is + // to tightly pack long-lived objects into available regions. In most cases, there has not been an accumulation + // of garbage within old-gen regions. The more likely opportunity will be to combine multiple sparsely populated + // old-gen regions which may have been promoted in place into a smaller number of densely packed old-gen regions. + // This improves subsequent allocation efficiency and reduces the likelihood of allocation failure (including + // humongous allocation failure) due to fragmentation of the available old-gen allocation pool + static int compare_by_live(RegionData a, RegionData b); + + static int compare_by_index(RegionData a, RegionData b); + + // Set the fragmentation trigger if old-gen memory has become fragmented. + void set_trigger_if_old_is_fragmented(size_t first_old_region, size_t last_old_region, + size_t old_region_count, size_t num_regions); + + // Set the overgrowth trigger if old-gen memory has grown beyond a particular threshold. + void set_trigger_if_old_is_overgrown(); + + protected: + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, RegionData* data, size_t data_size, size_t free) override; + +public: + explicit ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap); + + // Prepare for evacuation of old-gen regions by capturing the mark results of a recently completed concurrent mark pass. + void prepare_for_old_collections(); + + // Return true iff the collection set is primed with at least one old-gen region. + bool prime_collection_set(ShenandoahCollectionSet* set); + + // How many old-collection candidates have not yet been processed? + uint unprocessed_old_collection_candidates() const; + + // How much live memory must be evacuated from within old-collection candidates that have not yet been processed? + size_t unprocessed_old_collection_candidates_live_memory() const; + + void set_unprocessed_old_collection_candidates_live_memory(size_t initial_live); + + void decrease_unprocessed_old_collection_candidates_live_memory(size_t evacuated_live); + + // How many old or hidden collection candidates have not yet been processed? + uint last_old_collection_candidate_index() const; + + // Return the next old-collection candidate in order of decreasing amounts of garbage. (We process most-garbage regions + // first.) This does not consume the candidate. If the candidate is selected for inclusion in a collection set, then + // the candidate is consumed by invoking consume_old_collection_candidate(). + ShenandoahHeapRegion* next_old_collection_candidate(); + + // Adjust internal state to reflect that one fewer old-collection candidate remains to be processed. + void consume_old_collection_candidate(); + + // Fill in buffer with all the old-collection regions that were identified at the end of the most recent old-gen + // mark to require their unmarked objects to be coalesced and filled. The buffer array must have at least + // last_old_region_index() entries, or memory may be corrupted when this function overwrites the + // end of the array. + unsigned int get_coalesce_and_fill_candidates(ShenandoahHeapRegion** buffer); + + // True if there are old regions that need to be filled. + bool has_coalesce_and_fill_candidates() const { return coalesce_and_fill_candidates_count() > 0; } + + // Return the number of old regions that need to be filled. + size_t coalesce_and_fill_candidates_count() const { return _last_old_region - _next_old_collection_candidate; } + + // If a GLOBAL gc occurs, it will collect the entire heap which invalidates any collection candidates being + // held by this heuristic for supplying mixed collections. + void abandon_collection_candidates(); + + void trigger_cannot_expand() { _cannot_expand_trigger = true; }; + + inline void get_fragmentation_trigger_reason_for_log_message(double &density, size_t &first_index, size_t &last_index) { + density = _fragmentation_density; + first_index = _fragmentation_first_old_region; + last_index = _fragmentation_last_old_region; + } + + void clear_triggers(); + + // Check whether conditions merit the start of old GC. Set appropriate trigger if so. + void evaluate_triggers(size_t first_old_region, size_t last_old_region, size_t old_region_count, size_t num_regions); + + void record_cycle_end() override; + + bool should_start_gc() override; + + void record_success_concurrent() override; + + void record_success_degenerated() override; + + void record_success_full() override; + + const char* name() override; + + bool is_diagnostic() override; + + bool is_experimental() override; + +private: + void slide_pinned_regions_to_front(); + bool all_candidates_are_pinned(); +}; + +#endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHOLDHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp index 2e6b3d46ebe..7c65482d8c4 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp @@ -68,7 +68,7 @@ void ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(Shenando size_t live_cset = 0; for (size_t idx = 0; idx < size; idx++) { - ShenandoahHeapRegion* r = data[idx]._region; + ShenandoahHeapRegion* r = data[idx].get_region(); size_t new_cset = live_cset + r->get_live_data_bytes(); if (new_cset < max_cset && r->garbage() > threshold) { live_cset = new_cset; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp index 3a58196da3c..29b94e2f68f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp @@ -36,9 +36,12 @@ */ class ShenandoahSpaceInfo { public: + virtual const char* name() const = 0; virtual size_t soft_max_capacity() const = 0; virtual size_t max_capacity() const = 0; + virtual size_t soft_available() const = 0; virtual size_t available() const = 0; + virtual size_t used() const = 0; virtual size_t bytes_allocated_since_gc_start() const = 0; }; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp index ee59194feb6..db179d0a80a 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp @@ -52,7 +52,7 @@ bool ShenandoahStaticHeuristics::should_start_gc() { size_t threshold_available = capacity / 100 * ShenandoahMinFreeThreshold; if (available < threshold_available) { - log_info(gc)("Trigger: Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)", + log_trigger("Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)", byte_size_in_proper_unit(available), proper_unit_for_byte_size(available), byte_size_in_proper_unit(threshold_available), proper_unit_for_byte_size(threshold_available)); return true; @@ -66,7 +66,7 @@ void ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(Shenandoa size_t threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100; for (size_t idx = 0; idx < size; idx++) { - ShenandoahHeapRegion* r = data[idx]._region; + ShenandoahHeapRegion* r = data[idx].get_region(); if (r->garbage() > threshold) { cset->add_region(r); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp new file mode 100644 index 00000000000..ced406611b1 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -0,0 +1,224 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" + +#include "utilities/quickSort.hpp" + +ShenandoahYoungHeuristics::ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation) + : ShenandoahGenerationalHeuristics(generation) { +} + + +void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { + // See comments in ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(): + // we do the same here, but with the following adjustments for generational mode: + // + // In generational mode, the sort order within the data array is not strictly descending amounts + // of garbage. In particular, regions that have reached tenure age will be sorted into this + // array before younger regions that typically contain more garbage. This is one reason why, + // for example, we continue examining regions even after rejecting a region that has + // more live data than we can evacuate. + + // Better select garbage-first regions + QuickSort::sort(data, (int) size, compare_by_garbage); + + size_t cur_young_garbage = add_preselected_regions_to_collection_set(cset, data, size); + + choose_young_collection_set(cset, data, size, actual_free, cur_young_garbage); + + log_cset_composition(cset); +} + +void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollectionSet* cset, + const RegionData* data, + size_t size, size_t actual_free, + size_t cur_young_garbage) const { + + auto heap = ShenandoahGenerationalHeap::heap(); + + size_t capacity = heap->young_generation()->max_capacity(); + size_t garbage_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100; + size_t ignore_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahIgnoreGarbageThreshold / 100; + const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); + + // This is young-gen collection or a mixed evacuation. + // If this is mixed evacuation, the old-gen candidate regions have already been added. + size_t max_cset = (size_t) (heap->young_generation()->get_evacuation_reserve() / ShenandoahEvacWaste); + size_t cur_cset = 0; + size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_cset; + size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0; + + + log_info(gc, ergo)( + "Adaptive CSet Selection for YOUNG. Max Evacuation: " SIZE_FORMAT "%s, Actual Free: " SIZE_FORMAT "%s.", + byte_size_in_proper_unit(max_cset), proper_unit_for_byte_size(max_cset), + byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + + for (size_t idx = 0; idx < size; idx++) { + ShenandoahHeapRegion* r = data[idx].get_region(); + if (cset->is_preselected(r->index())) { + continue; + } + if (r->age() < tenuring_threshold) { + size_t new_cset = cur_cset + r->get_live_data_bytes(); + size_t region_garbage = r->garbage(); + size_t new_garbage = cur_young_garbage + region_garbage; + bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage); + assert(r->is_young(), "Only young candidates expected in the data array"); + if ((new_cset <= max_cset) && (add_regardless || (region_garbage > garbage_threshold))) { + cur_cset = new_cset; + cur_young_garbage = new_garbage; + cset->add_region(r); + } + } + // Note that we do not add aged regions if they were not pre-selected. The reason they were not preselected + // is because there is not sufficient room in old-gen to hold their to-be-promoted live objects or because + // they are to be promoted in place. + } +} + + +bool ShenandoahYoungHeuristics::should_start_gc() { + auto heap = ShenandoahGenerationalHeap::heap(); + ShenandoahOldGeneration* old_generation = heap->old_generation(); + ShenandoahOldHeuristics* old_heuristics = old_generation->heuristics(); + + // Checks that an old cycle has run for at least ShenandoahMinimumOldTimeMs before allowing a young cycle. + if (ShenandoahMinimumOldTimeMs > 0) { + if (old_generation->is_preparing_for_mark() || old_generation->is_concurrent_mark_in_progress()) { + size_t old_time_elapsed = size_t(old_heuristics->elapsed_cycle_time() * 1000); + if (old_time_elapsed < ShenandoahMinimumOldTimeMs) { + return false; + } + } + } + + // inherited triggers have already decided to start a cycle, so no further evaluation is required + if (ShenandoahAdaptiveHeuristics::should_start_gc()) { + return true; + } + + // Get through promotions and mixed evacuations as quickly as possible. These cycles sometimes require significantly + // more time than traditional young-generation cycles so start them up as soon as possible. This is a "mitigation" + // for the reality that old-gen and young-gen activities are not truly "concurrent". If there is old-gen work to + // be done, we start up the young-gen GC threads so they can do some of this old-gen work. As implemented, promotion + // gets priority over old-gen marking. + size_t promo_expedite_threshold = percent_of(heap->young_generation()->max_capacity(), ShenandoahExpeditePromotionsThreshold); + size_t promo_potential = old_generation->get_promotion_potential(); + if (promo_potential > promo_expedite_threshold) { + // Detect unsigned arithmetic underflow + assert(promo_potential < heap->capacity(), "Sanity"); + log_trigger("Expedite promotion of " PROPERFMT, PROPERFMTARGS(promo_potential)); + return true; + } + + size_t mixed_candidates = old_heuristics->unprocessed_old_collection_candidates(); + if (mixed_candidates > ShenandoahExpediteMixedThreshold && !heap->is_concurrent_weak_root_in_progress()) { + // We need to run young GC in order to open up some free heap regions so we can finish mixed evacuations. + // If concurrent weak root processing is in progress, it means the old cycle has chosen mixed collection + // candidates, but has not completed. There is no point in trying to start the young cycle before the old + // cycle completes. + log_trigger("Expedite mixed evacuation of " SIZE_FORMAT " regions", mixed_candidates); + return true; + } + + return false; +} + +// Return a conservative estimate of how much memory can be allocated before we need to start GC. The estimate is based +// on memory that is currently available within young generation plus all of the memory that will be added to the young +// generation at the end of the current cycle (as represented by young_regions_to_be_reclaimed) and on the anticipated +// amount of time required to perform a GC. +size_t ShenandoahYoungHeuristics::bytes_of_allocation_runway_before_gc_trigger(size_t young_regions_to_be_reclaimed) { + size_t capacity = _space_info->max_capacity(); + size_t usage = _space_info->used(); + size_t available = (capacity > usage)? capacity - usage: 0; + size_t allocated = _space_info->bytes_allocated_since_gc_start(); + + size_t available_young_collected = ShenandoahHeap::heap()->collection_set()->get_young_available_bytes_collected(); + size_t anticipated_available = + available + young_regions_to_be_reclaimed * ShenandoahHeapRegion::region_size_bytes() - available_young_collected; + size_t spike_headroom = capacity * ShenandoahAllocSpikeFactor / 100; + size_t penalties = capacity * _gc_time_penalties / 100; + + double rate = _allocation_rate.sample(allocated); + + // At what value of available, would avg and spike triggers occur? + // if allocation_headroom < avg_cycle_time * avg_alloc_rate, then we experience avg trigger + // if allocation_headroom < avg_cycle_time * rate, then we experience spike trigger if is_spiking + // + // allocation_headroom = + // 0, if penalties > available or if penalties + spike_headroom > available + // available - penalties - spike_headroom, otherwise + // + // so we trigger if available - penalties - spike_headroom < avg_cycle_time * avg_alloc_rate, which is to say + // available < avg_cycle_time * avg_alloc_rate + penalties + spike_headroom + // or if available < penalties + spike_headroom + // + // since avg_cycle_time * avg_alloc_rate > 0, the first test is sufficient to test both conditions + // + // thus, evac_slack_avg is MIN2(0, available - avg_cycle_time * avg_alloc_rate + penalties + spike_headroom) + // + // similarly, evac_slack_spiking is MIN2(0, available - avg_cycle_time * rate + penalties + spike_headroom) + // but evac_slack_spiking is only relevant if is_spiking, as defined below. + + double avg_cycle_time = _gc_cycle_time_history->davg() + (_margin_of_error_sd * _gc_cycle_time_history->dsd()); + double avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd); + size_t evac_slack_avg; + if (anticipated_available > avg_cycle_time * avg_alloc_rate + penalties + spike_headroom) { + evac_slack_avg = anticipated_available - (avg_cycle_time * avg_alloc_rate + penalties + spike_headroom); + } else { + // we have no slack because it's already time to trigger + evac_slack_avg = 0; + } + + bool is_spiking = _allocation_rate.is_spiking(rate, _spike_threshold_sd); + size_t evac_slack_spiking; + if (is_spiking) { + if (anticipated_available > avg_cycle_time * rate + penalties + spike_headroom) { + evac_slack_spiking = anticipated_available - (avg_cycle_time * rate + penalties + spike_headroom); + } else { + // we have no slack because it's already time to trigger + evac_slack_spiking = 0; + } + } else { + evac_slack_spiking = evac_slack_avg; + } + + size_t threshold = min_free_threshold(); + size_t evac_min_threshold = (anticipated_available > threshold)? anticipated_available - threshold: 0; + return MIN3(evac_slack_spiking, evac_slack_avg, evac_min_threshold); +} + diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp new file mode 100644 index 00000000000..b9d64059680 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp @@ -0,0 +1,57 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ +#ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHYOUNGHEURISTICS_HPP +#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHYOUNGHEURISTICS_HPP + +#include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" + +class ShenandoahYoungGeneration; + +/* + * This is a specialization of the generational heuristic which chooses + * young regions for evacuation. This heuristic also has additional triggers + * designed to expedite mixed collections and promotions. + */ +class ShenandoahYoungHeuristics : public ShenandoahGenerationalHeuristics { +public: + explicit ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation); + + + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; + + bool should_start_gc() override; + + size_t bytes_of_allocation_runway_before_gc_trigger(size_t young_regions_to_be_reclaimed); + +private: + void choose_young_collection_set(ShenandoahCollectionSet* cset, + const RegionData* data, + size_t size, size_t actual_free, + size_t cur_young_garbage) const; + +}; + +#endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHYOUNGHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp new file mode 100644 index 00000000000..3a34a9aa6a8 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com Inc. 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" +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" +#include "logging/log.hpp" +#include "logging/logTag.hpp" +#include "runtime/globals_extension.hpp" + +void ShenandoahGenerationalMode::initialize_flags() const { + +#if !(defined AARCH64 || defined AMD64 || defined IA32 || defined PPC64 || defined RISCV64) + vm_exit_during_initialization("Shenandoah Generational GC is not supported on this platform."); +#endif + + // Exit if the user has asked ShenandoahCardBarrier to be disabled + if (!FLAG_IS_DEFAULT(ShenandoahCardBarrier)) { + SHENANDOAH_CHECK_FLAG_SET(ShenandoahCardBarrier); + } + + // Enable card-marking post-write barrier for tracking old to young pointers + FLAG_SET_DEFAULT(ShenandoahCardBarrier, true); + + if (ClassUnloading) { + FLAG_SET_DEFAULT(VerifyBeforeExit, false); + } + + SHENANDOAH_ERGO_OVERRIDE_DEFAULT(GCTimeRatio, 70); + SHENANDOAH_ERGO_ENABLE_FLAG(ExplicitGCInvokesConcurrent); + SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent); + + // This helps most multi-core hardware hosts, enable by default + SHENANDOAH_ERGO_ENABLE_FLAG(UseCondCardMark); + + // Final configuration checks + SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier); + SHENANDOAH_CHECK_FLAG_SET(ShenandoahSATBBarrier); + SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); + SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); + SHENANDOAH_CHECK_FLAG_SET(ShenandoahCardBarrier); +} diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.hpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.hpp new file mode 100644 index 00000000000..0946858169a --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.hpp @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_MODE_SHENANDOAHGENERATIONALMODE_HPP +#define SHARE_GC_SHENANDOAH_MODE_SHENANDOAHGENERATIONALMODE_HPP + +#include "gc/shenandoah/mode/shenandoahMode.hpp" + +class ShenandoahGenerationalMode : public ShenandoahMode { +public: + virtual void initialize_flags() const; + virtual const char* name() { return "Generational"; } + virtual bool is_diagnostic() { return false; } + virtual bool is_experimental() { return true; } + virtual bool is_generational() { return true; } +}; + +#endif // SHARE_GC_SHENANDOAH_MODE_SHENANDOAHGENERATIONALMODE_HPP diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp new file mode 100644 index 00000000000..126062ab993 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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" +#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" +#include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" + +ShenandoahHeuristics* ShenandoahMode::initialize_heuristics(ShenandoahSpaceInfo* space_info) const { + if (ShenandoahGCHeuristics == nullptr) { + vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)"); + } + + if (strcmp(ShenandoahGCHeuristics, "aggressive") == 0) { + return new ShenandoahAggressiveHeuristics(space_info); + } else if (strcmp(ShenandoahGCHeuristics, "static") == 0) { + return new ShenandoahStaticHeuristics(space_info); + } else if (strcmp(ShenandoahGCHeuristics, "adaptive") == 0) { + return new ShenandoahAdaptiveHeuristics(space_info); + } else if (strcmp(ShenandoahGCHeuristics, "compact") == 0) { + return new ShenandoahCompactHeuristics(space_info); + } else { + vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option"); + } + + ShouldNotReachHere(); + return nullptr; +} + diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.hpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.hpp index 5af6fa826d5..f3e98d92b2e 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.hpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,8 +26,12 @@ #ifndef SHARE_GC_SHENANDOAH_MODE_SHENANDOAHMODE_HPP #define SHARE_GC_SHENANDOAH_MODE_SHENANDOAHMODE_HPP +#include "gc/shared/gc_globals.hpp" #include "memory/allocation.hpp" +#include "runtime/java.hpp" +#include "utilities/formatBuffer.hpp" +class ShenandoahSpaceInfo; class ShenandoahHeuristics; #define SHENANDOAH_CHECK_FLAG_SET(name) \ @@ -48,10 +53,11 @@ class ShenandoahHeuristics; class ShenandoahMode : public CHeapObj { public: virtual void initialize_flags() const = 0; - virtual ShenandoahHeuristics* initialize_heuristics() const = 0; + virtual ShenandoahHeuristics* initialize_heuristics(ShenandoahSpaceInfo* space_info) const; virtual const char* name() = 0; virtual bool is_diagnostic() = 0; virtual bool is_experimental() = 0; + virtual bool is_generational() { return false; } }; #endif // SHARE_GC_SHENANDOAH_MODE_SHENANDOAHMODE_HPP diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp index 9b83df5424d..5c0ec2f884f 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp @@ -23,12 +23,13 @@ */ #include "precompiled.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" -#include "runtime/globals_extension.hpp" #include "runtime/java.hpp" void ShenandoahPassiveMode::initialize_flags() const { @@ -50,13 +51,17 @@ void ShenandoahPassiveMode::initialize_flags() const { SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCASBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCloneBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahStackWatermarkBarrier); + SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCardBarrier); // Final configuration checks - // No barriers are required to run. + // Passive mode does not instantiate the machinery to support the card table. + // Exit if the flag has been explicitly set. + SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahCardBarrier); } -ShenandoahHeuristics* ShenandoahPassiveMode::initialize_heuristics() const { + +ShenandoahHeuristics* ShenandoahPassiveMode::initialize_heuristics(ShenandoahSpaceInfo* space_info) const { if (ShenandoahGCHeuristics == nullptr) { vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)"); } - return new ShenandoahPassiveHeuristics(ShenandoahHeap::heap()); + return new ShenandoahPassiveHeuristics(space_info); } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.hpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.hpp index c0e778174b3..032092a70ec 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.hpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.hpp @@ -30,8 +30,7 @@ class ShenandoahPassiveMode : public ShenandoahMode { public: virtual void initialize_flags() const; - virtual ShenandoahHeuristics* initialize_heuristics() const; - + virtual ShenandoahHeuristics* initialize_heuristics(ShenandoahSpaceInfo* space_info) const; virtual const char* name() { return "Passive"; } virtual bool is_diagnostic() { return true; } virtual bool is_experimental() { return false; } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp index c3ea11cf71e..f5023c7cae9 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp @@ -23,12 +23,8 @@ */ #include "precompiled.hpp" -#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/mode/shenandoahSATBMode.hpp" -#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" #include "runtime/globals_extension.hpp" @@ -48,22 +44,5 @@ void ShenandoahSATBMode::initialize_flags() const { SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahStackWatermarkBarrier); -} - -ShenandoahHeuristics* ShenandoahSATBMode::initialize_heuristics() const { - if (ShenandoahGCHeuristics == nullptr) { - vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)"); - } - ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (strcmp(ShenandoahGCHeuristics, "aggressive") == 0) { - return new ShenandoahAggressiveHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "static") == 0) { - return new ShenandoahStaticHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "adaptive") == 0) { - return new ShenandoahAdaptiveHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "compact") == 0) { - return new ShenandoahCompactHeuristics(heap); - } - vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option"); - return nullptr; + SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahCardBarrier); } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.hpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.hpp index f246f9b20c7..3c2ae5bde93 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.hpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.hpp @@ -32,7 +32,6 @@ class ShenandoahHeuristics; class ShenandoahSATBMode : public ShenandoahMode { public: virtual void initialize_flags() const; - virtual ShenandoahHeuristics* initialize_heuristics() const; virtual const char* name() { return "Snapshot-At-The-Beginning (SATB)"; } virtual bool is_diagnostic() { return false; } virtual bool is_experimental() { return false; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAffiliation.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAffiliation.hpp new file mode 100644 index 00000000000..6b3c846f5fe --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAffiliation.hpp @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHAFFILIATION_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHAFFILIATION_HPP + +enum ShenandoahAffiliation { + FREE, + YOUNG_GENERATION, + OLD_GENERATION, +}; + +inline const char* shenandoah_affiliation_code(ShenandoahAffiliation type) { + switch(type) { + case FREE: + return "F"; + case YOUNG_GENERATION: + return "Y"; + case OLD_GENERATION: + return "O"; + default: + ShouldNotReachHere(); + } +} + +inline const char* shenandoah_affiliation_name(ShenandoahAffiliation type) { + switch (type) { + case FREE: + return "FREE"; + case YOUNG_GENERATION: + return "YOUNG"; + case OLD_GENERATION: + return "OLD"; + default: + ShouldNotReachHere(); + } +} + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHAFFILIATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp new file mode 100644 index 00000000000..7c574a9d0dd --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp @@ -0,0 +1,383 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" +#include "gc/shenandoah/shenandoahAgeCensus.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" + +ShenandoahAgeCensus::ShenandoahAgeCensus() { + assert(ShenandoahHeap::heap()->mode()->is_generational(), "Only in generational mode"); + if (ShenandoahGenerationalMinTenuringAge > ShenandoahGenerationalMaxTenuringAge) { + vm_exit_during_initialization( + err_msg("ShenandoahGenerationalMinTenuringAge=" SIZE_FORMAT + " should be no more than ShenandoahGenerationalMaxTenuringAge=" SIZE_FORMAT, + ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge)); + } + + _global_age_table = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC); + CENSUS_NOISE(_global_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, MAX_SNAPSHOTS, mtGC);) + _tenuring_threshold = NEW_C_HEAP_ARRAY(uint, MAX_SNAPSHOTS, mtGC); + + for (int i = 0; i < MAX_SNAPSHOTS; i++) { + // Note that we don't now get perfdata from age_table + _global_age_table[i] = new AgeTable(false); + CENSUS_NOISE(_global_noise[i].clear();) + // Sentinel value + _tenuring_threshold[i] = MAX_COHORTS; + } + if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) { + size_t max_workers = ShenandoahHeap::heap()->max_workers(); + _local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, max_workers, mtGC); + CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);) + for (uint i = 0; i < max_workers; i++) { + _local_age_table[i] = new AgeTable(false); + CENSUS_NOISE(_local_noise[i].clear();) + } + } else { + _local_age_table = nullptr; + } + _epoch = MAX_SNAPSHOTS - 1; // see update_epoch() +} + +CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, uint region_youth, size_t size, uint worker_id) {) +NO_CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, size_t size, uint worker_id) {) + if (obj_age <= markWord::max_age) { + assert(obj_age < MAX_COHORTS && region_age < MAX_COHORTS, "Should have been tenured"); +#ifdef SHENANDOAH_CENSUS_NOISE + // Region ageing is stochastic and non-monotonic; this vitiates mortality + // demographics in ways that might defeat our algorithms. Marking may be a + // time when we might be able to correct this, but we currently do not do + // this. Like skipped statistics further below, we want to track the + // impact of this noise to see if this may be worthwhile. JDK-. + uint age = obj_age; + if (region_age > 0) { + add_aged(size, worker_id); // this tracking is coarse for now + age += region_age; + if (age >= MAX_COHORTS) { + age = (uint)(MAX_COHORTS - 1); // clamp + add_clamped(size, worker_id); + } + } + if (region_youth > 0) { // track object volume with retrograde age + add_young(size, worker_id); + } +#else // SHENANDOAH_CENSUS_NOISE + uint age = MIN2(obj_age + region_age, (uint)(MAX_COHORTS - 1)); // clamp +#endif // SHENANDOAH_CENSUS_NOISE + get_local_age_table(worker_id)->add(age, size); + } else { + // update skipped statistics + CENSUS_NOISE(add_skipped(size, worker_id);) + } +} + +#ifdef SHENANDOAH_CENSUS_NOISE +void ShenandoahAgeCensus::add_skipped(size_t size, uint worker_id) { + _local_noise[worker_id].skipped += size; +} + +void ShenandoahAgeCensus::add_aged(size_t size, uint worker_id) { + _local_noise[worker_id].aged += size; +} + +void ShenandoahAgeCensus::add_clamped(size_t size, uint worker_id) { + _local_noise[worker_id].clamped += size; +} + +void ShenandoahAgeCensus::add_young(size_t size, uint worker_id) { + _local_noise[worker_id].young += size; +} +#endif // SHENANDOAH_CENSUS_NOISE + +// Prepare for a new census update, by clearing appropriate global slots. +void ShenandoahAgeCensus::prepare_for_census_update() { + assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); + if (++_epoch >= MAX_SNAPSHOTS) { + _epoch=0; + } + _global_age_table[_epoch]->clear(); + CENSUS_NOISE(_global_noise[_epoch].clear();) +} + +// Update the census data from appropriate sources, +// and compute the new tenuring threshold. +void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable* pv2) { + prepare_for_census_update(); + assert(_global_age_table[_epoch]->is_clear(), "Dirty decks"); + CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");) + if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) { + assert(pv1 == nullptr && pv2 == nullptr, "Error, check caller"); + // Seed cohort 0 with population that may have been missed during + // regular census. + _global_age_table[_epoch]->add((uint)0, age0_pop); + + size_t max_workers = ShenandoahHeap::heap()->max_workers(); + // Merge data from local age tables into the global age table for the epoch, + // clearing the local tables. + for (uint i = 0; i < max_workers; i++) { + // age stats + _global_age_table[_epoch]->merge(_local_age_table[i]); + _local_age_table[i]->clear(); // clear for next census + // Merge noise stats + CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);) + CENSUS_NOISE(_local_noise[i].clear();) + } + } else { + // census during evac + assert(pv1 != nullptr && pv2 != nullptr, "Error, check caller"); + _global_age_table[_epoch]->merge(pv1); + _global_age_table[_epoch]->merge(pv2); + } + + update_tenuring_threshold(); + + // used for checking reasonableness of census coverage, non-product + // only. + NOT_PRODUCT(update_total();) +} + + +// Reset the epoch for the global age tables, +// clearing all history. +void ShenandoahAgeCensus::reset_global() { + assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); + for (uint i = 0; i < MAX_SNAPSHOTS; i++) { + _global_age_table[i]->clear(); + CENSUS_NOISE(_global_noise[i].clear();) + } + _epoch = MAX_SNAPSHOTS; + assert(_epoch < MAX_SNAPSHOTS, "Error"); +} + +// Reset the local age tables, clearing any partial census. +void ShenandoahAgeCensus::reset_local() { + if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) { + assert(_local_age_table == nullptr, "Error"); + return; + } + size_t max_workers = ShenandoahHeap::heap()->max_workers(); + for (uint i = 0; i < max_workers; i++) { + _local_age_table[i]->clear(); + CENSUS_NOISE(_local_noise[i].clear();) + } +} + +#ifndef PRODUCT +// Is global census information clear? +bool ShenandoahAgeCensus::is_clear_global() { + assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); + for (uint i = 0; i < MAX_SNAPSHOTS; i++) { + bool clear = _global_age_table[i]->is_clear(); + CENSUS_NOISE(clear |= _global_noise[i].is_clear();) + if (!clear) { + return false; + } + } + return true; +} + +// Is local census information clear? +bool ShenandoahAgeCensus::is_clear_local() { + if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) { + assert(_local_age_table == nullptr, "Error"); + return true; + } + size_t max_workers = ShenandoahHeap::heap()->max_workers(); + for (uint i = 0; i < max_workers; i++) { + bool clear = _local_age_table[i]->is_clear(); + CENSUS_NOISE(clear |= _local_noise[i].is_clear();) + if (!clear) { + return false; + } + } + return true; +} + +size_t ShenandoahAgeCensus::get_all_ages(uint snap) { + assert(snap < MAX_SNAPSHOTS, "Out of bounds"); + size_t pop = 0; + const AgeTable* pv = _global_age_table[snap]; + for (uint i = 0; i < MAX_COHORTS; i++) { + pop += pv->sizes[i]; + } + return pop; +} + +size_t ShenandoahAgeCensus::get_skipped(uint snap) { + assert(snap < MAX_SNAPSHOTS, "Out of bounds"); + return _global_noise[snap].skipped; +} + +void ShenandoahAgeCensus::update_total() { + _counted = get_all_ages(_epoch); + _skipped = get_skipped(_epoch); + _total = _counted + _skipped; +} +#endif // !PRODUCT + +void ShenandoahAgeCensus::update_tenuring_threshold() { + if (!ShenandoahGenerationalAdaptiveTenuring) { + _tenuring_threshold[_epoch] = InitialTenuringThreshold; + } else { + uint tt = compute_tenuring_threshold(); + assert(tt <= MAX_COHORTS, "Out of bounds"); + _tenuring_threshold[_epoch] = tt; + } + print(); + log_trace(gc, age)("New tenuring threshold " UINTX_FORMAT " (min " UINTX_FORMAT ", max " UINTX_FORMAT")", + (uintx) _tenuring_threshold[_epoch], ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge); +} + +// Currently Shenandoah{Min,Max}TenuringAge have a floor of 1 because we +// aren't set up to promote age 0 objects. +uint ShenandoahAgeCensus::compute_tenuring_threshold() { + // Dispose of the extremal cases early so the loop below + // is less fragile. + if (ShenandoahGenerationalMaxTenuringAge == ShenandoahGenerationalMinTenuringAge) { + return ShenandoahGenerationalMaxTenuringAge; // Any value in [1,16] + } + assert(ShenandoahGenerationalMinTenuringAge < ShenandoahGenerationalMaxTenuringAge, "Error"); + + // Starting with the oldest cohort with a non-trivial population + // (as specified by ShenandoahGenerationalTenuringCohortPopulationThreshold) in the + // previous epoch, and working down the cohorts by age, find the + // oldest age that has a significant mortality rate (as specified by + // ShenandoahGenerationalTenuringMortalityRateThreshold). We use this as + // tenuring age to be used for the evacuation cycle to follow. + // Results are clamped between user-specified min & max guardrails, + // so we ignore any cohorts outside ShenandoahGenerational[Min,Max]Age. + + // Current and previous epoch in ring + const uint cur_epoch = _epoch; + const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1 : markWord::max_age; + + // Current and previous population vectors in ring + const AgeTable* cur_pv = _global_age_table[cur_epoch]; + const AgeTable* prev_pv = _global_age_table[prev_epoch]; + uint upper_bound = ShenandoahGenerationalMaxTenuringAge; + const uint prev_tt = previous_tenuring_threshold(); + if (ShenandoahGenerationalCensusIgnoreOlderCohorts && prev_tt > 0) { + // We stay below the computed tenuring threshold for the last cycle plus 1, + // ignoring the mortality rates of any older cohorts. + upper_bound = MIN2(upper_bound, prev_tt + 1); + } + upper_bound = MIN2(upper_bound, markWord::max_age); + + const uint lower_bound = MAX2((uint)ShenandoahGenerationalMinTenuringAge, (uint)1); + + uint tenuring_threshold = upper_bound; + for (uint i = upper_bound; i >= lower_bound; i--) { + assert(i > 0, "Index (i-1) would underflow/wrap"); + assert(i <= markWord::max_age, "Index i would overflow"); + // Cohort of current age i + const size_t cur_pop = cur_pv->sizes[i]; + const size_t prev_pop = prev_pv->sizes[i-1]; + const double mr = mortality_rate(prev_pop, cur_pop); + if (prev_pop > ShenandoahGenerationalTenuringCohortPopulationThreshold && + mr > ShenandoahGenerationalTenuringMortalityRateThreshold) { + // This is the oldest cohort that has high mortality. + // We ignore any cohorts that had a very low population count, or + // that have a lower mortality rate than we care to age in young; these + // cohorts are considered eligible for tenuring when all older + // cohorts are. We return the next higher age as the tenuring threshold + // so that we do not prematurely promote objects of this age. + assert(tenuring_threshold == i+1 || tenuring_threshold == upper_bound, "Error"); + assert(tenuring_threshold >= lower_bound && tenuring_threshold <= upper_bound, "Error"); + return tenuring_threshold; + } + // Remember that we passed over this cohort, looking for younger cohorts + // showing high mortality. We want to tenure cohorts of this age. + tenuring_threshold = i; + } + assert(tenuring_threshold >= lower_bound && tenuring_threshold <= upper_bound, "Error"); + return tenuring_threshold; +} + +// Mortality rate of a cohort, given its previous and current population +double ShenandoahAgeCensus::mortality_rate(size_t prev_pop, size_t cur_pop) { + // The following also covers the case where both entries are 0 + if (prev_pop <= cur_pop) { + // adjust for inaccurate censuses by finessing the + // reappearance of dark matter as normal matter; + // mortality rate is 0 if population remained the same + // or increased. + if (cur_pop > prev_pop) { + log_trace(gc, age) + (" (dark matter) Cohort population " SIZE_FORMAT_W(10) " to " SIZE_FORMAT_W(10), + prev_pop*oopSize, cur_pop*oopSize); + } + return 0.0; + } + assert(prev_pop > 0 && prev_pop > cur_pop, "Error"); + return 1.0 - (((double)cur_pop)/((double)prev_pop)); +} + +void ShenandoahAgeCensus::print() { + // Print the population vector for the current epoch, and + // for the previous epoch, as well as the computed mortality + // ratio for each extant cohort. + const uint cur_epoch = _epoch; + const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1: markWord::max_age; + + const AgeTable* cur_pv = _global_age_table[cur_epoch]; + const AgeTable* prev_pv = _global_age_table[prev_epoch]; + + const uint tt = tenuring_threshold(); + + size_t total= 0; + for (uint i = 1; i < MAX_COHORTS; i++) { + const size_t prev_pop = prev_pv->sizes[i-1]; // (i-1) OK because i >= 1 + const size_t cur_pop = cur_pv->sizes[i]; + double mr = mortality_rate(prev_pop, cur_pop); + // Suppress printing when everything is zero + if (prev_pop + cur_pop > 0) { + log_info(gc, age) + (" - age %3u: prev " SIZE_FORMAT_W(10) " bytes, curr " SIZE_FORMAT_W(10) " bytes, mortality %.2f ", + i, prev_pop*oopSize, cur_pop*oopSize, mr); + } + total += cur_pop; + if (i == tt) { + // Underline the cohort for tenuring threshold (if < MAX_COHORTS) + log_info(gc, age)("----------------------------------------------------------------------------"); + } + } + CENSUS_NOISE(_global_noise[cur_epoch].print(total);) +} + +#ifdef SHENANDOAH_CENSUS_NOISE +void ShenandoahNoiseStats::print(size_t total) { + if (total > 0) { + float f_skipped = (float)skipped/(float)total; + float f_aged = (float)aged/(float)total; + float f_clamped = (float)clamped/(float)total; + float f_young = (float)young/(float)total; + log_info(gc, age)("Skipped: " SIZE_FORMAT_W(10) " (%.2f), R-Aged: " SIZE_FORMAT_W(10) " (%.2f), " + "Clamped: " SIZE_FORMAT_W(10) " (%.2f), R-Young: " SIZE_FORMAT_W(10) " (%.2f)", + skipped*oopSize, f_skipped, aged*oopSize, f_aged, + clamped*oopSize, f_clamped, young*oopSize, f_young); + } +} +#endif // SHENANDOAH_CENSUS_NOISE diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp new file mode 100644 index 00000000000..89c68f7120b --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp @@ -0,0 +1,229 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP + +#include "gc/shared/ageTable.hpp" + +#ifndef PRODUCT +// Enable noise instrumentation +#define SHENANDOAH_CENSUS_NOISE 1 +#endif // PRODUCT + +#ifdef SHENANDOAH_CENSUS_NOISE + +#define CENSUS_NOISE(x) x +#define NO_CENSUS_NOISE(x) + +struct ShenandoahNoiseStats { + size_t skipped; // Volume of objects skipped + size_t aged; // Volume of objects from aged regions + size_t clamped; // Volume of objects whose ages were clamped + size_t young; // Volume of (rejuvenated) objects of retrograde age + + ShenandoahNoiseStats() { + clear(); + } + + void clear() { + skipped = 0; + aged = 0; + clamped = 0; + young = 0; + } + +#ifndef PRODUCT + bool is_clear() { + return (skipped + aged + clamped + young) == 0; + } +#endif // !PRODUCT + + void merge(ShenandoahNoiseStats& other) { + skipped += other.skipped; + aged += other.aged; + clamped += other.clamped; + young += other.young; + } + + void print(size_t total); +}; +#else // SHENANDOAH_CENSUS_NOISE +#define CENSUS_NOISE(x) +#define NO_CENSUS_NOISE(x) x +#endif // SHENANDOAH_CENSUS_NOISE + +// A class for tracking a sequence of cohort population vectors (or, +// interchangeably, age tables) for up to C=MAX_COHORTS age cohorts, where a cohort +// represents the set of objects allocated during a specific inter-GC epoch. +// Epochs are demarcated by GC cycles, with those surviving a cycle aging by +// an epoch. The census tracks the historical variation of cohort demographics +// across N=MAX_SNAPSHOTS recent epochs. Since there are at most C age cohorts in +// the population, we need only track at most N=C epochal snapshots to track a +// maximal longitudinal demographics of every object's longitudinal cohort in +// the young generation. The _global_age_table is thus, currently, a C x N (row-major) +// matrix, with C=16, and, for now N=C=16, currently. +// In theory, we might decide to track even longer (N=MAX_SNAPSHOTS) demographic +// histories, but that isn't the case today. In particular, the current tenuring +// threshold algorithm uses only 2 most recent snapshots, with the remaining +// MAX_SNAPSHOTS-2=14 reserved for research purposes. +// +// In addition, this class also maintains per worker population vectors into which +// census for the current minor GC is accumulated (during marking or, optionally, during +// evacuation). These are cleared after each marking (resectively, evacuation) cycle, +// once the per-worker data is consolidated into the appropriate population vector +// per minor collection. The _local_age_table is thus C x N, for N GC workers. +class ShenandoahAgeCensus: public CHeapObj { + AgeTable** _global_age_table; // Global age table used for adapting tenuring threshold, one per snapshot + AgeTable** _local_age_table; // Local scratch age tables to track object ages, one per worker + +#ifdef SHENANDOAH_CENSUS_NOISE + ShenandoahNoiseStats* _global_noise; // Noise stats, one per snapshot + ShenandoahNoiseStats* _local_noise; // Local scratch table for noise stats, one per worker + + size_t _skipped; // net size of objects encountered, but skipped during census, + // because their age was indeterminate +#endif // SHENANDOAH_CENSUS_NOISE + +#ifndef PRODUCT + size_t _counted; // net size of objects counted in census + size_t _total; // net size of objects encountered (counted or skipped) in census +#endif + + uint _epoch; // Current epoch (modulo max age) + uint *_tenuring_threshold; // An array of the last N tenuring threshold values we + // computed. + + // Mortality rate of a cohort, given its population in + // previous and current epochs + double mortality_rate(size_t prev_pop, size_t cur_pop); + + // Update to a new epoch, creating a slot for new census. + void prepare_for_census_update(); + + // Update the tenuring threshold, calling + // compute_tenuring_threshold() to calculate the new + // value + void update_tenuring_threshold(); + + // Use _global_age_table and the current _epoch to compute a new tenuring + // threshold, which will be remembered until the next invocation of + // compute_tenuring_threshold. + uint compute_tenuring_threshold(); + + // Return the tenuring threshold computed for the previous epoch + uint previous_tenuring_threshold() const { + assert(_epoch < MAX_SNAPSHOTS, "Error"); + uint prev = _epoch - 1; + if (prev >= MAX_SNAPSHOTS) { + // _epoch is 0 + assert(_epoch == 0, "Error"); + prev = MAX_SNAPSHOTS - 1; + } + return _tenuring_threshold[prev]; + } + +#ifndef PRODUCT + // Return the sum of size of objects of all ages recorded in the + // census at snapshot indexed by snap. + size_t get_all_ages(uint snap); + + // Return the size of all objects that were encountered, but skipped, + // during the census, because their age was indeterminate. + size_t get_skipped(uint snap); + + // Update the total size of objects counted or skipped at the census for + // the most recent epoch. + void update_total(); +#endif // !PRODUCT + + public: + enum { + MAX_COHORTS = AgeTable::table_size, // = markWord::max_age + 1 + MAX_SNAPSHOTS = MAX_COHORTS // May change in the future + }; + + ShenandoahAgeCensus(); + + // Return the local age table (population vector) for worker_id. + // Only used in the case of (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) + AgeTable* get_local_age_table(uint worker_id) { + return (AgeTable*) _local_age_table[worker_id]; + } + + // Update the local age table for worker_id by size for + // given obj_age, region_age, and region_youth + CENSUS_NOISE(void add(uint obj_age, uint region_age, uint region_youth, size_t size, uint worker_id);) + NO_CENSUS_NOISE(void add(uint obj_age, uint region_age, size_t size, uint worker_id);) + +#ifdef SHENANDOAH_CENSUS_NOISE + // Update the local skip table for worker_id by size + void add_skipped(size_t size, uint worker_id); + // Update the local aged region volume table for worker_id by size + void add_aged(size_t size, uint worker_id); + // Update the local clamped object volume table for worker_id by size + void add_clamped(size_t size, uint worker_id); + // Update the local (rejuvenated) object volume (retrograde age) for worker_id by size + void add_young(size_t size, uint worker_id); +#endif // SHENANDOAH_CENSUS_NOISE + + // Update the census data, and compute the new tenuring threshold. + // This method should be called at the end of each marking (or optionally + // evacuation) cycle to update the tenuring threshold to be used in + // the next cycle. + // age0_pop is the population of Cohort 0 that may have been missed in + // the regular census during the marking cycle, corresponding to objects + // allocated when the concurrent marking was in progress. + // Optional parameters, pv1 and pv2 are population vectors that together + // provide object census data (only) for the case when + // ShenandoahGenerationalCensusAtEvac. In this case, the age0_pop + // is 0, because the evacuated objects have all had their ages incremented. + void update_census(size_t age0_pop, AgeTable* pv1 = nullptr, AgeTable* pv2 = nullptr); + + // Return the most recently computed tenuring threshold + uint tenuring_threshold() const { return _tenuring_threshold[_epoch]; } + + // Reset the epoch, clearing accumulated census history + // Note: this isn't currently used, but reserved for planned + // future usage. + void reset_global(); + + // Reset any (potentially partial) census information in worker-local age tables + void reset_local(); + +#ifndef PRODUCT + // Check whether census information is clear + bool is_clear_global(); + bool is_clear_local(); + + // Return the net size of objects encountered (counted or skipped) in census + // at most recent epoch. + size_t get_total() { return _total; } +#endif // !PRODUCT + + // Print the age census information + void print(); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp index 59b4615d326..6ac62e8e9ed 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,15 +26,17 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHALLOCREQUEST_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHALLOCREQUEST_HPP +#include "gc/shenandoah/shenandoahAffiliation.hpp" #include "memory/allocation.hpp" class ShenandoahAllocRequest : StackObj { public: enum Type { _alloc_shared, // Allocate common, outside of TLAB - _alloc_shared_gc, // Allocate common, outside of GCLAB + _alloc_shared_gc, // Allocate common, outside of GCLAB/PLAB _alloc_tlab, // Allocate TLAB _alloc_gclab, // Allocate GCLAB + _alloc_plab, // Allocate PLAB _ALLOC_LIMIT }; @@ -47,6 +50,8 @@ class ShenandoahAllocRequest : StackObj { return "TLAB"; case _alloc_gclab: return "GCLAB"; + case _alloc_plab: + return "PLAB"; default: ShouldNotReachHere(); return ""; @@ -54,17 +59,38 @@ class ShenandoahAllocRequest : StackObj { } private: + // When ShenandoahElasticTLAB is enabled, the request cannot be made smaller than _min_size. size_t _min_size; + + // The size of the request in words. size_t _requested_size; + + // The allocation may be increased for padding or decreased to fit in the remaining space of a region. size_t _actual_size; + + // For a humongous object, the _waste is the amount of free memory in the last region. + // For other requests, the _waste will be non-zero if the request enountered one or more regions + // with less memory than _min_size. This waste does not contribute to the used memory for + // the heap, but it does contribute to the allocation rate for heuristics. + size_t _waste; + + // This is the type of the request. Type _alloc_type; + + // This is the generation which the request is targeting. + ShenandoahAffiliation const _affiliation; + + // True if this request is trying to copy any object from young to old (promote). + bool _is_promotion; + #ifdef ASSERT + // Check that this is set before being read. bool _actual_size_set; #endif - ShenandoahAllocRequest(size_t _min_size, size_t _requested_size, Type _alloc_type) : + ShenandoahAllocRequest(size_t _min_size, size_t _requested_size, Type _alloc_type, ShenandoahAffiliation affiliation, bool is_promotion = false) : _min_size(_min_size), _requested_size(_requested_size), - _actual_size(0), _alloc_type(_alloc_type) + _actual_size(0), _waste(0), _alloc_type(_alloc_type), _affiliation(affiliation), _is_promotion(is_promotion) #ifdef ASSERT , _actual_size_set(false) #endif @@ -72,39 +98,47 @@ class ShenandoahAllocRequest : StackObj { public: static inline ShenandoahAllocRequest for_tlab(size_t min_size, size_t requested_size) { - return ShenandoahAllocRequest(min_size, requested_size, _alloc_tlab); + return ShenandoahAllocRequest(min_size, requested_size, _alloc_tlab, ShenandoahAffiliation::YOUNG_GENERATION); } static inline ShenandoahAllocRequest for_gclab(size_t min_size, size_t requested_size) { - return ShenandoahAllocRequest(min_size, requested_size, _alloc_gclab); + return ShenandoahAllocRequest(min_size, requested_size, _alloc_gclab, ShenandoahAffiliation::YOUNG_GENERATION); + } + + static inline ShenandoahAllocRequest for_plab(size_t min_size, size_t requested_size) { + return ShenandoahAllocRequest(min_size, requested_size, _alloc_plab, ShenandoahAffiliation::OLD_GENERATION); } - static inline ShenandoahAllocRequest for_shared_gc(size_t requested_size) { - return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc); + static inline ShenandoahAllocRequest for_shared_gc(size_t requested_size, ShenandoahAffiliation affiliation, bool is_promotion = false) { + if (is_promotion) { + assert(affiliation == ShenandoahAffiliation::OLD_GENERATION, "Should only promote to old generation"); + return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc, affiliation, true); + } + return ShenandoahAllocRequest(0, requested_size, _alloc_shared_gc, affiliation); } static inline ShenandoahAllocRequest for_shared(size_t requested_size) { - return ShenandoahAllocRequest(0, requested_size, _alloc_shared); + return ShenandoahAllocRequest(0, requested_size, _alloc_shared, ShenandoahAffiliation::YOUNG_GENERATION); } - inline size_t size() { + inline size_t size() const { return _requested_size; } - inline Type type() { + inline Type type() const { return _alloc_type; } - inline const char* type_string() { + inline const char* type_string() const { return alloc_type_to_string(_alloc_type); } - inline size_t min_size() { + inline size_t min_size() const { assert (is_lab_alloc(), "Only access for LAB allocs"); return _min_size; } - inline size_t actual_size() { + inline size_t actual_size() const { assert (_actual_size_set, "Should be set"); return _actual_size; } @@ -117,12 +151,21 @@ class ShenandoahAllocRequest : StackObj { _actual_size = v; } - inline bool is_mutator_alloc() { + inline size_t waste() const { + return _waste; + } + + inline void set_waste(size_t v) { + _waste = v; + } + + inline bool is_mutator_alloc() const { switch (_alloc_type) { case _alloc_tlab: case _alloc_shared: return true; case _alloc_gclab: + case _alloc_plab: case _alloc_shared_gc: return false; default: @@ -131,12 +174,13 @@ class ShenandoahAllocRequest : StackObj { } } - inline bool is_gc_alloc() { + inline bool is_gc_alloc() const { switch (_alloc_type) { case _alloc_tlab: case _alloc_shared: return false; case _alloc_gclab: + case _alloc_plab: case _alloc_shared_gc: return true; default: @@ -145,10 +189,11 @@ class ShenandoahAllocRequest : StackObj { } } - inline bool is_lab_alloc() { + inline bool is_lab_alloc() const { switch (_alloc_type) { case _alloc_tlab: case _alloc_gclab: + case _alloc_plab: return true; case _alloc_shared: case _alloc_shared_gc: @@ -158,6 +203,26 @@ class ShenandoahAllocRequest : StackObj { return false; } } + + bool is_old() const { + return _affiliation == OLD_GENERATION; + } + + bool is_young() const { + return _affiliation == YOUNG_GENERATION; + } + + ShenandoahAffiliation affiliation() const { + return _affiliation; + } + + const char* affiliation_name() const { + return shenandoah_affiliation_name(_affiliation); + } + + bool is_promotion() const { + return _is_promotion; + } }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHALLOCREQUEST_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index 4376e9d1150..fa3f9019af4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -29,6 +30,7 @@ #include "gc/shared/workerPolicy.hpp" #include "gc/shenandoah/shenandoahArguments.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "runtime/globals_extension.hpp" @@ -50,6 +52,7 @@ void ShenandoahArguments::initialize() { FLAG_SET_DEFAULT(ShenandoahSATBBarrier, false); FLAG_SET_DEFAULT(ShenandoahLoadRefBarrier, false); FLAG_SET_DEFAULT(ShenandoahCASBarrier, false); + FLAG_SET_DEFAULT(ShenandoahCardBarrier, false); FLAG_SET_DEFAULT(ShenandoahCloneBarrier, false); FLAG_SET_DEFAULT(ShenandoahVerifyOptoBarriers, false); @@ -69,6 +72,13 @@ void ShenandoahArguments::initialize() { FLAG_SET_DEFAULT(UseNUMA, true); } + // We use this as the time period for tracking minimum mutator utilization (MMU). + // In generational mode, the MMU is used as a signal to adjust the size of the + // young generation. + if (FLAG_IS_DEFAULT(GCPauseIntervalMillis)) { + FLAG_SET_DEFAULT(GCPauseIntervalMillis, 5000); + } + // Set up default number of concurrent threads. We want to have cycles complete fast // enough, but we also do not want to steal too much CPU from the concurrently running // application. Using 1/4 of available threads for concurrent GC seems a good @@ -189,6 +199,8 @@ size_t ShenandoahArguments::conservative_max_heap_alignment() { } void ShenandoahArguments::initialize_alignments() { + CardTable::initialize_card_size(); + // Need to setup sizes early to get correct alignments. MaxHeapSize = ShenandoahHeapRegion::setup_sizes(MaxHeapSize); @@ -202,5 +214,10 @@ void ShenandoahArguments::initialize_alignments() { } CollectedHeap* ShenandoahArguments::create_heap() { - return new ShenandoahHeap(new ShenandoahCollectorPolicy()); + if (strcmp(ShenandoahGCMode, "generational") != 0) { + // Not generational + return new ShenandoahHeap(new ShenandoahCollectorPolicy()); + } else { + return new ShenandoahGenerationalHeap(new ShenandoahCollectorPolicy()); + } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index 22822ad3fec..437646becce 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -71,6 +71,9 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) { msg.append(" %3s marked strong\n", ctx->is_marked_strong(obj) ? "" : "not"); msg.append(" %3s marked weak\n", ctx->is_marked_weak(obj) ? "" : "not"); msg.append(" %3s in collection set\n", heap->in_collection_set(obj) ? "" : "not"); + if (heap->mode()->is_generational() && !obj->is_forwarded()) { + msg.append(" age: %d\n", obj->age()); + } msg.append(" mark:%s\n", mw_ss.freeze()); msg.append(" region: %s", ss.freeze()); } @@ -424,7 +427,7 @@ void ShenandoahAsserts::assert_locked_or_shenandoah_safepoint(Mutex* lock, const return; } - ShenandoahMessageBuffer msg("Must ba at a Shenandoah safepoint or held %s lock", lock->name()); + ShenandoahMessageBuffer msg("Must be at a Shenandoah safepoint or held %s lock", lock->name()); report_vm_error(file, line, msg.buffer()); } @@ -457,10 +460,55 @@ void ShenandoahAsserts::assert_heaplocked_or_safepoint(const char* file, int lin return; } - if (ShenandoahSafepoint::is_at_shenandoah_safepoint() && Thread::current()->is_VM_thread()) { + if (ShenandoahSafepoint::is_at_shenandoah_safepoint()) { return; } ShenandoahMessageBuffer msg("Heap lock must be owned by current thread, or be at safepoint"); report_vm_error(file, line, msg.buffer()); } + +void ShenandoahAsserts::assert_generational(const char* file, int line) { + if (ShenandoahHeap::heap()->mode()->is_generational()) { + return; + } + + ShenandoahMessageBuffer msg("Must be in generational mode"); + report_vm_error(file, line, msg.buffer()); +} + +void ShenandoahAsserts::assert_control_or_vm_thread_at_safepoint(bool at_safepoint, const char* file, int line) { + Thread* thr = Thread::current(); + if (thr == ShenandoahHeap::heap()->control_thread()) { + return; + } + if (thr->is_VM_thread()) { + if (!at_safepoint) { + return; + } else if (SafepointSynchronize::is_at_safepoint()) { + return; + } + } + + ShenandoahMessageBuffer msg("Must be either control thread, or vm thread"); + if (at_safepoint) { + msg.append(" at a safepoint"); + } + report_vm_error(file, line, msg.buffer()); +} + +void ShenandoahAsserts::assert_generations_reconciled(const char* file, int line) { + if (!SafepointSynchronize::is_at_safepoint()) { + return; + } + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahGeneration* ggen = heap->gc_generation(); + ShenandoahGeneration* agen = heap->active_generation(); + if (agen == ggen) { + return; + } + + ShenandoahMessageBuffer msg("Active(%d) & GC(%d) Generations aren't reconciled", agen->type(), ggen->type()); + report_vm_error(file, line, msg.buffer()); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp index 154edebcf3e..31a99bf438c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -72,6 +73,9 @@ class ShenandoahAsserts { static void assert_heaplocked(const char* file, int line); static void assert_not_heaplocked(const char* file, int line); static void assert_heaplocked_or_safepoint(const char* file, int line); + static void assert_control_or_vm_thread_at_safepoint(bool at_safepoint, const char* file, int line); + static void assert_generational(const char* file, int line); + static void assert_generations_reconciled(const char* file, int line); #ifdef ASSERT #define shenandoah_assert_in_heap_bounds(interior_loc, obj) \ @@ -163,6 +167,21 @@ class ShenandoahAsserts { #define shenandoah_assert_heaplocked_or_safepoint() \ ShenandoahAsserts::assert_heaplocked_or_safepoint(__FILE__, __LINE__) + +#define shenandoah_assert_control_or_vm_thread() \ + ShenandoahAsserts::assert_control_or_vm_thread(false /* at_safepoint */, __FILE__, __LINE__) + +// A stronger version of the above that checks that we are at a safepoint if the vm thread +#define shenandoah_assert_control_or_vm_thread_at_safepoint() \ + ShenandoahAsserts::assert_control_or_vm_thread_at_safepoint(true /* at_safepoint */, __FILE__, __LINE__) + +#define shenandoah_assert_generational() \ + ShenandoahAsserts::assert_generational(__FILE__, __LINE__) + +// Some limited sanity checking of the _gc_generation and _active_generation fields of ShenandoahHeap +#define shenandoah_assert_generations_reconciled() \ + ShenandoahAsserts::assert_generations_reconciled(__FILE__, __LINE__) + #else #define shenandoah_assert_in_heap_bounds(interior_loc, obj) #define shenandoah_assert_in_heap_bounds_or_null(interior_loc, obj) @@ -213,6 +232,10 @@ class ShenandoahAsserts { #define shenandoah_assert_heaplocked() #define shenandoah_assert_not_heaplocked() #define shenandoah_assert_heaplocked_or_safepoint() +#define shenandoah_assert_control_or_vm_thread() +#define shenandoah_assert_control_or_vm_thread_at_safepoint() +#define shenandoah_assert_generational() +#define shenandoah_assert_generations_reconciled() #endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 40b6d70ce45..38e363664dc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -28,8 +29,10 @@ #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahBarrierSetNMethod.hpp" #include "gc/shenandoah/shenandoahBarrierSetStackChunk.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahStackWatermark.hpp" #ifdef COMPILER1 #include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" @@ -41,7 +44,7 @@ class ShenandoahBarrierSetC1; class ShenandoahBarrierSetC2; -ShenandoahBarrierSet::ShenandoahBarrierSet(ShenandoahHeap* heap) : +ShenandoahBarrierSet::ShenandoahBarrierSet(ShenandoahHeap* heap, MemRegion heap_region) : BarrierSet(make_barrier_set_assembler(), make_barrier_set_c1(), make_barrier_set_c2(), @@ -49,9 +52,14 @@ ShenandoahBarrierSet::ShenandoahBarrierSet(ShenandoahHeap* heap) : new ShenandoahBarrierSetStackChunk(), BarrierSet::FakeRtti(BarrierSet::ShenandoahBarrierSet)), _heap(heap), + _card_table(nullptr), _satb_mark_queue_buffer_allocator("SATB Buffer Allocator", ShenandoahSATBBufferSize), _satb_mark_queue_set(&_satb_mark_queue_buffer_allocator) { + if (ShenandoahCardBarrier) { + _card_table = new ShenandoahCardTable(heap_region); + _card_table->initialize(); + } } ShenandoahBarrierSetAssembler* ShenandoahBarrierSet::assembler() { @@ -124,6 +132,12 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { gclab->retire(); } + PLAB* plab = ShenandoahThreadLocalData::plab(thread); + if (plab != nullptr) { + // This will assert if plab is not null in non-generational mode + ShenandoahGenerationalHeap::heap()->retire_plab(plab); + } + // SATB protocol requires to keep alive reachable oops from roots at the beginning of GC if (ShenandoahStackWatermarkBarrier) { if (_heap->is_concurrent_mark_in_progress()) { @@ -142,3 +156,22 @@ void ShenandoahBarrierSet::clone_barrier_runtime(oop src) { clone_barrier(src); } } + +void ShenandoahBarrierSet::write_ref_array(HeapWord* start, size_t count) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + + HeapWord* end = (HeapWord*)((char*) start + (count * heapOopSize)); + // In the case of compressed oops, start and end may potentially be misaligned; + // so we need to conservatively align the first downward (this is not + // strictly necessary for current uses, but a case of good hygiene and, + // if you will, aesthetics) and the second upward (this is essential for + // current uses) to a HeapWord boundary, so we mark all cards overlapping + // this write. + HeapWord* aligned_start = align_down(start, HeapWordSize); + HeapWord* aligned_end = align_up (end, HeapWordSize); + // If compressed oops were not being used, these should already be aligned + assert(UseCompressedOops || (aligned_start == start && aligned_end == end), + "Expected heap word alignment of start and end"); + _heap->old_generation()->card_scan()->mark_range_as_dirty(aligned_start, (aligned_end - aligned_start)); +} + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index e116e0225db..8d1dc92761a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -31,15 +32,17 @@ class ShenandoahHeap; class ShenandoahBarrierSetAssembler; +class ShenandoahCardTable; class ShenandoahBarrierSet: public BarrierSet { private: ShenandoahHeap* const _heap; + ShenandoahCardTable* _card_table; BufferNode::Allocator _satb_mark_queue_buffer_allocator; ShenandoahSATBMarkQueueSet _satb_mark_queue_set; public: - ShenandoahBarrierSet(ShenandoahHeap* heap); + ShenandoahBarrierSet(ShenandoahHeap* heap, MemRegion heap_region); static ShenandoahBarrierSetAssembler* assembler(); @@ -47,6 +50,10 @@ class ShenandoahBarrierSet: public BarrierSet { return barrier_set_cast(BarrierSet::barrier_set()); } + inline ShenandoahCardTable* card_table() { + return _card_table; + } + static ShenandoahSATBMarkQueueSet& satb_mark_queue_set() { return barrier_set()->_satb_mark_queue_set; } @@ -111,9 +118,14 @@ class ShenandoahBarrierSet: public BarrierSet { template inline oop oop_xchg(DecoratorSet decorators, T* addr, oop new_value); + template + void write_ref_field_post(T* field); + + void write_ref_array(HeapWord* start, size_t count); + private: template - inline void arraycopy_marking(T* src, T* dst, size_t count); + inline void arraycopy_marking(T* src, T* dst, size_t count, bool is_old_marking); template inline void arraycopy_evacuation(T* src, size_t count); template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 80735f453c6..f3ce9418d35 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -28,14 +29,19 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shared/accessBarrierSupport.inline.hpp" +#include "gc/shared/cardTable.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahCollectionSet.inline.hpp" #include "gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp" #include "gc/shenandoah/shenandoahForwarding.inline.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "memory/iterator.inline.hpp" #include "oops/oop.inline.hpp" inline oop ShenandoahBarrierSet::resolve_forwarded_not_null(oop p) { @@ -103,6 +109,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(DecoratorSet decorators, // Prevent resurrection of unreachable phantom (i.e. weak-native) references. if ((decorators & ON_PHANTOM_OOP_REF) != 0 && _heap->is_concurrent_weak_root_in_progress() && + _heap->is_in_active_generation(obj) && !_heap->marking_context()->is_marked(obj)) { return nullptr; } @@ -110,6 +117,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(DecoratorSet decorators, // Prevent resurrection of unreachable weak references. if ((decorators & ON_WEAK_OOP_REF) != 0 && _heap->is_concurrent_weak_root_in_progress() && + _heap->is_in_active_generation(obj) && !_heap->marking_context()->is_marked_strong(obj)) { return nullptr; } @@ -173,6 +181,13 @@ inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oo } } +template +inline void ShenandoahBarrierSet::write_ref_field_post(T* field) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); + volatile CardTable::CardValue* byte = card_table()->byte_for(field); + *byte = CardTable::dirty_card_val(); +} + template inline oop ShenandoahBarrierSet::oop_load(DecoratorSet decorators, T* addr) { oop value = RawAccess<>::oop_load(addr); @@ -234,7 +249,10 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_loa template template inline void ShenandoahBarrierSet::AccessBarrier::oop_store_common(T* addr, oop value) { - shenandoah_assert_marked_if(nullptr, value, !CompressedOops::is_null(value) && ShenandoahHeap::heap()->is_evacuation_in_progress()); + shenandoah_assert_marked_if(nullptr, value, + !CompressedOops::is_null(value) && ShenandoahHeap::heap()->is_evacuation_in_progress() + && !(ShenandoahHeap::heap()->active_generation()->is_young() + && ShenandoahHeap::heap()->heap_region_containing(value)->is_old())); shenandoah_assert_not_in_cset_if(addr, value, value != nullptr && !ShenandoahHeap::heap()->cancelled_gc()); ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); bs->satb_barrier(addr); @@ -254,6 +272,10 @@ inline void ShenandoahBarrierSet::AccessBarrier::oop_st shenandoah_assert_not_forwarded_except (addr, value, value == nullptr || ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahHeap::heap()->is_concurrent_mark_in_progress()); oop_store_common(addr, value); + if (ShenandoahCardBarrier) { + ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); + bs->write_ref_field_post(addr); + } } template @@ -274,7 +296,11 @@ template inline oop ShenandoahBarrierSet::AccessBarrier::oop_atomic_cmpxchg_in_heap(T* addr, oop compare_value, oop new_value) { assert((decorators & (AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF)) == 0, "must be absent"); ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - return bs->oop_cmpxchg(decorators, addr, compare_value, new_value); + oop result = bs->oop_cmpxchg(decorators, addr, compare_value, new_value); + if (ShenandoahCardBarrier) { + bs->write_ref_field_post(addr); + } + return result; } template @@ -282,7 +308,12 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato assert((decorators & AS_NO_KEEPALIVE) == 0, "must be absent"); ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset); - return bs->oop_cmpxchg(resolved_decorators, AccessInternal::oop_field_addr(base, offset), compare_value, new_value); + auto addr = AccessInternal::oop_field_addr(base, offset); + oop result = bs->oop_cmpxchg(resolved_decorators, addr, compare_value, new_value); + if (ShenandoahCardBarrier) { + bs->write_ref_field_post(addr); + } + return result; } template @@ -298,7 +329,11 @@ template inline oop ShenandoahBarrierSet::AccessBarrier::oop_atomic_xchg_in_heap(T* addr, oop new_value) { assert((decorators & (AS_NO_KEEPALIVE | ON_UNKNOWN_OOP_REF)) == 0, "must be absent"); ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - return bs->oop_xchg(decorators, addr, new_value); + oop result = bs->oop_xchg(decorators, addr, new_value); + if (ShenandoahCardBarrier) { + bs->write_ref_field_post(addr); + } + return result; } template @@ -306,7 +341,12 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato assert((decorators & AS_NO_KEEPALIVE) == 0, "must be absent"); ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset); - return bs->oop_xchg(resolved_decorators, AccessInternal::oop_field_addr(base, offset), new_value); + auto addr = AccessInternal::oop_field_addr(base, offset); + oop result = bs->oop_xchg(resolved_decorators, addr, new_value); + if (ShenandoahCardBarrier) { + bs->write_ref_field_post(addr); + } + return result; } // Clone barrier support @@ -323,16 +363,29 @@ template bool ShenandoahBarrierSet::AccessBarrier::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw, arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw, size_t length) { + T* src = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw); + T* dst = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw); + ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - bs->arraycopy_barrier(arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw), - arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw), - length); - return Raw::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length); + bs->arraycopy_barrier(src, dst, length); + bool result = Raw::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length); + if (ShenandoahCardBarrier) { + bs->write_ref_array((HeapWord*) dst, length); + } + return result; } template void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { - assert(HAS_FWD == _heap->has_forwarded_objects(), "Forwarded object status is sane"); + // Young cycles are allowed to run when old marking is in progress. When old marking is in progress, + // this barrier will be called with ENQUEUE=true and HAS_FWD=false, even though the young generation + // may have forwarded objects. In this case, the `arraycopy_work` is first called with HAS_FWD=true and + // ENQUEUE=false. + assert(HAS_FWD == _heap->has_forwarded_objects() || (_heap->gc_state() & ShenandoahHeap::OLD_MARKING) != 0, + "Forwarded object status is sane"); + // This function cannot be called to handle marking and evacuation at the same time (they operate on + // different sides of the copy). + assert((HAS_FWD || EVAC) != ENQUEUE, "Cannot evacuate and mark both sides of copy."); Thread* thread = Thread::current(); SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); @@ -350,9 +403,8 @@ void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { } shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc()); ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); - obj = fwd; } - if (ENQUEUE && !ctx->is_marked_strong(obj)) { + if (ENQUEUE && !ctx->is_marked_strong_or_old(obj)) { _satb_mark_queue_set.enqueue_known_active(queue, obj); } } @@ -362,24 +414,73 @@ void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { template void ShenandoahBarrierSet::arraycopy_barrier(T* src, T* dst, size_t count) { if (count == 0) { + // No elements to copy, no need for barrier return; } + int gc_state = _heap->gc_state(); - if ((gc_state & ShenandoahHeap::MARKING) != 0) { - arraycopy_marking(src, dst, count); - } else if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { + if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { arraycopy_evacuation(src, count); } else if ((gc_state & ShenandoahHeap::UPDATEREFS) != 0) { arraycopy_update(src, count); } + + if (_heap->mode()->is_generational()) { + assert(ShenandoahSATBBarrier, "Generational mode assumes SATB mode"); + if ((gc_state & ShenandoahHeap::YOUNG_MARKING) != 0) { + arraycopy_marking(src, dst, count, false); + } + if ((gc_state & ShenandoahHeap::OLD_MARKING) != 0) { + arraycopy_marking(src, dst, count, true); + } + } else if ((gc_state & ShenandoahHeap::MARKING) != 0) { + arraycopy_marking(src, dst, count, false); + } } template -void ShenandoahBarrierSet::arraycopy_marking(T* src, T* dst, size_t count) { +void ShenandoahBarrierSet::arraycopy_marking(T* src, T* dst, size_t count, bool is_old_marking) { assert(_heap->is_concurrent_mark_in_progress(), "only during marking"); - T* array = ShenandoahSATBBarrier ? dst : src; - if (!_heap->marking_context()->allocated_after_mark_start(reinterpret_cast(array))) { - arraycopy_work(array, count); + /* + * Note that an old-gen object is considered live if it is live at the start of OLD marking or if it is promoted + * following the start of OLD marking. + * + * 1. Every object promoted following the start of OLD marking will be above TAMS within its old-gen region + * 2. Every object live at the start of OLD marking will be referenced from a "root" or it will be referenced from + * another live OLD-gen object. With regards to old-gen, roots include stack locations and all of live young-gen. + * All root references to old-gen are identified during a bootstrap young collection. All references from other + * old-gen objects will be marked during the traversal of all old objects, or will be marked by the SATB barrier. + * + * During old-gen marking (which is interleaved with young-gen collections), call arraycopy_work() if: + * + * 1. The overwritten array resides in old-gen and it is below TAMS within its old-gen region + * 2. Do not call arraycopy_work for any array residing in young-gen because young-gen collection is idle at this time + * + * During young-gen marking, call arraycopy_work() if: + * + * 1. The overwritten array resides in young-gen and is below TAMS within its young-gen region + * 2. Additionally, if array resides in old-gen, regardless of its relationship to TAMS because this old-gen array + * may hold references to young-gen + */ + if (ShenandoahSATBBarrier) { + T* array = dst; + HeapWord* array_addr = reinterpret_cast(array); + ShenandoahHeapRegion* r = _heap->heap_region_containing(array_addr); + if (is_old_marking) { + // Generational, old marking + assert(_heap->mode()->is_generational(), "Invariant"); + if (r->is_old() && (array_addr < _heap->marking_context()->top_at_mark_start(r))) { + arraycopy_work(array, count); + } + } else if (_heap->mode()->is_generational()) { + // Generational, young marking + if (r->is_old() || (array_addr < _heap->marking_context()->top_at_mark_start(r))) { + arraycopy_work(array, count); + } + } else if (array_addr < _heap->marking_context()->top_at_mark_start(r)) { + // Non-generational, marking + arraycopy_work(array, count); + } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCardStats.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCardStats.cpp new file mode 100644 index 00000000000..ef2d6e134b2 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahCardStats.cpp @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahCardStats.hpp" +#include "logging/log.hpp" + +#ifndef PRODUCT +void ShenandoahCardStats::log() const { + if (ShenandoahEnableCardStats) { + log_info(gc,remset)("Card stats: dirty " SIZE_FORMAT " (max run: " SIZE_FORMAT ")," + " clean " SIZE_FORMAT " (max run: " SIZE_FORMAT ")," + " dirty scans/objs " SIZE_FORMAT, + _dirty_card_cnt, _max_dirty_run, + _clean_card_cnt, _max_clean_run, + _dirty_scan_obj_cnt); + } +} +#endif // !PRODUCT + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCardStats.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCardStats.hpp new file mode 100644 index 00000000000..0ef0eaadb6b --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahCardStats.hpp @@ -0,0 +1,132 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCARDSTATS_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHCARDSTATS_HPP + +#include "gc/shared/gc_globals.hpp" +#include "gc/shenandoah/shenandoahNumberSeq.hpp" + +enum CardStatType { + DIRTY_RUN, + CLEAN_RUN, + DIRTY_CARDS, + CLEAN_CARDS, + MAX_DIRTY_RUN, + MAX_CLEAN_RUN, + DIRTY_SCAN_OBJS, + ALTERNATIONS, + MAX_CARD_STAT_TYPE +}; + +enum CardStatLogType { + CARD_STAT_SCAN_RS, + CARD_STAT_UPDATE_REFS, + MAX_CARD_STAT_LOG_TYPE +}; + +class ShenandoahCardStats: public CHeapObj { +private: + size_t _cards_in_cluster; + HdrSeq* _local_card_stats; + + size_t _dirty_card_cnt; + size_t _clean_card_cnt; + + size_t _max_dirty_run; + size_t _max_clean_run; + + size_t _dirty_scan_obj_cnt; + + size_t _alternation_cnt; + +public: + ShenandoahCardStats(size_t cards_in_cluster, HdrSeq* card_stats) : + _cards_in_cluster(cards_in_cluster), + _local_card_stats(card_stats), + _dirty_card_cnt(0), + _clean_card_cnt(0), + _max_dirty_run(0), + _max_clean_run(0), + _dirty_scan_obj_cnt(0), + _alternation_cnt(0) + { } + + ~ShenandoahCardStats() { + record(); + } + + void record() { + if (ShenandoahEnableCardStats) { + // Update global stats for distribution of dirty/clean cards as a percentage of chunk + _local_card_stats[DIRTY_CARDS].add(percent_of(_dirty_card_cnt, _cards_in_cluster)); + _local_card_stats[CLEAN_CARDS].add(percent_of(_clean_card_cnt, _cards_in_cluster)); + + // Update global stats for max dirty/clean run distribution as a percentage of chunk + _local_card_stats[MAX_DIRTY_RUN].add(percent_of(_max_dirty_run, _cards_in_cluster)); + _local_card_stats[MAX_CLEAN_RUN].add(percent_of(_max_clean_run, _cards_in_cluster)); + + // Update global stats for dirty obj scan counts + _local_card_stats[DIRTY_SCAN_OBJS].add(_dirty_scan_obj_cnt); + + // Update global stats for alternation counts + _local_card_stats[ALTERNATIONS].add(_alternation_cnt); + } + } + +public: + inline void record_dirty_run(size_t len) { + if (ShenandoahEnableCardStats) { + _alternation_cnt++; + if (len > _max_dirty_run) { + _max_dirty_run = len; + } + _dirty_card_cnt += len; + assert(len <= _cards_in_cluster, "Error"); + _local_card_stats[DIRTY_RUN].add(percent_of(len, _cards_in_cluster)); + } + } + + inline void record_clean_run(size_t len) { + if (ShenandoahEnableCardStats) { + _alternation_cnt++; + if (len > _max_clean_run) { + _max_clean_run = len; + } + _clean_card_cnt += len; + assert(len <= _cards_in_cluster, "Error"); + _local_card_stats[CLEAN_RUN].add(percent_of(len, _cards_in_cluster)); + } + } + + inline void record_scan_obj_cnt(size_t i) { + if (ShenandoahEnableCardStats) { + _dirty_scan_obj_cnt += i; + } + } + + void log() const PRODUCT_RETURN; +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCARDSTATS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp new file mode 100644 index 00000000000..6ecb93717df --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com Inc. 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" +#include "gc/shenandoah/shenandoahCardTable.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "runtime/init.hpp" +#include "nmt/memTracker.hpp" + +void ShenandoahCardTable::initialize() { + size_t num_cards = cards_required(_whole_heap.word_size()); + + // each card takes 1 byte; + 1 for the guard card + size_t num_bytes = num_cards + 1; + const size_t granularity = os::vm_allocation_granularity(); + _byte_map_size = align_up(num_bytes, MAX2(_page_size, granularity)); + + HeapWord* low_bound = _whole_heap.start(); + HeapWord* high_bound = _whole_heap.end(); + + // ReservedSpace constructor would assert rs_align >= os::vm_page_size(). + const size_t rs_align = _page_size == os::vm_page_size() ? 0 : MAX2(_page_size, granularity); + + ReservedSpace write_space(_byte_map_size, rs_align, _page_size); + initialize(write_space); + + // The assembler store_check code will do an unsigned shift of the oop, + // then add it to _byte_map_base, i.e. + // + // _byte_map = _byte_map_base + (uintptr_t(low_bound) >> card_shift) + _byte_map = (CardValue*) write_space.base(); + _byte_map_base = _byte_map - (uintptr_t(low_bound) >> _card_shift); + assert(byte_for(low_bound) == &_byte_map[0], "Checking start of map"); + assert(byte_for(high_bound-1) <= &_byte_map[last_valid_index()], "Checking end of map"); + + _write_byte_map = _byte_map; + _write_byte_map_base = _byte_map_base; + + ReservedSpace read_space(_byte_map_size, rs_align, _page_size); + initialize(read_space); + + _read_byte_map = (CardValue*) read_space.base(); + _read_byte_map_base = _read_byte_map - (uintptr_t(low_bound) >> card_shift()); + assert(read_byte_for(low_bound) == &_read_byte_map[0], "Checking start of map"); + assert(read_byte_for(high_bound-1) <= &_read_byte_map[last_valid_index()], "Checking end of map"); + + _covered[0] = _whole_heap; + + log_trace(gc, barrier)("ShenandoahCardTable::ShenandoahCardTable:"); + log_trace(gc, barrier)(" &_write_byte_map[0]: " INTPTR_FORMAT " &_write_byte_map[_last_valid_index]: " INTPTR_FORMAT, + p2i(&_write_byte_map[0]), p2i(&_write_byte_map[last_valid_index()])); + log_trace(gc, barrier)(" _write_byte_map_base: " INTPTR_FORMAT, p2i(_write_byte_map_base)); + log_trace(gc, barrier)(" &_read_byte_map[0]: " INTPTR_FORMAT " &_read_byte_map[_last_valid_index]: " INTPTR_FORMAT, + p2i(&_read_byte_map[0]), p2i(&_read_byte_map[last_valid_index()])); + log_trace(gc, barrier)(" _read_byte_map_base: " INTPTR_FORMAT, p2i(_read_byte_map_base)); +} + +void ShenandoahCardTable::initialize(const ReservedSpace& card_table) { + MemTracker::record_virtual_memory_tag((address)card_table.base(), mtGC); + + os::trace_page_sizes("Card Table", _byte_map_size, _byte_map_size, + card_table.base(), card_table.size(), _page_size); + if (!card_table.is_reserved()) { + vm_exit_during_initialization("Could not reserve enough space for the card marking array"); + } + os::commit_memory_or_exit(card_table.base(), _byte_map_size, card_table.alignment(), false, + "Cannot commit memory for card table"); +} + +bool ShenandoahCardTable::is_in_young(const void* obj) const { + return ShenandoahHeap::heap()->is_in_young(obj); +} + +CardValue* ShenandoahCardTable::read_byte_for(const void* p) { + CardValue* result = &_read_byte_map_base[uintptr_t(p) >> _card_shift]; + assert(result >= _read_byte_map && result < _read_byte_map + _byte_map_size, + "out of bounds accessor for card marking array"); + return result; +} + +size_t ShenandoahCardTable::last_valid_index() { + return CardTable::last_valid_index(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCardTable.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.hpp new file mode 100644 index 00000000000..f30ce09668a --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.hpp @@ -0,0 +1,87 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCARDTABLE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHCARDTABLE_HPP + +#include "gc/shared/cardTable.hpp" +#include "memory/virtualspace.hpp" +#include "oops/oopsHierarchy.hpp" +#include "utilities/macros.hpp" + +class ShenandoahCardTable: public CardTable { + friend class VMStructs; + +private: + // We maintain two copies of the card table to facilitate concurrent remembered set scanning + // and concurrent clearing of stale remembered set information. During the init_mark safepoint, + // we copy the contents of _write_byte_map to _read_byte_map and clear _write_byte_map. + // + // Concurrent remembered set scanning reads from _read_byte_map while concurrent mutator write + // barriers are overwriting cards of the _write_byte_map with DIRTY codes. Concurrent remembered + // set scanning also overwrites cards of the _write_byte_map with DIRTY codes whenever it discovers + // interesting pointers. + // + // During a concurrent update-references phase, we scan the _write_byte_map concurrently to find + // all old-gen references that may need to be updated. + // + // In a future implementation, we may swap the values of _read_byte_map and _write_byte_map during + // the init-mark safepoint to avoid the need for bulk STW copying and initialization. Doing so + // requires a change to the implementation of mutator write barriers as the address of the card + // table is currently in-lined and hard-coded. + CardValue* _read_byte_map; + CardValue* _write_byte_map; + CardValue* _read_byte_map_base; + CardValue* _write_byte_map_base; + +public: + explicit ShenandoahCardTable(MemRegion whole_heap) : CardTable(whole_heap), + _read_byte_map(nullptr), _write_byte_map(nullptr), + _read_byte_map_base(nullptr), _write_byte_map_base(nullptr) {} + + void initialize(); + + bool is_in_young(const void* obj) const override; + + CardValue* read_byte_for(const void* p); + + size_t last_valid_index(); + + CardValue* read_byte_map() { + return _read_byte_map; + } + + CardValue* write_byte_map() { + return _write_byte_map; + } + + CardValue* write_byte_map_base() { + return _write_byte_map_base; + } + +private: + void initialize(const ReservedSpace& card_table); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCARDTABLE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 58527e808e4..fa7887145df 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -58,6 +58,7 @@ class ShenandoahSuperClosure : public MetadataVisitingOopIterateClosure { class ShenandoahMarkRefsSuperClosure : public ShenandoahSuperClosure { private: ShenandoahObjToScanQueue* _queue; + ShenandoahObjToScanQueue* _old_queue; ShenandoahMarkingContext* const _mark_context; bool _weak; @@ -66,7 +67,7 @@ class ShenandoahMarkRefsSuperClosure : public ShenandoahSuperClosure { void work(T *p); public: - inline ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp); + inline ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q); bool is_weak() const { return _weak; @@ -89,8 +90,8 @@ class ShenandoahMarkRefsClosure : public ShenandoahMarkRefsSuperClosure { inline void do_oop_work(T* p) { work(p); } public: - ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : - ShenandoahMarkRefsSuperClosure(q, rp) {}; + ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q) : + ShenandoahMarkRefsSuperClosure(q, rp, old_q) {}; virtual void do_oop(narrowOop* p) { do_oop_work(p); } virtual void do_oop(oop* p) { do_oop_work(p); } @@ -191,7 +192,7 @@ class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkRefsSuperClosure { inline void work(T* p); public: - ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp); + ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q); virtual void do_oop(narrowOop* p) { work(p); } virtual void do_oop(oop* p) { work(p); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index edfb62b4046..a9c6a3395eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -59,15 +60,18 @@ void ShenandoahSuperClosure::do_nmethod(nmethod* nm) { // ========= Marking // -ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : +ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToScanQueue* q, + ShenandoahReferenceProcessor* rp, + ShenandoahObjToScanQueue* old_q) : ShenandoahSuperClosure(rp), _queue(q), + _old_queue(old_q), _mark_context(ShenandoahHeap::heap()->marking_context()), _weak(false) {} template inline void ShenandoahMarkRefsSuperClosure::work(T* p) { - ShenandoahMark::mark_through_ref(p, _queue, _mark_context, _weak); + ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, _weak); } ShenandoahForwardedIsAliveClosure::ShenandoahForwardedIsAliveClosure() : @@ -79,7 +83,7 @@ bool ShenandoahForwardedIsAliveClosure::do_object_b(oop obj) { } obj = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); shenandoah_assert_not_forwarded_if(nullptr, obj, ShenandoahHeap::heap()->is_concurrent_mark_in_progress()); - return _mark_context->is_marked(obj); + return _mark_context->is_marked_or_old(obj); } ShenandoahIsAliveClosure::ShenandoahIsAliveClosure() : @@ -90,7 +94,7 @@ bool ShenandoahIsAliveClosure::do_object_b(oop obj) { return false; } shenandoah_assert_not_forwarded(nullptr, obj); - return _mark_context->is_marked(obj); + return _mark_context->is_marked_or_old(obj); } BoolObjectClosure* ShenandoahIsAliveSelector::is_alive_closure() { @@ -105,7 +109,7 @@ ShenandoahKeepAliveClosure::ShenandoahKeepAliveClosure() : template void ShenandoahKeepAliveClosure::do_oop_work(T* p) { assert(ShenandoahHeap::heap()->is_concurrent_mark_in_progress(), "Only for concurrent marking phase"); - assert(!ShenandoahHeap::heap()->has_forwarded_objects(), "Not expected"); + assert(ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress() || !ShenandoahHeap::heap()->has_forwarded_objects(), "Not expected"); T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { @@ -215,8 +219,10 @@ void ShenandoahNMethodAndDisarmClosure::do_nmethod(nmethod* nm) { // template -ShenandoahMarkUpdateRefsClosure::ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : - ShenandoahMarkRefsSuperClosure(q, rp) { +ShenandoahMarkUpdateRefsClosure::ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, + ShenandoahReferenceProcessor* rp, + ShenandoahObjToScanQueue* old_q) : + ShenandoahMarkRefsSuperClosure(q, rp, old_q) { assert(_heap->is_stw_gc_in_progress(), "Can only be used for STW GC"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp index 70401b42461..124ab1958eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2023, Red Hat, Inc. All rights reserved. * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,6 +26,7 @@ #include "precompiled.hpp" +#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" @@ -41,9 +43,13 @@ ShenandoahCollectionSet::ShenandoahCollectionSet(ShenandoahHeap* heap, ReservedS _cset_map(_map_space.base() + ((uintx)heap_base >> _region_size_bytes_shift)), _biased_cset_map(_map_space.base()), _heap(heap), + _has_old_regions(false), _garbage(0), _used(0), + _live(0), _region_count(0), + _old_garbage(0), + _preselected_regions(nullptr), _current_index(0) { // The collection set map is reserved to cover the entire heap *and* zero addresses. @@ -84,17 +90,35 @@ void ShenandoahCollectionSet::add_region(ShenandoahHeapRegion* r) { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); assert(Thread::current()->is_VM_thread(), "Must be VMThread"); assert(!is_in(r), "Already in collection set"); + assert(!r->is_humongous(), "Only add regular regions to the collection set"); + _cset_map[r->index()] = 1; + size_t live = r->get_live_data_bytes(); + size_t garbage = r->garbage(); + size_t free = r->free(); + if (r->is_young()) { + _young_bytes_to_evacuate += live; + _young_available_bytes_collected += free; + if (ShenandoahHeap::heap()->mode()->is_generational() && r->age() >= ShenandoahGenerationalHeap::heap()->age_census()->tenuring_threshold()) { + _young_bytes_to_promote += live; + } + } else if (r->is_old()) { + _old_bytes_to_evacuate += live; + _old_garbage += garbage; + } + _region_count++; - _garbage += r->garbage(); + _has_old_regions |= r->is_old(); + _garbage += garbage; _used += r->used(); - + _live += live; // Update the region status too. State transition would be checked internally. r->make_cset(); } void ShenandoahCollectionSet::clear() { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); + Copy::zero_to_bytes(_cset_map, _map_size); #ifdef ASSERT @@ -104,10 +128,20 @@ void ShenandoahCollectionSet::clear() { #endif _garbage = 0; + _old_garbage = 0; _used = 0; + _live = 0; _region_count = 0; _current_index = 0; + + _young_bytes_to_evacuate = 0; + _young_bytes_to_promote = 0; + _old_bytes_to_evacuate = 0; + + _young_available_bytes_collected = 0; + + _has_old_regions = false; } ShenandoahHeapRegion* ShenandoahCollectionSet::claim_next() { @@ -151,7 +185,11 @@ ShenandoahHeapRegion* ShenandoahCollectionSet::next() { } void ShenandoahCollectionSet::print_on(outputStream* out) const { - out->print_cr("Collection Set : " SIZE_FORMAT "", count()); + out->print_cr("Collection Set: Regions: " + SIZE_FORMAT ", Garbage: " SIZE_FORMAT "%s, Live: " SIZE_FORMAT "%s, Used: " SIZE_FORMAT "%s", count(), + byte_size_in_proper_unit(garbage()), proper_unit_for_byte_size(garbage()), + byte_size_in_proper_unit(live()), proper_unit_for_byte_size(live()), + byte_size_in_proper_unit(used()), proper_unit_for_byte_size(used())); debug_only(size_t regions = 0;) for (size_t index = 0; index < _heap->num_regions(); index ++) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp index 8ac2d9fb2ea..ae1971f30d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -33,6 +34,14 @@ class ShenandoahCollectionSet : public CHeapObj { friend class ShenandoahHeap; + friend class ShenandoahCollectionSetPreselector; + + void establish_preselected(bool *preselected) { + assert(_preselected_regions == nullptr, "Over-writing"); + _preselected_regions = preselected; + } + void abandon_preselected() { _preselected_regions = nullptr; } + private: size_t const _map_size; size_t const _region_size_bytes_shift; @@ -43,10 +52,28 @@ class ShenandoahCollectionSet : public CHeapObj { ShenandoahHeap* const _heap; + bool _has_old_regions; size_t _garbage; size_t _used; + size_t _live; size_t _region_count; + size_t _young_bytes_to_evacuate; + size_t _young_bytes_to_promote; + size_t _old_bytes_to_evacuate; + + // How many bytes of old garbage are present in a mixed collection set? + size_t _old_garbage; + + // Points to array identifying which tenure-age regions have been preselected + // for inclusion in collection set. This field is only valid during brief + // spans of time while collection set is being constructed. + bool* _preselected_regions; + + // When a region having memory available to be allocated is added to the collection set, the region's available memory + // should be subtracted from what's available. + size_t _young_available_bytes_collected; + shenandoah_padding(0); volatile size_t _current_index; shenandoah_padding(1); @@ -77,8 +104,31 @@ class ShenandoahCollectionSet : public CHeapObj { void print_on(outputStream* out) const; - size_t used() const { return _used; } - size_t garbage() const { return _garbage; } + // It is not known how many of these bytes will be promoted. + inline size_t get_young_bytes_reserved_for_evacuation(); + inline size_t get_old_bytes_reserved_for_evacuation(); + + inline size_t get_young_bytes_to_be_promoted(); + + size_t get_young_available_bytes_collected() { return _young_available_bytes_collected; } + + inline size_t get_old_garbage(); + + bool is_preselected(size_t region_idx) { + assert(_preselected_regions != nullptr, "Missing etsablish after abandon"); + return _preselected_regions[region_idx]; + } + + bool* preselected_regions() { + assert(_preselected_regions != nullptr, "Null ptr"); + return _preselected_regions; + } + + bool has_old_regions() const { return _has_old_regions; } + size_t used() const { return _used; } + size_t live() const { return _live; } + size_t garbage() const { return _garbage; } + void clear(); private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp index cceebae0b1c..791e9c73b28 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -53,4 +54,20 @@ bool ShenandoahCollectionSet::is_in_loc(void* p) const { return _biased_cset_map[index] == 1; } +size_t ShenandoahCollectionSet::get_old_bytes_reserved_for_evacuation() { + return _old_bytes_to_evacuate; +} + +size_t ShenandoahCollectionSet::get_young_bytes_reserved_for_evacuation() { + return _young_bytes_to_evacuate - _young_bytes_to_promote; +} + +size_t ShenandoahCollectionSet::get_young_bytes_to_be_promoted() { + return _young_bytes_to_promote; +} + +size_t ShenandoahCollectionSet::get_old_garbage() { + return _old_garbage; +} + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSET_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp new file mode 100644 index 00000000000..b78259dd85b --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSetPreselector.hpp @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSETPRESELECTOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSETPRESELECTOR_HPP + +#include "gc/shenandoah/shenandoahCollectionSet.hpp" +#include "memory/resourceArea.hpp" + +class ShenandoahCollectionSetPreselector : public StackObj { + ShenandoahCollectionSet* _cset; + bool* _pset; + ResourceMark _rm; + +public: + ShenandoahCollectionSetPreselector(ShenandoahCollectionSet* cset, size_t num_regions): + _cset(cset) { + _pset = NEW_RESOURCE_ARRAY(bool, num_regions); + for (unsigned int i = 0; i < num_regions; i++) { + _pset[i] = false; + } + _cset->establish_preselected(_pset); + } + + ~ShenandoahCollectionSetPreselector() { + _cset->abandon_preselected(); + } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTIONSETPRESELECTOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp index d14bcc39f45..42500ae8778 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -36,6 +37,10 @@ ShenandoahCollectorPolicy::ShenandoahCollectorPolicy() : _abbreviated_degenerated_gcs(0), _success_full_gcs(0), _consecutive_degenerated_gcs(0), + _consecutive_young_gcs(0), + _mixed_gcs(0), + _success_old_gcs(0), + _interrupted_old_gcs(0), _alloc_failure_degenerated(0), _alloc_failure_degenerated_upgrade_to_full(0), _alloc_failure_full(0) { @@ -66,7 +71,9 @@ void ShenandoahCollectorPolicy::record_degenerated_upgrade_to_full() { _alloc_failure_degenerated_upgrade_to_full++; } -void ShenandoahCollectorPolicy::record_success_concurrent(bool is_abbreviated) { +void ShenandoahCollectorPolicy::record_success_concurrent(bool is_young, bool is_abbreviated) { + update_young(is_young); + _consecutive_degenerated_gcs = 0; _success_concurrent_gcs++; if (is_abbreviated) { @@ -74,7 +81,23 @@ void ShenandoahCollectorPolicy::record_success_concurrent(bool is_abbreviated) { } } -void ShenandoahCollectorPolicy::record_success_degenerated(bool is_abbreviated) { +void ShenandoahCollectorPolicy::record_mixed_cycle() { + _mixed_gcs++; +} + +void ShenandoahCollectorPolicy::record_success_old() { + _consecutive_young_gcs = 0; + _success_old_gcs++; +} + +void ShenandoahCollectorPolicy::record_interrupted_old() { + _consecutive_young_gcs = 0; + _interrupted_old_gcs++; +} + +void ShenandoahCollectorPolicy::record_success_degenerated(bool is_young, bool is_abbreviated) { + update_young(is_young); + _success_degenerated_gcs++; _consecutive_degenerated_gcs++; if (is_abbreviated) { @@ -82,8 +105,17 @@ void ShenandoahCollectorPolicy::record_success_degenerated(bool is_abbreviated) } } +void ShenandoahCollectorPolicy::update_young(bool is_young) { + if (is_young) { + _consecutive_young_gcs++; + } else { + _consecutive_young_gcs = 0; + } +} + void ShenandoahCollectorPolicy::record_success_full() { _consecutive_degenerated_gcs = 0; + _consecutive_young_gcs = 0; _success_full_gcs++; } @@ -101,8 +133,9 @@ bool is_explicit_gc(GCCause::Cause cause) { } bool is_implicit_gc(GCCause::Cause cause) { - return cause != GCCause::_allocation_failure + return cause != GCCause::_no_gc && cause != GCCause::_shenandoah_concurrent_gc + && cause != GCCause::_allocation_failure && !is_explicit_gc(cause); } @@ -120,6 +153,10 @@ bool is_valid_request(GCCause::Cause cause) { } #endif +bool ShenandoahCollectorPolicy::is_requested_gc(GCCause::Cause cause) { + return is_explicit_gc(cause) || is_implicit_gc(cause); +} + bool ShenandoahCollectorPolicy::should_run_full_gc(GCCause::Cause cause) { return is_explicit_gc(cause) ? !ExplicitGCInvokesConcurrent : !ShenandoahImplicitGCInvokesConcurrent; } @@ -141,7 +178,7 @@ void ShenandoahCollectorPolicy::print_gc_stats(outputStream* out) const { out->print_cr("enough regions with no live objects to skip evacuation."); out->cr(); - size_t completed_gcs = _success_full_gcs + _success_degenerated_gcs + _success_concurrent_gcs; + size_t completed_gcs = _success_full_gcs + _success_degenerated_gcs + _success_concurrent_gcs + _success_old_gcs; out->print_cr(SIZE_FORMAT_W(5) " Completed GCs", completed_gcs); size_t explicit_requests = 0; @@ -171,6 +208,13 @@ void ShenandoahCollectorPolicy::print_gc_stats(outputStream* out) const { out->print_cr(" " SIZE_FORMAT_W(5) " abbreviated (%.2f%%)", _abbreviated_concurrent_gcs, percent_of(_abbreviated_concurrent_gcs, _success_concurrent_gcs)); out->cr(); + if (ShenandoahHeap::heap()->mode()->is_generational()) { + out->print_cr(SIZE_FORMAT_W(5) " Completed Old GCs (%.2f%%)", _success_old_gcs, percent_of(_success_old_gcs, completed_gcs)); + out->print_cr(" " SIZE_FORMAT_W(5) " mixed", _mixed_gcs); + out->print_cr(" " SIZE_FORMAT_W(5) " interruptions", _interrupted_old_gcs); + out->cr(); + } + size_t degenerated_gcs = _alloc_failure_degenerated_upgrade_to_full + _success_degenerated_gcs; out->print_cr(SIZE_FORMAT_W(5) " Degenerated GCs (%.2f%%)", degenerated_gcs, percent_of(degenerated_gcs, completed_gcs)); out->print_cr(" " SIZE_FORMAT_W(5) " upgraded to Full GC (%.2f%%)", _alloc_failure_degenerated_upgrade_to_full, percent_of(_alloc_failure_degenerated_upgrade_to_full, degenerated_gcs)); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp index 638acce1456..2c92d91ac99 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -28,14 +29,10 @@ #include "gc/shared/gcTrace.hpp" #include "gc/shenandoah/shenandoahGC.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" +#include "gc/shenandoah/shenandoahTrace.hpp" #include "memory/allocation.hpp" #include "utilities/ostream.hpp" -class ShenandoahTracer : public GCTracer, public CHeapObj { -public: - ShenandoahTracer() : GCTracer(Shenandoah) {} -}; - class ShenandoahCollectorPolicy : public CHeapObj { private: size_t _success_concurrent_gcs; @@ -45,6 +42,10 @@ class ShenandoahCollectorPolicy : public CHeapObj { // Written by control thread, read by mutators volatile size_t _success_full_gcs; uint _consecutive_degenerated_gcs; + volatile size_t _consecutive_young_gcs; + size_t _mixed_gcs; + size_t _success_old_gcs; + size_t _interrupted_old_gcs; size_t _alloc_failure_degenerated; size_t _alloc_failure_degenerated_upgrade_to_full; size_t _alloc_failure_full; @@ -58,13 +59,17 @@ class ShenandoahCollectorPolicy : public CHeapObj { public: ShenandoahCollectorPolicy(); + void record_mixed_cycle(); + void record_success_old(); + void record_interrupted_old(); + // A collection cycle may be "abbreviated" if Shenandoah finds a sufficient percentage // of regions that contain no live objects (ShenandoahImmediateThreshold). These cycles // end after final mark, skipping the evacuation and reference-updating phases. Such // cycles are very efficient and are worth tracking. Note that both degenerated and // concurrent cycles can be abbreviated. - void record_success_concurrent(bool is_abbreviated); - void record_success_degenerated(bool is_abbreviated); + void record_success_concurrent(bool is_young, bool is_abbreviated); + void record_success_degenerated(bool is_young, bool is_abbreviated); void record_success_full(); void record_alloc_failure_to_degenerated(ShenandoahGC::ShenandoahDegenPoint point); void record_alloc_failure_to_full(); @@ -89,8 +94,16 @@ class ShenandoahCollectorPolicy : public CHeapObj { return _consecutive_degenerated_gcs; } + static bool is_requested_gc(GCCause::Cause cause); static bool should_run_full_gc(GCCause::Cause cause); static bool should_handle_requested_gc(GCCause::Cause cause); + + inline size_t consecutive_young_gc_count() const { + return _consecutive_young_gcs; + } + +private: + void update_young(bool is_young); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHCOLLECTORPOLICY_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index d801dda372e..a2f15140991 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -33,6 +34,10 @@ #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" @@ -86,22 +91,21 @@ class ShenandoahBreakpointMarkScope : public StackObj { } }; -ShenandoahConcurrentGC::ShenandoahConcurrentGC() : - _mark(), +ShenandoahConcurrentGC::ShenandoahConcurrentGC(ShenandoahGeneration* generation, bool do_old_gc_bootstrap) : + _mark(generation), + _generation(generation), _degen_point(ShenandoahDegenPoint::_degenerated_unset), - _abbreviated(false) { + _abbreviated(false), + _do_old_gc_bootstrap(do_old_gc_bootstrap) { } ShenandoahGC::ShenandoahDegenPoint ShenandoahConcurrentGC::degen_point() const { return _degen_point; } -void ShenandoahConcurrentGC::cancel() { - ShenandoahConcurrentMark::cancel(); -} - bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + ShenandoahBreakpointGCScope breakpoint_gc_scope(cause); // Reset for upcoming marking @@ -112,9 +116,18 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { { ShenandoahBreakpointMarkScope breakpoint_mark_scope(cause); + + // Reset task queue stats here, rather than in mark_concurrent_roots, + // because remembered set scan will `push` oops into the queues and + // resetting after this happens will lose those counts. + TASKQUEUE_STATS_ONLY(_mark.task_queues()->reset_taskqueue_stats()); + + // Concurrent remembered set scanning + entry_scan_remembered_set(); + // Concurrent mark roots entry_mark_roots(); - if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_outside_cycle)) { + if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_roots)) { return false; } @@ -132,7 +145,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // in the marking phase and must resume the degenerated cycle from there. If the GC was cancelled // after final mark, then we've entered the evacuation phase and must resume the degenerated cycle // from that phase. - if (heap->is_concurrent_mark_in_progress()) { + if (_generation->is_concurrent_mark_in_progress()) { bool cancelled = check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_mark); assert(cancelled, "GC must have been cancelled between concurrent and final mark"); return false; @@ -150,7 +163,8 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { } // Final mark might have reclaimed some immediate garbage, kick cleanup to reclaim - // the space. This would be the last action if there is nothing to evacuate. + // the space. This would be the last action if there is nothing to evacuate. Note that + // we will not age young-gen objects in the case that we skip evacuation. entry_cleanup_early(); heap->free_set()->log_status_under_lock(); @@ -196,10 +210,32 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // Update references freed up collection set, kick the cleanup to reclaim the space. entry_cleanup_complete(); } else { + // We chose not to evacuate because we found sufficient immediate garbage. + // However, there may still be regions to promote in place, so do that now. + if (has_in_place_promotions(heap)) { + entry_promote_in_place(); + + // If the promote-in-place operation was cancelled, we can have the degenerated + // cycle complete the operation. It will see that no evacuations are in progress, + // and that there are regions wanting promotion. The risk with not handling the + // cancellation would be failing to restore top for these regions and leaving + // them unable to serve allocations for the old generation. + if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { + return false; + } + } + + // At this point, the cycle is effectively complete. If the cycle has been cancelled here, + // the control thread will detect it on its next iteration and run a degenerated young cycle. vmop_entry_final_roots(); _abbreviated = true; } + // We defer generation resizing actions until after cset regions have been recycled. We do this even following an + // abbreviated cycle. + if (heap->mode()->is_generational()) { + ShenandoahGenerationalHeap::heap()->complete_concurrent_cycle(); + } return true; } @@ -300,7 +336,7 @@ void ShenandoahConcurrentGC::entry_final_updaterefs() { } void ShenandoahConcurrentGC::entry_final_roots() { - static const char* msg = "Pause Final Roots"; + const char* msg = final_roots_event_message(); ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_roots); EventMark em("%s", msg); @@ -309,17 +345,47 @@ void ShenandoahConcurrentGC::entry_final_roots() { void ShenandoahConcurrentGC::entry_reset() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + heap->try_inject_alloc_failure(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent reset"; - ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset); - EventMark em("%s", msg); + { + const char* msg = conc_reset_event_message(); + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_reset(), + msg); + op_reset(); + } - ShenandoahWorkerScope scope(heap->workers(), - ShenandoahWorkerPolicy::calc_workers_for_conc_reset(), - "concurrent reset"); + if (_do_old_gc_bootstrap) { + static const char* msg = "Concurrent reset (Old)"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset_old); + ShenandoahWorkerScope scope(ShenandoahHeap::heap()->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_reset(), + msg); + EventMark em("%s", msg); - heap->try_inject_alloc_failure(); - op_reset(); + heap->old_generation()->prepare_gc(); + } +} + +void ShenandoahConcurrentGC::entry_scan_remembered_set() { + if (_generation->is_young()) { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + const char* msg = "Concurrent remembered set scanning"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::init_scan_rset); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_rs_scanning(), + msg); + + heap->try_inject_alloc_failure(); + _generation->scan_remembered_set(true /* is_concurrent */); + } } void ShenandoahConcurrentGC::entry_mark_roots() { @@ -368,7 +434,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() { void ShenandoahConcurrentGC::entry_weak_refs() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - static const char* msg = "Concurrent weak references"; + const char* msg = conc_weak_refs_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_refs); EventMark em("%s", msg); @@ -383,7 +449,7 @@ void ShenandoahConcurrentGC::entry_weak_refs() { void ShenandoahConcurrentGC::entry_weak_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent weak roots"; + const char* msg = conc_weak_roots_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_roots); EventMark em("%s", msg); @@ -430,7 +496,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() { void ShenandoahConcurrentGC::entry_cleanup_early() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent cleanup"; + const char* msg = conc_cleanup_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_early, true /* log_heap_usage */); EventMark em("%s", msg); @@ -455,6 +521,23 @@ void ShenandoahConcurrentGC::entry_evacuate() { op_evacuate(); } +void ShenandoahConcurrentGC::entry_promote_in_place() { + shenandoah_assert_generational(); + + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + + static const char* msg = "Promote in place"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::promote_in_place); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), + "promote in place"); + + ShenandoahGenerationalHeap::heap()->promote_regions_in_place(true); +} + void ShenandoahConcurrentGC::entry_update_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); @@ -486,7 +569,7 @@ void ShenandoahConcurrentGC::entry_updaterefs() { void ShenandoahConcurrentGC::entry_cleanup_complete() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent cleanup"; + const char* msg = conc_cleanup_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_complete, true /* log_heap_usage */); EventMark em("%s", msg); @@ -500,8 +583,7 @@ void ShenandoahConcurrentGC::op_reset() { if (ShenandoahPacing) { heap->pacer()->setup_for_reset(); } - - heap->prepare_gc(); + _generation->prepare_gc(); } class ShenandoahInitMarkUpdateRegionStateClosure : public ShenandoahHeapRegionClosure { @@ -514,7 +596,8 @@ class ShenandoahInitMarkUpdateRegionStateClosure : public ShenandoahHeapRegionCl assert(!r->has_live(), "Region " SIZE_FORMAT " should have no live data", r->index()); if (r->is_active()) { // Check if region needs updating its TAMS. We have updated it already during concurrent - // reset, so it is very likely we don't need to do another write here. + // reset, so it is very likely we don't need to do another write here. Since most regions + // are not "active", this path is relatively rare. if (_ctx->top_at_mark_start(r) != r->top()) { _ctx->capture_top_at_mark_start(r); } @@ -536,10 +619,29 @@ void ShenandoahConcurrentGC::op_init_mark() { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Should be at safepoint"); assert(Thread::current()->is_VM_thread(), "can only do this in VMThread"); - assert(heap->marking_context()->is_bitmap_clear(), "need clear marking bitmap"); - assert(!heap->marking_context()->is_complete(), "should not be complete"); + assert(_generation->is_bitmap_clear(), "need clear marking bitmap"); + assert(!_generation->is_mark_complete(), "should not be complete"); assert(!heap->has_forwarded_objects(), "No forwarded objects on this path"); + + if (heap->mode()->is_generational()) { + if (_generation->is_young()) { + // The current implementation of swap_remembered_set() copies the write-card-table to the read-card-table. + ShenandoahGCPhase phase(ShenandoahPhaseTimings::init_swap_rset); + _generation->swap_remembered_set(); + } + + if (_generation->is_global()) { + heap->old_generation()->cancel_gc(); + } else if (heap->is_concurrent_old_mark_in_progress()) { + // Purge the SATB buffers, transferring any valid, old pointers to the + // old generation mark queue. Any pointers in a young region will be + // abandoned. + ShenandoahGCPhase phase(ShenandoahPhaseTimings::init_transfer_satb); + heap->old_generation()->transfer_pointers_from_satb(); + } + } + if (ShenandoahVerify) { heap->verifier()->verify_before_concmark(); } @@ -548,18 +650,26 @@ void ShenandoahConcurrentGC::op_init_mark() { Universe::verify(); } - heap->set_concurrent_mark_in_progress(true); + _generation->set_concurrent_mark_in_progress(true); start_mark(); - { + if (_do_old_gc_bootstrap) { + shenandoah_assert_generational(); + // Update region state for both young and old regions ShenandoahGCPhase phase(ShenandoahPhaseTimings::init_update_region_states); ShenandoahInitMarkUpdateRegionStateClosure cl; heap->parallel_heap_region_iterate(&cl); + heap->old_generation()->ref_processor()->reset_thread_locals(); + } else { + // Update region state for only young regions + ShenandoahGCPhase phase(ShenandoahPhaseTimings::init_update_region_states); + ShenandoahInitMarkUpdateRegionStateClosure cl; + _generation->parallel_heap_region_iterate(&cl); } // Weak reference processing - ShenandoahReferenceProcessor* rp = heap->ref_processor(); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); rp->reset_thread_locals(); rp->set_soft_reference_policy(heap->soft_ref_policy()->should_clear_all_soft_refs()); @@ -599,12 +709,22 @@ void ShenandoahConcurrentGC::op_final_mark() { // Notify JVMTI that the tagmap table will need cleaning. JvmtiTagMap::set_needs_cleaning(); - heap->prepare_regions_and_collection_set(true /*concurrent*/); + // The collection set is chosen by prepare_regions_and_collection_set(). Additionally, certain parameters have been + // established to govern the evacuation efforts that are about to begin. Refer to comments on reserve members in + // ShenandoahGeneration and ShenandoahOldGeneration for more detail. + _generation->prepare_regions_and_collection_set(true /*concurrent*/); // Has to be done after cset selection heap->prepare_concurrent_roots(); if (!heap->collection_set()->is_empty()) { + LogTarget(Debug, gc, cset) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + heap->collection_set()->print_on(&ls); + } + if (ShenandoahVerify) { heap->verifier()->verify_before_evacuation(); } @@ -622,7 +742,11 @@ void ShenandoahConcurrentGC::op_final_mark() { } } else { if (ShenandoahVerify) { - heap->verifier()->verify_after_concmark(); + if (has_in_place_promotions(heap)) { + heap->verifier()->verify_after_concmark_with_promotions(); + } else { + heap->verifier()->verify_after_concmark(); + } } if (VerifyAfterGC) { @@ -632,39 +756,47 @@ void ShenandoahConcurrentGC::op_final_mark() { } } +bool ShenandoahConcurrentGC::has_in_place_promotions(ShenandoahHeap* heap) { + return heap->mode()->is_generational() && heap->old_generation()->has_in_place_promotions(); +} + +template class ShenandoahConcurrentEvacThreadClosure : public ThreadClosure { private: OopClosure* const _oops; - public: - ShenandoahConcurrentEvacThreadClosure(OopClosure* oops); - void do_thread(Thread* thread); -}; + explicit ShenandoahConcurrentEvacThreadClosure(OopClosure* oops) : _oops(oops) {} -ShenandoahConcurrentEvacThreadClosure::ShenandoahConcurrentEvacThreadClosure(OopClosure* oops) : - _oops(oops) { -} - -void ShenandoahConcurrentEvacThreadClosure::do_thread(Thread* thread) { - JavaThread* const jt = JavaThread::cast(thread); - StackWatermarkSet::finish_processing(jt, _oops, StackWatermarkKind::gc); -} + void do_thread(Thread* thread) override { + JavaThread* const jt = JavaThread::cast(thread); + StackWatermarkSet::finish_processing(jt, _oops, StackWatermarkKind::gc); + if (GENERATIONAL) { + ShenandoahThreadLocalData::enable_plab_promotions(thread); + } + } +}; +template class ShenandoahConcurrentEvacUpdateThreadTask : public WorkerTask { private: ShenandoahJavaThreadsIterator _java_threads; public: - ShenandoahConcurrentEvacUpdateThreadTask(uint n_workers) : + explicit ShenandoahConcurrentEvacUpdateThreadTask(uint n_workers) : WorkerTask("Shenandoah Evacuate/Update Concurrent Thread Roots"), _java_threads(ShenandoahPhaseTimings::conc_thread_roots, n_workers) { } - void work(uint worker_id) { + void work(uint worker_id) override { + if (GENERATIONAL) { + Thread* worker_thread = Thread::current(); + ShenandoahThreadLocalData::enable_plab_promotions(worker_thread); + } + // ShenandoahEvacOOMScope has to be setup by ShenandoahContextEvacuateUpdateRootsClosure. // Otherwise, may deadlock with watermark lock ShenandoahContextEvacuateUpdateRootsClosure oops_cl; - ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl); + ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl); _java_threads.threads_do(&thr_cl, worker_id); } }; @@ -673,8 +805,13 @@ void ShenandoahConcurrentGC::op_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); assert(heap->is_evacuation_in_progress(), "Checked by caller"); ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_thread_roots); - ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); - heap->workers()->run_task(&task); + if (heap->mode()->is_generational()) { + ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); + heap->workers()->run_task(&task); + } else { + ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); + heap->workers()->run_task(&task); + } } void ShenandoahConcurrentGC::op_weak_refs() { @@ -685,7 +822,7 @@ void ShenandoahConcurrentGC::op_weak_refs() { if (heap->gc_cause() == GCCause::_wb_breakpoint) { ShenandoahBreakpoint::at_after_reference_processing_started(); } - heap->ref_processor()->process_references(ShenandoahPhaseTimings::conc_weak_refs, heap->workers(), true /* concurrent */); + _generation->ref_processor()->process_references(ShenandoahPhaseTimings::conc_weak_refs, heap->workers(), true /* concurrent */); } class ShenandoahEvacUpdateCleanupOopStorageRootsClosure : public BasicOopIterateClosure { @@ -712,8 +849,11 @@ void ShenandoahEvacUpdateCleanupOopStorageRootsClosure::do_oop(oop* p) { const oop obj = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(obj)) { if (!_mark_context->is_marked(obj)) { - // Note: The obj is dead here. Do not touch it, just clear. - ShenandoahHeap::atomic_clear_oop(p, obj); + shenandoah_assert_generations_reconciled(); + if (_heap->is_in_active_generation(obj)) { + // Note: The obj is dead here. Do not touch it, just clear. + ShenandoahHeap::atomic_clear_oop(p, obj); + } } else if (_evac_in_progress && _heap->in_collection_set(obj)) { oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); if (resolved == obj) { @@ -819,6 +959,9 @@ void ShenandoahConcurrentGC::op_weak_roots() { ShenandoahTimingsTracker t(ShenandoahPhaseTimings::conc_weak_roots_rendezvous); heap->rendezvous_threads("Shenandoah Concurrent Weak Roots"); } + // We can only toggle concurrent_weak_root_in_progress flag + // at a safepoint, so that mutators see a consistent + // value. The flag will be cleared at the next safepoint. } void ShenandoahConcurrentGC::op_class_unloading() { @@ -915,11 +1058,10 @@ void ShenandoahConcurrentGC::op_init_updaterefs() { heap->set_evacuation_in_progress(false); heap->set_concurrent_weak_root_in_progress(false); heap->prepare_update_heap_references(true /*concurrent*/); + heap->set_update_refs_in_progress(true); if (ShenandoahVerify) { heap->verifier()->verify_before_updaterefs(); } - - heap->set_update_refs_in_progress(true); if (ShenandoahPacing) { heap->pacer()->setup_for_updaterefs(); } @@ -967,7 +1109,7 @@ void ShenandoahConcurrentGC::op_final_updaterefs() { // Clear cancelled GC, if set. On cancellation path, the block before would handle // everything. if (heap->cancelled_gc()) { - heap->clear_cancelled_gc(); + heap->clear_cancelled_gc(true /* clear oom handler */); } // Has to be done before cset is clear @@ -975,11 +1117,35 @@ void ShenandoahConcurrentGC::op_final_updaterefs() { heap->verifier()->verify_roots_in_to_space(); } + // If we are running in generational mode and this is an aging cycle, this will also age active + // regions that haven't been used for allocation. heap->update_heap_region_states(true /*concurrent*/); heap->set_update_refs_in_progress(false); heap->set_has_forwarded_objects(false); + if (heap->mode()->is_generational() && heap->is_concurrent_old_mark_in_progress()) { + // When the SATB barrier is left on to support concurrent old gen mark, it may pick up writes to + // objects in the collection set. After those objects are evacuated, the pointers in the + // SATB are no longer safe. Once we have finished update references, we are guaranteed that + // no more writes to the collection set are possible. + // + // This will transfer any old pointers in _active_ regions from the SATB to the old gen + // mark queues. All other pointers will be discarded. This would also discard any pointers + // in old regions that were included in a mixed evacuation. We aren't using the SATB filter + // methods here because we cannot control when they execute. If the SATB filter runs _after_ + // a region has been recycled, we will not be able to detect the bad pointer. + // + // We are not concerned about skipping this step in abbreviated cycles because regions + // with no live objects cannot have been written to and so cannot have entries in the SATB + // buffers. + heap->old_generation()->transfer_pointers_from_satb(); + + // Aging_cycle is only relevant during evacuation cycle for individual objects and during final mark for + // entire regions. Both of these relevant operations occur before final update refs. + ShenandoahGenerationalHeap::heap()->set_aging_cycle(false); + } + if (ShenandoahVerify) { heap->verifier()->verify_after_updaterefs(); } @@ -992,7 +1158,23 @@ void ShenandoahConcurrentGC::op_final_updaterefs() { } void ShenandoahConcurrentGC::op_final_roots() { - ShenandoahHeap::heap()->set_concurrent_weak_root_in_progress(false); + + ShenandoahHeap *heap = ShenandoahHeap::heap(); + heap->set_concurrent_weak_root_in_progress(false); + heap->set_evacuation_in_progress(false); + + if (heap->mode()->is_generational()) { + // If the cycle was shortened for having enough immediate garbage, this could be + // the last GC safepoint before concurrent marking of old resumes. We must be sure + // that old mark threads don't see any pointers to garbage in the SATB buffers. + if (heap->is_concurrent_old_mark_in_progress()) { + heap->old_generation()->transfer_pointers_from_satb(); + } + + if (!_generation->is_old()) { + ShenandoahGenerationalHeap::heap()->update_region_ages(_generation->complete_marking_context()); + } + } } void ShenandoahConcurrentGC::op_cleanup_complete() { @@ -1011,28 +1193,71 @@ const char* ShenandoahConcurrentGC::init_mark_event_message() const { ShenandoahHeap* const heap = ShenandoahHeap::heap(); assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here"); if (heap->unload_classes()) { - return "Pause Init Mark (unload classes)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Init Mark", " (unload classes)"); } else { - return "Pause Init Mark"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Init Mark", ""); } } const char* ShenandoahConcurrentGC::final_mark_event_message() const { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here"); + assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(), + "Should not have forwarded objects during final mark, unless old gen concurrent mark is running"); + if (heap->unload_classes()) { - return "Pause Final Mark (unload classes)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Mark", " (unload classes)"); } else { - return "Pause Final Mark"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Mark", ""); } } const char* ShenandoahConcurrentGC::conc_mark_event_message() const { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here"); + assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(), + "Should not have forwarded objects concurrent mark, unless old gen concurrent mark is running"); if (heap->unload_classes()) { - return "Concurrent marking (unload classes)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent marking", " (unload classes)"); + } else { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent marking", ""); + } +} + +const char* ShenandoahConcurrentGC::conc_reset_event_message() const { + if (ShenandoahHeap::heap()->unload_classes()) { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset", " (unload classes)"); + } else { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset", ""); + } +} + +const char* ShenandoahConcurrentGC::final_roots_event_message() const { + if (ShenandoahHeap::heap()->unload_classes()) { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Roots", " (unload classes)"); + } else { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Roots", ""); + } +} + +const char* ShenandoahConcurrentGC::conc_weak_refs_event_message() const { + if (ShenandoahHeap::heap()->unload_classes()) { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak references", " (unload classes)"); + } else { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak references", ""); + } +} + +const char* ShenandoahConcurrentGC::conc_weak_roots_event_message() const { + if (ShenandoahHeap::heap()->unload_classes()) { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak roots", " (unload classes)"); + } else { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak roots", ""); + } +} + +const char* ShenandoahConcurrentGC::conc_cleanup_event_message() const { + if (ShenandoahHeap::heap()->unload_classes()) { + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent cleanup", " (unload classes)"); } else { - return "Concurrent marking"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent cleanup", ""); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index 4cd1a841350..a1837068a7c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -30,6 +31,8 @@ #include "gc/shenandoah/shenandoahGC.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" +class ShenandoahGeneration; + class VM_ShenandoahInitMark; class VM_ShenandoahFinalMarkStartEvac; class VM_ShenandoahInitUpdateRefs; @@ -42,22 +45,24 @@ class ShenandoahConcurrentGC : public ShenandoahGC { friend class VM_ShenandoahFinalUpdateRefs; friend class VM_ShenandoahFinalRoots; +protected: + ShenandoahConcurrentMark _mark; + ShenandoahGeneration* const _generation; + private: - ShenandoahConcurrentMark _mark; - ShenandoahDegenPoint _degen_point; - bool _abbreviated; + ShenandoahDegenPoint _degen_point; + bool _abbreviated; + const bool _do_old_gc_bootstrap; public: - ShenandoahConcurrentGC(); - bool collect(GCCause::Cause cause); + ShenandoahConcurrentGC(ShenandoahGeneration* generation, bool do_old_gc_bootstrap); + bool collect(GCCause::Cause cause) override; ShenandoahDegenPoint degen_point() const; // Return true if this cycle found enough immediate garbage to skip evacuation bool abbreviated() const { return _abbreviated; } - // Cancel ongoing concurrent GC - static void cancel(); -private: +protected: // Entry points to STW GC operations, these cause a related safepoint, that then // call the entry method below void vmop_entry_init_mark(); @@ -78,6 +83,7 @@ class ShenandoahConcurrentGC : public ShenandoahGC { // for concurrent operation. void entry_reset(); void entry_mark_roots(); + void entry_scan_remembered_set(); void entry_mark(); void entry_thread_roots(); void entry_weak_refs(); @@ -90,12 +96,15 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_updaterefs(); void entry_cleanup_complete(); + // Called when the collection set is empty, but the generational mode has regions to promote in place + void entry_promote_in_place(); + // Actual work for the phases void op_reset(); void op_init_mark(); void op_mark_roots(); void op_mark(); - void op_final_mark(); + virtual void op_final_mark(); void op_thread_roots(); void op_weak_refs(); void op_weak_roots(); @@ -110,16 +119,24 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void op_final_roots(); void op_cleanup_complete(); + // Check GC cancellation and abort concurrent GC + bool check_cancellation_and_abort(ShenandoahDegenPoint point); + +private: void start_mark(); + static bool has_in_place_promotions(ShenandoahHeap* heap) ; + // Messages for GC trace events, they have to be immortal for // passing around the logging/tracing systems const char* init_mark_event_message() const; const char* final_mark_event_message() const; + const char* final_roots_event_message() const; const char* conc_mark_event_message() const; - - // Check GC cancellation and abort concurrent GC - bool check_cancellation_and_abort(ShenandoahDegenPoint point); + const char* conc_reset_event_message() const; + const char* conc_weak_refs_event_message() const; + const char* conc_weak_roots_event_message() const; + const char* conc_cleanup_event_message() const; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONCURRENTGC_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 0e2ef4144ad..1709844a8c3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -30,6 +31,7 @@ #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahConcurrentMark.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" @@ -38,6 +40,7 @@ #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "memory/iterator.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/continuation.hpp" @@ -57,8 +60,13 @@ class ShenandoahConcurrentMarkingTask : public WorkerTask { void work(uint worker_id) { ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahConcurrentWorkerSession worker_session(worker_id); + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_mark, ShenandoahPhaseTimings::ParallelMark, worker_id, true); ShenandoahSuspendibleThreadSetJoiner stsj; - ShenandoahReferenceProcessor* rp = heap->ref_processor(); + // Do not use active_generation() : we must use the gc_generation() set by + // ShenandoahGCScope on the ControllerThread's stack; no safepoint may + // intervene to update active_generation, so we can't + // shenandoah_assert_generations_reconciled() here. + ShenandoahReferenceProcessor* rp = heap->gc_generation()->ref_processor(); assert(rp != nullptr, "need reference processor"); StringDedup::Requests requests; _cm->mark_loop(worker_id, _terminator, rp, GENERATION, true /*cancellable*/, @@ -97,14 +105,16 @@ class ShenandoahFinalMarkingTask : public WorkerTask { ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahParallelWorkerSession worker_session(worker_id); - ShenandoahReferenceProcessor* rp = heap->ref_processor(); StringDedup::Requests requests; + ShenandoahReferenceProcessor* rp = heap->gc_generation()->ref_processor(); + shenandoah_assert_generations_reconciled(); // First drain remaining SATB buffers. { ShenandoahObjToScanQueue* q = _cm->get_queue(worker_id); + ShenandoahObjToScanQueue* old_q = _cm->get_old_queue(worker_id); - ShenandoahSATBBufferClosure cl(q); + ShenandoahSATBBufferClosure cl(q, old_q); SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); while (satb_mq_set.apply_closure_to_completed_buffer(&cl)) {} assert(!heap->has_forwarded_objects(), "Not expected"); @@ -119,8 +129,8 @@ class ShenandoahFinalMarkingTask : public WorkerTask { } }; -ShenandoahConcurrentMark::ShenandoahConcurrentMark() : - ShenandoahMark() {} +ShenandoahConcurrentMark::ShenandoahConcurrentMark(ShenandoahGeneration* generation) : + ShenandoahMark(generation) {} // Mark concurrent roots during concurrent phases template @@ -129,10 +139,12 @@ class ShenandoahMarkConcurrentRootsTask : public WorkerTask { SuspendibleThreadSetJoiner _sts_joiner; ShenandoahConcurrentRootScanner _root_scanner; ShenandoahObjToScanQueueSet* const _queue_set; + ShenandoahObjToScanQueueSet* const _old_queue_set; ShenandoahReferenceProcessor* const _rp; public: ShenandoahMarkConcurrentRootsTask(ShenandoahObjToScanQueueSet* qs, + ShenandoahObjToScanQueueSet* old, ShenandoahReferenceProcessor* rp, ShenandoahPhaseTimings::Phase phase, uint nworkers); @@ -141,12 +153,14 @@ class ShenandoahMarkConcurrentRootsTask : public WorkerTask { template ShenandoahMarkConcurrentRootsTask::ShenandoahMarkConcurrentRootsTask(ShenandoahObjToScanQueueSet* qs, - ShenandoahReferenceProcessor* rp, - ShenandoahPhaseTimings::Phase phase, - uint nworkers) : + ShenandoahObjToScanQueueSet* old, + ShenandoahReferenceProcessor* rp, + ShenandoahPhaseTimings::Phase phase, + uint nworkers) : WorkerTask("Shenandoah Concurrent Mark Roots"), _root_scanner(nworkers, phase), _queue_set(qs), + _old_queue_set(old), _rp(rp) { assert(!ShenandoahHeap::heap()->has_forwarded_objects(), "Not expected"); } @@ -155,7 +169,9 @@ template void ShenandoahMarkConcurrentRootsTask::work(uint worker_id) { ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahObjToScanQueue* q = _queue_set->queue(worker_id); - ShenandoahMarkRefsClosure cl(q, _rp); + ShenandoahObjToScanQueue* old_q = (_old_queue_set == nullptr) ? + nullptr : _old_queue_set->queue(worker_id); + ShenandoahMarkRefsClosure cl(q, _rp, old_q); _root_scanner.roots_do(&cl, worker_id); } @@ -163,14 +179,38 @@ void ShenandoahConcurrentMark::mark_concurrent_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); assert(!heap->has_forwarded_objects(), "Not expected"); - TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); - WorkerThreads* workers = heap->workers(); - ShenandoahReferenceProcessor* rp = heap->ref_processor(); - task_queues()->reserve(workers->active_workers()); - ShenandoahMarkConcurrentRootsTask task(task_queues(), rp, ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); - - workers->run_task(&task); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); + _generation->reserve_task_queues(workers->active_workers()); + switch (_generation->type()) { + case YOUNG: { + ShenandoahMarkConcurrentRootsTask task(task_queues(), old_task_queues(), rp, + ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); + workers->run_task(&task); + break; + } + case GLOBAL: { + assert(old_task_queues() == nullptr, "Global mark should not have old gen mark queues"); + ShenandoahMarkConcurrentRootsTask task(task_queues(), nullptr, rp, + ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); + workers->run_task(&task); + break; + } + case NON_GEN: { + assert(old_task_queues() == nullptr, "Non-generational mark should not have old gen mark queues"); + ShenandoahMarkConcurrentRootsTask task(task_queues(), nullptr, rp, + ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); + workers->run_task(&task); + break; + } + case OLD: { + // We use a YOUNG generation cycle to bootstrap concurrent old marking. + ShouldNotReachHere(); + break; + } + default: + ShouldNotReachHere(); + } } class ShenandoahFlushSATBHandshakeClosure : public HandshakeClosure { @@ -192,12 +232,38 @@ void ShenandoahConcurrentMark::concurrent_mark() { uint nworkers = workers->active_workers(); task_queues()->reserve(nworkers); + ShenandoahGenerationType gen_type = _generation->type(); ShenandoahSATBMarkQueueSet& qset = ShenandoahBarrierSet::satb_mark_queue_set(); ShenandoahFlushSATBHandshakeClosure flush_satb(qset); for (uint flushes = 0; flushes < ShenandoahMaxSATBBufferFlushes; flushes++) { - TaskTerminator terminator(nworkers, task_queues()); - ShenandoahConcurrentMarkingTask task(this, &terminator); - workers->run_task(&task); + switch (gen_type) { + case YOUNG: { + TaskTerminator terminator(nworkers, task_queues()); + ShenandoahConcurrentMarkingTask task(this, &terminator); + workers->run_task(&task); + break; + } + case OLD: { + TaskTerminator terminator(nworkers, task_queues()); + ShenandoahConcurrentMarkingTask task(this, &terminator); + workers->run_task(&task); + break; + } + case GLOBAL: { + TaskTerminator terminator(nworkers, task_queues()); + ShenandoahConcurrentMarkingTask task(this, &terminator); + workers->run_task(&task); + break; + } + case NON_GEN: { + TaskTerminator terminator(nworkers, task_queues()); + ShenandoahConcurrentMarkingTask task(this, &terminator); + workers->run_task(&task); + break; + } + default: + ShouldNotReachHere(); + } if (heap->cancelled_gc()) { // GC is cancelled, break out. @@ -226,9 +292,8 @@ void ShenandoahConcurrentMark::finish_mark() { assert(task_queues()->is_empty(), "Should be empty"); TASKQUEUE_STATS_ONLY(task_queues()->print_and_reset_taskqueue_stats("")); - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - heap->set_concurrent_mark_in_progress(false); - heap->mark_complete_marking_context(); + _generation->set_concurrent_mark_in_progress(false); + _generation->set_mark_complete(); end_mark(); } @@ -248,15 +313,32 @@ void ShenandoahConcurrentMark::finish_mark_work() { StrongRootsScope scope(nworkers); TaskTerminator terminator(nworkers, task_queues()); - ShenandoahFinalMarkingTask task(this, &terminator, ShenandoahStringDedup::is_enabled()); - heap->workers()->run_task(&task); - assert(task_queues()->is_empty(), "Should be empty"); -} + switch (_generation->type()) { + case YOUNG:{ + ShenandoahFinalMarkingTask task(this, &terminator, ShenandoahStringDedup::is_enabled()); + heap->workers()->run_task(&task); + break; + } + case OLD:{ + ShenandoahFinalMarkingTask task(this, &terminator, ShenandoahStringDedup::is_enabled()); + heap->workers()->run_task(&task); + break; + } + case GLOBAL:{ + ShenandoahFinalMarkingTask task(this, &terminator, ShenandoahStringDedup::is_enabled()); + heap->workers()->run_task(&task); + break; + } + case NON_GEN:{ + ShenandoahFinalMarkingTask task(this, &terminator, ShenandoahStringDedup::is_enabled()); + heap->workers()->run_task(&task); + break; + } + default: + ShouldNotReachHere(); + } -void ShenandoahConcurrentMark::cancel() { - clear(); - ShenandoahReferenceProcessor* rp = ShenandoahHeap::heap()->ref_processor(); - rp->abandon_partial_discovery(); + assert(task_queues()->is_empty(), "Should be empty"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp index b658e42dfb8..17b27037658 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp @@ -32,22 +32,24 @@ template class ShenandoahConcurrentMarkingTask; template class ShenandoahFinalMarkingTask; +class ShenandoahGeneration; class ShenandoahConcurrentMark: public ShenandoahMark { template friend class ShenandoahConcurrentMarkingTask; template friend class ShenandoahFinalMarkingTask; public: - ShenandoahConcurrentMark(); + ShenandoahConcurrentMark(ShenandoahGeneration* generation); + // Concurrent mark roots void mark_concurrent_roots(); + // Concurrent mark void concurrent_mark(); + // Finish mark at a safepoint void finish_mark(); - static void cancel(); - private: void finish_mark_work(); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index df2d6d092e6..8a82498225a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -30,6 +30,7 @@ #include "gc/shenandoah/shenandoahDegeneratedGC.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahFullGC.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" @@ -85,7 +86,7 @@ void ShenandoahControlThread::run_service() { if (alloc_failure_pending) { // Allocation failure takes precedence: we have to deal with it first thing - log_info(gc)("Trigger: Handle Allocation Failure"); + heuristics->log_trigger("Handle Allocation Failure"); cause = GCCause::_allocation_failure; @@ -104,7 +105,7 @@ void ShenandoahControlThread::run_service() { } } else if (is_gc_requested) { cause = requested_gc_cause; - log_info(gc)("Trigger: GC request (%s)", GCCause::to_string(cause)); + heuristics->log_trigger("GC request (%s)", GCCause::to_string(cause)); heuristics->record_requested_gc(); if (ShenandoahCollectorPolicy::should_run_full_gc(cause)) { @@ -315,19 +316,21 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) return; GCIdMark gc_id_mark; - ShenandoahGCSession session(cause); + ShenandoahGCSession session(cause, heap->global_generation()); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - ShenandoahConcurrentGC gc; + ShenandoahConcurrentGC gc(heap->global_generation(), false); if (gc.collect(cause)) { // Cycle is complete. There were no failed allocation requests and no degeneration, so count this as good progress. heap->notify_gc_progress(); - heap->heuristics()->record_success_concurrent(); - heap->shenandoah_policy()->record_success_concurrent(gc.abbreviated()); + heap->global_generation()->heuristics()->record_success_concurrent(); + heap->shenandoah_policy()->record_success_concurrent(false, gc.abbreviated()); + heap->log_heap_status("At end of GC"); } else { assert(heap->cancelled_gc(), "Must have been cancelled"); check_cancellation_or_degen(gc.degen_point()); + heap->log_heap_status("At end of cancelled GC"); } } @@ -350,8 +353,9 @@ void ShenandoahControlThread::stop_service() { } void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); GCIdMark gc_id_mark; - ShenandoahGCSession session(cause); + ShenandoahGCSession session(cause, heap->global_generation()); ShenandoahFullGC gc; gc.collect(cause); @@ -359,11 +363,11 @@ void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) { void ShenandoahControlThread::service_stw_degenerated_cycle(GCCause::Cause cause, ShenandoahGC::ShenandoahDegenPoint point) { assert (point != ShenandoahGC::_degenerated_unset, "Degenerated point should be set"); - + ShenandoahHeap* const heap = ShenandoahHeap::heap(); GCIdMark gc_id_mark; - ShenandoahGCSession session(cause); + ShenandoahGCSession session(cause, heap->global_generation()); - ShenandoahDegenGC gc(point); + ShenandoahDegenGC gc(point, heap->global_generation()); gc.collect(cause); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 2249c38455f..7d2690ef5f6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -29,26 +30,38 @@ #include "gc/shenandoah/shenandoahConcurrentMark.hpp" #include "gc/shenandoah/shenandoahDegeneratedGC.hpp" #include "gc/shenandoah/shenandoahFullGC.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMetrics.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" #include "gc/shenandoah/shenandoahVMOperations.hpp" #include "runtime/vmThread.hpp" #include "utilities/events.hpp" -ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point) : +ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation) : ShenandoahGC(), _degen_point(degen_point), + _generation(generation), _abbreviated(false) { } bool ShenandoahDegenGC::collect(GCCause::Cause cause) { vmop_degenerated(); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (heap->mode()->is_generational()) { + bool is_bootstrap_gc = heap->old_generation()->is_bootstrapping(); + heap->mmu_tracker()->record_degenerated(GCId::current(), is_bootstrap_gc); + const char* msg = is_bootstrap_gc? "At end of Degenerated Bootstrap Old GC": "At end of Degenerated Young GC"; + heap->log_heap_status(msg); + } return true; } @@ -64,7 +77,6 @@ void ShenandoahDegenGC::entry_degenerated() { ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::degen_gc, true /* log_heap_usage */); EventMark em("%s", msg); ShenandoahHeap* const heap = ShenandoahHeap::heap(); - ShenandoahWorkerScope scope(heap->workers(), ShenandoahWorkerPolicy::calc_workers_for_stw_degenerated(), "stw degenerated gc"); @@ -79,7 +91,26 @@ void ShenandoahDegenGC::op_degenerated() { // Degenerated GC is STW, but it can also fail. Current mechanics communicates // GC failure via cancelled_concgc() flag. So, if we detect the failure after // some phase, we have to upgrade the Degenerate GC to Full GC. - heap->clear_cancelled_gc(); + heap->clear_cancelled_gc(true /* clear oom handler */); + +#ifdef ASSERT + if (heap->mode()->is_generational()) { + ShenandoahOldGeneration* old_generation = heap->old_generation(); + if (!heap->is_concurrent_old_mark_in_progress()) { + // If we are not marking the old generation, there should be nothing in the old mark queues + assert(old_generation->task_queues()->is_empty(), "Old gen task queues should be empty"); + } + + if (_generation->is_global()) { + // If we are in a global cycle, the old generation should not be marking. It is, however, + // allowed to be holding regions for evacuation or coalescing. + assert(old_generation->is_idle() + || old_generation->is_doing_mixed_evacuations() + || old_generation->is_preparing_for_mark(), + "Old generation cannot be in state: %s", old_generation->state_name()); + } + } +#endif ShenandoahMetricsSnapshot metrics; metrics.snap_before(); @@ -95,17 +126,51 @@ void ShenandoahDegenGC::op_degenerated() { // space. It makes little sense to wait for Full GC to reclaim as much as it can, when // we can do the most aggressive degen cycle, which includes processing references and // class unloading, unless those features are explicitly disabled. - // - - // Degenerated from concurrent root mark, reset the flag for STW mark - if (heap->is_concurrent_mark_in_progress()) { - ShenandoahConcurrentMark::cancel(); - heap->set_concurrent_mark_in_progress(false); - } // Note that we can only do this for "outside-cycle" degens, otherwise we would risk // changing the cycle parameters mid-cycle during concurrent -> degenerated handover. - heap->set_unload_classes(heap->heuristics()->can_unload_classes()); + heap->set_unload_classes(_generation->heuristics()->can_unload_classes() && + (!heap->mode()->is_generational() || _generation->is_global())); + + if (heap->mode()->is_generational() && _generation->is_young()) { + // Swap remembered sets for young + _generation->swap_remembered_set(); + } + + case _degenerated_roots: + // Degenerated from concurrent root mark, reset the flag for STW mark + if (!heap->mode()->is_generational()) { + if (heap->is_concurrent_mark_in_progress()) { + heap->cancel_concurrent_mark(); + } + } else { + if (_generation->is_concurrent_mark_in_progress()) { + // We want to allow old generation marking to be punctuated by young collections + // (even if they have degenerated). If this is a global cycle, we'd have cancelled + // the entire old gc before coming into this switch. Note that cancel_marking on + // the generation does NOT abandon incomplete SATB buffers as cancel_concurrent_mark does. + // We need to separate out the old pointers which is done below. + _generation->cancel_marking(); + } + + if (heap->is_concurrent_mark_in_progress()) { + // If either old or young marking is in progress, the SATB barrier will be enabled. + // The SATB buffer may hold a mix of old and young pointers. The old pointers need to be + // transferred to the old generation mark queues and the young pointers are NOT part + // of this snapshot, so they must be dropped here. It is safe to drop them here because + // we will rescan the roots on this safepoint. + heap->old_generation()->transfer_pointers_from_satb(); + } + + if (_degen_point == ShenandoahDegenPoint::_degenerated_roots) { + // We only need this if the concurrent cycle has already swapped the card tables. + // Marking will use the 'read' table, but interesting pointers may have been + // recorded in the 'write' table in the time between the cancelled concurrent cycle + // and this degenerated cycle. These pointers need to be included the 'read' table + // used to scan the remembered set during the STW mark which follows here. + _generation->merge_write_table(); + } + } op_reset(); @@ -169,7 +234,6 @@ void ShenandoahDegenGC::op_degenerated() { { heap->sync_pinned_region_status(); heap->collection_set()->clear_current_index(); - ShenandoahHeapRegion* r; while ((r = heap->collection_set()->next()) != nullptr) { if (r->is_pinned()) { @@ -186,8 +250,17 @@ void ShenandoahDegenGC::op_degenerated() { op_degenerated_fail(); return; } + } else if (has_in_place_promotions(heap)) { + // We have nothing to evacuate, but there are still regions to promote in place. + ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_promote_regions); + ShenandoahGenerationalHeap::heap()->promote_regions_in_place(false /* concurrent*/); } + // Update collector state regardless of whether there are forwarded objects + heap->set_evacuation_in_progress(false); + heap->set_concurrent_weak_root_in_progress(false); + heap->set_concurrent_strong_root_in_progress(false); + // If heuristics thinks we should do the cycle, this flag would be set, // and we need to do update-refs. Otherwise, it would be the shortcut cycle. if (heap->has_forwarded_objects()) { @@ -209,6 +282,11 @@ void ShenandoahDegenGC::op_degenerated() { ShenandoahCodeRoots::disarm_nmethods(); op_cleanup_complete(); + + if (heap->mode()->is_generational()) { + ShenandoahGenerationalHeap::heap()->complete_degenerated_cycle(); + } + break; default: ShouldNotReachHere(); @@ -231,25 +309,24 @@ void ShenandoahDegenGC::op_degenerated() { op_degenerated_futile(); } else { heap->notify_gc_progress(); - heap->shenandoah_policy()->record_success_degenerated(_abbreviated); - heap->heuristics()->record_success_degenerated(); + heap->shenandoah_policy()->record_success_degenerated(_generation->is_young(), _abbreviated); + _generation->heuristics()->record_success_degenerated(); } } void ShenandoahDegenGC::op_reset() { - ShenandoahHeap::heap()->prepare_gc(); + _generation->prepare_gc(); } void ShenandoahDegenGC::op_mark() { - assert(!ShenandoahHeap::heap()->is_concurrent_mark_in_progress(), "Should be reset"); + assert(!_generation->is_concurrent_mark_in_progress(), "Should be reset"); ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_stw_mark); - ShenandoahSTWMark mark(false /*full gc*/); - mark.clear(); + ShenandoahSTWMark mark(_generation, false /*full gc*/); mark.mark(); } void ShenandoahDegenGC::op_finish_mark() { - ShenandoahConcurrentMark mark; + ShenandoahConcurrentMark mark(_generation); mark.finish_mark(); } @@ -261,8 +338,9 @@ void ShenandoahDegenGC::op_prepare_evacuation() { // STW cleanup weak roots and unload classes heap->parallel_cleaning(false /*full gc*/); + // Prepare regions and collection set - heap->prepare_regions_and_collection_set(false /*concurrent*/); + _generation->prepare_regions_and_collection_set(false /*concurrent*/); // Retire the TLABs, which will force threads to reacquire their TLABs after the pause. // This is needed for two reasons. Strong one: new allocations would be with new freeset, @@ -283,7 +361,11 @@ void ShenandoahDegenGC::op_prepare_evacuation() { heap->set_has_forwarded_objects(true); } else { if (ShenandoahVerify) { - heap->verifier()->verify_after_concmark(); + if (has_in_place_promotions(heap)) { + heap->verifier()->verify_after_concmark_with_promotions(); + } else { + heap->verifier()->verify_after_concmark(); + } } if (VerifyAfterGC) { @@ -292,6 +374,10 @@ void ShenandoahDegenGC::op_prepare_evacuation() { } } +bool ShenandoahDegenGC::has_in_place_promotions(const ShenandoahHeap* heap) const { + return heap->mode()->is_generational() && heap->old_generation()->has_in_place_promotions(); +} + void ShenandoahDegenGC::op_cleanup_early() { ShenandoahHeap::heap()->recycle_trash(); } @@ -304,10 +390,6 @@ void ShenandoahDegenGC::op_evacuate() { void ShenandoahDegenGC::op_init_updaterefs() { // Evacuation has completed ShenandoahHeap* const heap = ShenandoahHeap::heap(); - heap->set_evacuation_in_progress(false); - heap->set_concurrent_weak_root_in_progress(false); - heap->set_concurrent_strong_root_in_progress(false); - heap->prepare_update_heap_references(false /*concurrent*/); heap->set_update_refs_in_progress(true); } @@ -356,18 +438,20 @@ void ShenandoahDegenGC::op_degenerated_futile() { const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) const { switch (point) { case _degenerated_unset: - return "Pause Degenerated GC ()"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " ()"); case _degenerated_outside_cycle: - return "Pause Degenerated GC (Outside of Cycle)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Outside of Cycle)"); + case _degenerated_roots: + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Roots)"); case _degenerated_mark: - return "Pause Degenerated GC (Mark)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Mark)"); case _degenerated_evac: - return "Pause Degenerated GC (Evacuation)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Evacuation)"); case _degenerated_updaterefs: - return "Pause Degenerated GC (Update Refs)"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Update Refs)"); default: ShouldNotReachHere(); - return "ERROR"; + SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (?)"); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp index 633c3f1ce73..8dc8ecea75f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp @@ -28,15 +28,17 @@ #include "gc/shenandoah/shenandoahGC.hpp" class VM_ShenandoahDegeneratedGC; +class ShenandoahGeneration; class ShenandoahDegenGC : public ShenandoahGC { friend class VM_ShenandoahDegeneratedGC; private: const ShenandoahDegenPoint _degen_point; + ShenandoahGeneration* _generation; bool _abbreviated; public: - ShenandoahDegenGC(ShenandoahDegenPoint degen_point); + ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation); bool collect(GCCause::Cause cause); private: @@ -64,6 +66,8 @@ class ShenandoahDegenGC : public ShenandoahGC { void upgrade_to_full(); const char* degen_event_message(ShenandoahDegenPoint point) const; + + bool has_in_place_promotions(const ShenandoahHeap* heap) const; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHDEGENERATEDGC_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp new file mode 100644 index 00000000000..5ca65763833 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHEVACINFO_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHEVACINFO_HPP + +#include "memory/allocation.hpp" + +class ShenandoahEvacuationInformation : public StackObj { + // Values for ShenandoahEvacuationInformation jfr event, sizes stored as bytes + size_t _collection_set_regions; + size_t _collection_set_used_before; + size_t _collection_set_used_after; + size_t _collected_old; + size_t _collected_promoted; + size_t _collected_young; + size_t _regions_promoted_humongous; + size_t _regions_promoted_regular; + size_t _regular_promoted_garbage; + size_t _regular_promoted_free; + size_t _regions_freed; + size_t _regions_immediate; + size_t _immediate_size; + +public: + ShenandoahEvacuationInformation() : + _collection_set_regions(0), _collection_set_used_before(0), _collection_set_used_after(0), + _collected_old(0), _collected_promoted(0), _collected_young(0), _regions_promoted_humongous(0), + _regions_promoted_regular(0), _regular_promoted_garbage(0), _regular_promoted_free(0), + _regions_freed(0), _regions_immediate(0), _immediate_size(0) { } + + void set_collection_set_regions(size_t collection_set_regions) { + _collection_set_regions = collection_set_regions; + } + + void set_collection_set_used_before(size_t used) { + _collection_set_used_before = used; + } + + void set_collection_set_used_after(size_t used) { + _collection_set_used_after = used; + } + + void set_collected_old(size_t collected) { + _collected_old = collected; + } + + void set_collected_promoted(size_t collected) { + _collected_promoted = collected; + } + + void set_collected_young(size_t collected) { + _collected_young = collected; + } + + void set_regions_freed(size_t freed) { + _regions_freed = freed; + } + + void set_regions_promoted_humongous(size_t humongous) { + _regions_promoted_humongous = humongous; + } + + void set_regions_promoted_regular(size_t regular) { + _regions_promoted_regular = regular; + } + + void set_regular_promoted_garbage(size_t garbage) { + _regular_promoted_garbage = garbage; + } + + void set_regular_promoted_free(size_t free) { + _regular_promoted_free = free; + } + + void set_regions_immediate(size_t immediate) { + _regions_immediate = immediate; + } + + void set_immediate_size(size_t size) { + _immediate_size = size; + } + + size_t collection_set_regions() { return _collection_set_regions; } + size_t collection_set_used_before() { return _collection_set_used_before; } + size_t collection_set_used_after() { return _collection_set_used_after; } + size_t collected_old() { return _collected_old; } + size_t collected_promoted() { return _collected_promoted; } + size_t collected_young() { return _collected_young; } + size_t regions_promoted_humongous() { return _regions_promoted_humongous; } + size_t regions_promoted_regular() { return _regions_promoted_regular; } + size_t regular_promoted_garbage() { return _regular_promoted_garbage; } + size_t regular_promoted_free() { return _regular_promoted_free; } + size_t regions_freed() { return _regions_freed; } + size_t regions_immediate() { return _regions_immediate; } + size_t immediate_size() { return _immediate_size; } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHEVACINFO_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp new file mode 100644 index 00000000000..ededb99b24e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp @@ -0,0 +1,173 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahAgeCensus.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahEvacTracker.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" +#include "runtime/threadSMR.inline.hpp" +#include "runtime/thread.hpp" + +ShenandoahEvacuationStats::ShenandoahEvacuationStats() + : _evacuations_completed(0), _bytes_completed(0), + _evacuations_attempted(0), _bytes_attempted(0), + _use_age_table(ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring), + _age_table(nullptr) { + if (_use_age_table) { + _age_table = new AgeTable(false); + } +} + +AgeTable* ShenandoahEvacuationStats::age_table() const { + assert(_use_age_table, "Don't call"); + return _age_table; +} + +void ShenandoahEvacuationStats::begin_evacuation(size_t bytes) { + ++_evacuations_attempted; + _bytes_attempted += bytes; +} + +void ShenandoahEvacuationStats::end_evacuation(size_t bytes) { + ++_evacuations_completed; + _bytes_completed += bytes; +} + +void ShenandoahEvacuationStats::record_age(size_t bytes, uint age) { + assert(_use_age_table, "Don't call!"); + if (age <= markWord::max_age) { // Filter age sentinel. + _age_table->add(age, bytes >> LogBytesPerWord); + } +} + +void ShenandoahEvacuationStats::accumulate(const ShenandoahEvacuationStats* other) { + _evacuations_completed += other->_evacuations_completed; + _bytes_completed += other->_bytes_completed; + _evacuations_attempted += other->_evacuations_attempted; + _bytes_attempted += other->_bytes_attempted; + if (_use_age_table) { + _age_table->merge(other->age_table()); + } +} + +void ShenandoahEvacuationStats::reset() { + _evacuations_completed = _evacuations_attempted = 0; + _bytes_completed = _bytes_attempted = 0; + if (_use_age_table) { + _age_table->clear(); + } +} + +void ShenandoahEvacuationStats::print_on(outputStream* st) { +#ifndef PRODUCT + size_t abandoned_size = _bytes_attempted - _bytes_completed; + size_t abandoned_count = _evacuations_attempted - _evacuations_completed; + st->print_cr("Evacuated " SIZE_FORMAT "%s across " SIZE_FORMAT " objects, " + "abandoned " SIZE_FORMAT "%s across " SIZE_FORMAT " objects.", + byte_size_in_proper_unit(_bytes_completed), proper_unit_for_byte_size(_bytes_completed), + _evacuations_completed, + byte_size_in_proper_unit(abandoned_size), proper_unit_for_byte_size(abandoned_size), + abandoned_count); +#endif + if (_use_age_table) { + _age_table->print_on(st); + } +} + +void ShenandoahEvacuationTracker::print_global_on(outputStream* st) { + print_evacuations_on(st, &_workers_global, &_mutators_global); +} + +void ShenandoahEvacuationTracker::print_evacuations_on(outputStream* st, + ShenandoahEvacuationStats* workers, + ShenandoahEvacuationStats* mutators) { + st->print("Workers: "); + workers->print_on(st); + st->cr(); + st->print("Mutators: "); + mutators->print_on(st); + st->cr(); + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + AgeTable young_region_ages(false); + for (uint i = 0; i < heap->num_regions(); ++i) { + ShenandoahHeapRegion* r = heap->get_region(i); + if (r->is_young()) { + young_region_ages.add(r->age(), r->get_live_data_words()); + } + } + st->print("Young regions: "); + young_region_ages.print_on(st); + st->cr(); +} + +class ShenandoahStatAggregator : public ThreadClosure { +public: + ShenandoahEvacuationStats* _target; + explicit ShenandoahStatAggregator(ShenandoahEvacuationStats* target) : _target(target) {} + void do_thread(Thread* thread) override { + ShenandoahEvacuationStats* local = ShenandoahThreadLocalData::evacuation_stats(thread); + _target->accumulate(local); + local->reset(); + } +}; + +ShenandoahCycleStats ShenandoahEvacuationTracker::flush_cycle_to_global() { + ShenandoahEvacuationStats mutators, workers; + + ThreadsListHandle java_threads_iterator; + ShenandoahStatAggregator aggregate_mutators(&mutators); + java_threads_iterator.list()->threads_do(&aggregate_mutators); + + ShenandoahStatAggregator aggregate_workers(&workers); + ShenandoahHeap::heap()->gc_threads_do(&aggregate_workers); + + _mutators_global.accumulate(&mutators); + _workers_global.accumulate(&workers); + + if (ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring) { + // Ingest mutator & worker collected population vectors into the heap's + // global census data, and use it to compute an appropriate tenuring threshold + // for use in the next cycle. + // The first argument is used for any age 0 cohort population that we may otherwise have + // missed during the census. This is non-zero only when census happens at marking. + ShenandoahGenerationalHeap::heap()->age_census()->update_census(0, _mutators_global.age_table(), _workers_global.age_table()); + } + + return {workers, mutators}; +} + +void ShenandoahEvacuationTracker::begin_evacuation(Thread* thread, size_t bytes) { + ShenandoahThreadLocalData::begin_evacuation(thread, bytes); +} + +void ShenandoahEvacuationTracker::end_evacuation(Thread* thread, size_t bytes) { + ShenandoahThreadLocalData::end_evacuation(thread, bytes); +} + +void ShenandoahEvacuationTracker::record_age(Thread* thread, size_t bytes, uint age) { + ShenandoahThreadLocalData::record_age(thread, bytes, age); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp new file mode 100644 index 00000000000..7d195656b11 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHEVACTRACKER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHEVACTRACKER_HPP + +#include "gc/shared/ageTable.hpp" +#include "utilities/ostream.hpp" + +class ShenandoahEvacuationStats : public CHeapObj { +private: + size_t _evacuations_completed; + size_t _bytes_completed; + size_t _evacuations_attempted; + size_t _bytes_attempted; + + bool _use_age_table; + AgeTable* _age_table; + + public: + ShenandoahEvacuationStats(); + + AgeTable* age_table() const; + + void begin_evacuation(size_t bytes); + void end_evacuation(size_t bytes); + void record_age(size_t bytes, uint age); + + void print_on(outputStream* st); + void accumulate(const ShenandoahEvacuationStats* other); + void reset(); +}; + +struct ShenandoahCycleStats { + ShenandoahEvacuationStats workers; + ShenandoahEvacuationStats mutators; +}; + +class ShenandoahEvacuationTracker : public CHeapObj { +private: + + ShenandoahEvacuationStats _workers_global; + ShenandoahEvacuationStats _mutators_global; + +public: + ShenandoahEvacuationTracker() = default; + + void begin_evacuation(Thread* thread, size_t bytes); + void end_evacuation(Thread* thread, size_t bytes); + void record_age(Thread* thread, size_t bytes, uint age); + + void print_global_on(outputStream* st); + void print_evacuations_on(outputStream* st, + ShenandoahEvacuationStats* workers, + ShenandoahEvacuationStats* mutators); + + ShenandoahCycleStats flush_cycle_to_global(); +}; + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHEVACTRACKER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 310cd5b8061..1a29fbbc4a7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -25,10 +25,13 @@ #include "precompiled.hpp" #include "gc/shared/tlab_globals.hpp" +#include "gc/shenandoah/shenandoahAffiliation.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.inline.hpp" #include "logging/logStream.hpp" @@ -40,27 +43,96 @@ static const char* partition_name(ShenandoahFreeSetPartitionId t) { case ShenandoahFreeSetPartitionId::NotFree: return "NotFree"; case ShenandoahFreeSetPartitionId::Mutator: return "Mutator"; case ShenandoahFreeSetPartitionId::Collector: return "Collector"; + case ShenandoahFreeSetPartitionId::OldCollector: return "OldCollector"; default: ShouldNotReachHere(); return "Unrecognized"; } } +class ShenandoahLeftRightIterator { +private: + idx_t _idx; + idx_t _end; + ShenandoahRegionPartitions* _partitions; + ShenandoahFreeSetPartitionId _partition; +public: + explicit ShenandoahLeftRightIterator(ShenandoahRegionPartitions* partitions, ShenandoahFreeSetPartitionId partition, bool use_empty = false) + : _idx(0), _end(0), _partitions(partitions), _partition(partition) { + _idx = use_empty ? _partitions->leftmost_empty(_partition) : _partitions->leftmost(_partition); + _end = use_empty ? _partitions->rightmost_empty(_partition) : _partitions->rightmost(_partition); + } + + bool has_next() const { + if (_idx <= _end) { + assert(_partitions->in_free_set(_partition, _idx), "Boundaries or find_last_set_bit failed: " SSIZE_FORMAT, _idx); + return true; + } + return false; + } + + idx_t current() const { + return _idx; + } + + idx_t next() { + _idx = _partitions->find_index_of_next_available_region(_partition, _idx + 1); + return current(); + } +}; + +class ShenandoahRightLeftIterator { +private: + idx_t _idx; + idx_t _end; + ShenandoahRegionPartitions* _partitions; + ShenandoahFreeSetPartitionId _partition; +public: + explicit ShenandoahRightLeftIterator(ShenandoahRegionPartitions* partitions, ShenandoahFreeSetPartitionId partition, bool use_empty = false) + : _idx(0), _end(0), _partitions(partitions), _partition(partition) { + _idx = use_empty ? _partitions->rightmost_empty(_partition) : _partitions->rightmost(_partition); + _end = use_empty ? _partitions->leftmost_empty(_partition) : _partitions->leftmost(_partition); + } + + bool has_next() const { + if (_idx >= _end) { + assert(_partitions->in_free_set(_partition, _idx), "Boundaries or find_last_set_bit failed: " SSIZE_FORMAT, _idx); + return true; + } + return false; + } + + idx_t current() const { + return _idx; + } + + idx_t next() { + _idx = _partitions->find_index_of_previous_available_region(_partition, _idx - 1); + return current(); + } +}; + #ifndef PRODUCT void ShenandoahRegionPartitions::dump_bitmap() const { - log_debug(gc)("Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "], Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", - _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)], - _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)], - _leftmosts[int(ShenandoahFreeSetPartitionId::Collector)], - _rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]); + log_debug(gc)("Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "], Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT + "], Old Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)], + _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)], + _leftmosts[int(ShenandoahFreeSetPartitionId::Collector)], + _rightmosts[int(ShenandoahFreeSetPartitionId::Collector)], + _leftmosts[int(ShenandoahFreeSetPartitionId::OldCollector)], + _rightmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]); log_debug(gc)("Empty Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT - "], Empty Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", - _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], - _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], - _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], - _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); - - log_debug(gc)("%6s: %18s %18s %18s", "index", "Mutator Bits", "Collector Bits", "NotFree Bits"); + "], Empty Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT + "], Empty Old Collecto range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)]); + + log_debug(gc)("%6s: %18s %18s %18s %18s", "index", "Mutator Bits", "Collector Bits", "Old Collector Bits", "NotFree Bits"); dump_bitmap_range(0, _max-1); } @@ -81,10 +153,11 @@ void ShenandoahRegionPartitions::dump_bitmap_row(idx_t region_idx) const { idx_t aligned_idx = _membership[int(ShenandoahFreeSetPartitionId::Mutator)].aligned_index(region_idx); uintx mutator_bits = _membership[int(ShenandoahFreeSetPartitionId::Mutator)].bits_at(aligned_idx); uintx collector_bits = _membership[int(ShenandoahFreeSetPartitionId::Collector)].bits_at(aligned_idx); - uintx free_bits = mutator_bits | collector_bits; + uintx old_collector_bits = _membership[int(ShenandoahFreeSetPartitionId::OldCollector)].bits_at(aligned_idx); + uintx free_bits = mutator_bits | collector_bits | old_collector_bits; uintx notfree_bits = ~free_bits; - log_debug(gc)(SSIZE_FORMAT_W(6) ": " SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0, - aligned_idx, mutator_bits, collector_bits, notfree_bits); + log_debug(gc)(SSIZE_FORMAT_W(6) ": " SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0, + aligned_idx, mutator_bits, collector_bits, old_collector_bits, notfree_bits); } #endif @@ -92,7 +165,7 @@ ShenandoahRegionPartitions::ShenandoahRegionPartitions(size_t max_regions, Shena _max(max_regions), _region_size_bytes(ShenandoahHeapRegion::region_size_bytes()), _free_set(free_set), - _membership{ ShenandoahSimpleBitMap(max_regions), ShenandoahSimpleBitMap(max_regions) } + _membership{ ShenandoahSimpleBitMap(max_regions), ShenandoahSimpleBitMap(max_regions) , ShenandoahSimpleBitMap(max_regions) } { make_all_regions_unavailable(); } @@ -162,7 +235,6 @@ void ShenandoahRegionPartitions::make_all_regions_unavailable() { void ShenandoahRegionPartitions::establish_mutator_intervals(idx_t mutator_leftmost, idx_t mutator_rightmost, idx_t mutator_leftmost_empty, idx_t mutator_rightmost_empty, size_t mutator_region_count, size_t mutator_used) { - _region_counts[int(ShenandoahFreeSetPartitionId::Mutator)] = mutator_region_count; _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)] = mutator_leftmost; _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)] = mutator_rightmost; _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)] = mutator_leftmost_empty; @@ -182,6 +254,20 @@ void ShenandoahRegionPartitions::establish_mutator_intervals(idx_t mutator_leftm _capacity[int(ShenandoahFreeSetPartitionId::Collector)] = 0; } +void ShenandoahRegionPartitions::establish_old_collector_intervals(idx_t old_collector_leftmost, idx_t old_collector_rightmost, + idx_t old_collector_leftmost_empty, + idx_t old_collector_rightmost_empty, + size_t old_collector_region_count, size_t old_collector_used) { + _leftmosts[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_leftmost; + _rightmosts[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_rightmost; + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_leftmost_empty; + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_rightmost_empty; + + _region_counts[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_region_count; + _used[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_used; + _capacity[int(ShenandoahFreeSetPartitionId::OldCollector)] = old_collector_region_count * _region_size_bytes; +} + void ShenandoahRegionPartitions::increase_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes) { assert (which_partition < NumPartitions, "Partition must be valid"); _used[int(which_partition)] += bytes; @@ -202,7 +288,7 @@ inline void ShenandoahRegionPartitions::shrink_interval_if_range_modifies_either } if (_leftmosts_empty[int(partition)] < _leftmosts[int(partition)]) { // This gets us closer to where we need to be; we'll scan further when leftmosts_empty is requested. - _leftmosts_empty[int(partition)] = leftmost(partition); + _leftmosts_empty[int(partition)] = _leftmosts[int(partition)]; } } if (high_idx == _rightmosts[int(partition)]) { @@ -289,31 +375,61 @@ void ShenandoahRegionPartitions::make_free(idx_t idx, ShenandoahFreeSetPartition _capacity[int(which_partition)] += _region_size_bytes; _used[int(which_partition)] += _region_size_bytes - available; expand_interval_if_boundary_modified(which_partition, idx, available); - _region_counts[int(which_partition)]++; } +bool ShenandoahRegionPartitions::is_mutator_partition(ShenandoahFreeSetPartitionId p) { + return (p == ShenandoahFreeSetPartitionId::Mutator); +} + +bool ShenandoahRegionPartitions::is_young_collector_partition(ShenandoahFreeSetPartitionId p) { + return (p == ShenandoahFreeSetPartitionId::Collector); +} + +bool ShenandoahRegionPartitions::is_old_collector_partition(ShenandoahFreeSetPartitionId p) { + return (p == ShenandoahFreeSetPartitionId::OldCollector); +} + +bool ShenandoahRegionPartitions::available_implies_empty(size_t available_in_region) { + return (available_in_region == _region_size_bytes); +} + + void ShenandoahRegionPartitions::move_from_partition_to_partition(idx_t idx, ShenandoahFreeSetPartitionId orig_partition, ShenandoahFreeSetPartitionId new_partition, size_t available) { + ShenandoahHeapRegion* r = ShenandoahHeap::heap()->get_region(idx); assert (idx < _max, "index is sane: " SIZE_FORMAT " < " SIZE_FORMAT, idx, _max); assert (orig_partition < NumPartitions, "Original partition must be valid"); assert (new_partition < NumPartitions, "New partition must be valid"); assert (available <= _region_size_bytes, "Available cannot exceed region size"); + assert (_membership[int(orig_partition)].is_set(idx), "Cannot move from partition unless in partition"); + assert ((r != nullptr) && ((r->is_trash() && (available == _region_size_bytes)) || + (r->used() + available == _region_size_bytes)), + "Used: " SIZE_FORMAT " + available: " SIZE_FORMAT " should equal region size: " SIZE_FORMAT, + ShenandoahHeap::heap()->get_region(idx)->used(), available, _region_size_bytes); // Expected transitions: // During rebuild: Mutator => Collector + // Mutator empty => Collector + // Mutator empty => OldCollector // During flip_to_gc: Mutator empty => Collector + // Mutator empty => OldCollector // At start of update refs: Collector => Mutator - assert (((available <= _region_size_bytes) && - (((orig_partition == ShenandoahFreeSetPartitionId::Mutator) - && (new_partition == ShenandoahFreeSetPartitionId::Collector)) || - ((orig_partition == ShenandoahFreeSetPartitionId::Collector) - && (new_partition == ShenandoahFreeSetPartitionId::Mutator)))) || - ((available == _region_size_bytes) && - ((orig_partition == ShenandoahFreeSetPartitionId::Mutator) - && (new_partition == ShenandoahFreeSetPartitionId::Collector))), "Unexpected movement between partitions"); + // OldCollector Empty => Mutator + assert ((is_mutator_partition(orig_partition) && is_young_collector_partition(new_partition)) || + (is_mutator_partition(orig_partition) && + available_implies_empty(available) && is_old_collector_partition(new_partition)) || + (is_young_collector_partition(orig_partition) && is_mutator_partition(new_partition)) || + (is_old_collector_partition(orig_partition) + && available_implies_empty(available) && is_mutator_partition(new_partition)), + "Unexpected movement between partitions, available: " SIZE_FORMAT ", _region_size_bytes: " SIZE_FORMAT + ", orig_partition: %s, new_partition: %s", + available, _region_size_bytes, partition_name(orig_partition), partition_name(new_partition)); size_t used = _region_size_bytes - available; + assert (_used[int(orig_partition)] >= used, + "Orig partition used: " SIZE_FORMAT " must exceed moved used: " SIZE_FORMAT " within region " SSIZE_FORMAT, + _used[int(orig_partition)], used, idx); _membership[int(orig_partition)].clear_bit(idx); _membership[int(new_partition)].set_bit(idx); @@ -482,6 +598,7 @@ void ShenandoahRegionPartitions::assert_bounds() { case ShenandoahFreeSetPartitionId::Mutator: case ShenandoahFreeSetPartitionId::Collector: + case ShenandoahFreeSetPartitionId::OldCollector: { size_t capacity = _free_set->alloc_capacity(i); bool is_empty = (capacity == _region_size_bytes); @@ -571,6 +688,41 @@ void ShenandoahRegionPartitions::assert_bounds() { assert (end_off <= _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], "free empty regions past the rightmost: " SSIZE_FORMAT ", bound " SSIZE_FORMAT, end_off, rightmost_empty(ShenandoahFreeSetPartitionId::Collector)); + + // Performance invariants. Failing these would not break the free partition, but performance would suffer. + assert (leftmost(ShenandoahFreeSetPartitionId::OldCollector) <= _max, "leftmost in bounds: " SSIZE_FORMAT " < " SSIZE_FORMAT, + leftmost(ShenandoahFreeSetPartitionId::OldCollector), _max); + assert (rightmost(ShenandoahFreeSetPartitionId::OldCollector) < _max, "rightmost in bounds: " SSIZE_FORMAT " < " SSIZE_FORMAT, + rightmost(ShenandoahFreeSetPartitionId::OldCollector), _max); + + assert (leftmost(ShenandoahFreeSetPartitionId::OldCollector) == _max + || partition_id_matches(leftmost(ShenandoahFreeSetPartitionId::OldCollector), + ShenandoahFreeSetPartitionId::OldCollector), + "leftmost region should be free: " SSIZE_FORMAT, leftmost(ShenandoahFreeSetPartitionId::OldCollector)); + assert (leftmost(ShenandoahFreeSetPartitionId::OldCollector) == _max + || partition_id_matches(rightmost(ShenandoahFreeSetPartitionId::OldCollector), + ShenandoahFreeSetPartitionId::OldCollector), + "rightmost region should be free: " SSIZE_FORMAT, rightmost(ShenandoahFreeSetPartitionId::OldCollector)); + + // If OldCollector partition is empty, leftmosts will both equal max, rightmosts will both equal zero. + // Likewise for empty region partitions. + beg_off = leftmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; + end_off = rightmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; + assert (beg_off >= leftmost(ShenandoahFreeSetPartitionId::OldCollector), + "free regions before the leftmost: " SSIZE_FORMAT ", bound " SSIZE_FORMAT, + beg_off, leftmost(ShenandoahFreeSetPartitionId::OldCollector)); + assert (end_off <= rightmost(ShenandoahFreeSetPartitionId::OldCollector), + "free regions past the rightmost: " SSIZE_FORMAT ", bound " SSIZE_FORMAT, + end_off, rightmost(ShenandoahFreeSetPartitionId::OldCollector)); + + beg_off = empty_leftmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; + end_off = empty_rightmosts[int(ShenandoahFreeSetPartitionId::OldCollector)]; + assert (beg_off >= _leftmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], + "free empty regions before the leftmost: " SSIZE_FORMAT ", bound " SSIZE_FORMAT, + beg_off, leftmost_empty(ShenandoahFreeSetPartitionId::OldCollector)); + assert (end_off <= _rightmosts_empty[int(ShenandoahFreeSetPartitionId::OldCollector)], + "free empty regions past the rightmost: " SSIZE_FORMAT ", bound " SSIZE_FORMAT, + end_off, rightmost_empty(ShenandoahFreeSetPartitionId::OldCollector)); } #endif @@ -578,150 +730,277 @@ ShenandoahFreeSet::ShenandoahFreeSet(ShenandoahHeap* heap, size_t max_regions) : _heap(heap), _partitions(max_regions, this), _trash_regions(NEW_C_HEAP_ARRAY(ShenandoahHeapRegion*, max_regions, mtGC)), - _right_to_left_bias(false), _alloc_bias_weight(0) { clear_internal(); } +void ShenandoahFreeSet::add_promoted_in_place_region_to_old_collector(ShenandoahHeapRegion* region) { + shenandoah_assert_heaplocked(); + size_t plab_min_size_in_bytes = ShenandoahGenerationalHeap::heap()->plab_min_size() * HeapWordSize; + size_t idx = region->index(); + size_t capacity = alloc_capacity(region); + assert(_partitions.membership(idx) == ShenandoahFreeSetPartitionId::NotFree, + "Regions promoted in place should have been excluded from Mutator partition"); + if (capacity >= plab_min_size_in_bytes) { + _partitions.make_free(idx, ShenandoahFreeSetPartitionId::OldCollector, capacity); + _heap->old_generation()->augment_promoted_reserve(capacity); + } +} + +HeapWord* ShenandoahFreeSet::allocate_from_partition_with_affiliation(ShenandoahAffiliation affiliation, + ShenandoahAllocRequest& req, bool& in_new_region) { + + shenandoah_assert_heaplocked(); + ShenandoahFreeSetPartitionId which_partition = req.is_old()? ShenandoahFreeSetPartitionId::OldCollector: ShenandoahFreeSetPartitionId::Collector; + if (_partitions.alloc_from_left_bias(which_partition)) { + ShenandoahLeftRightIterator iterator(&_partitions, which_partition, affiliation == ShenandoahAffiliation::FREE); + return allocate_with_affiliation(iterator, affiliation, req, in_new_region); + } else { + ShenandoahRightLeftIterator iterator(&_partitions, which_partition, affiliation == ShenandoahAffiliation::FREE); + return allocate_with_affiliation(iterator, affiliation, req, in_new_region); + } +} + +template +HeapWord* ShenandoahFreeSet::allocate_with_affiliation(Iter& iterator, ShenandoahAffiliation affiliation, ShenandoahAllocRequest& req, bool& in_new_region) { + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + if (r->affiliation() == affiliation) { + HeapWord* result = try_allocate_in(r, req, in_new_region); + if (result != nullptr) { + return result; + } + } + } + log_debug(gc, free)("Could not allocate collector region with affiliation: %s for request " PTR_FORMAT, + shenandoah_affiliation_name(affiliation), p2i(&req)); + return nullptr; +} + HeapWord* ShenandoahFreeSet::allocate_single(ShenandoahAllocRequest& req, bool& in_new_region) { shenandoah_assert_heaplocked(); // Scan the bitmap looking for a first fit. // - // Leftmost and rightmost bounds provide enough caching to quickly find a region from which to allocate. + // Leftmost and rightmost bounds provide enough caching to walk bitmap efficiently. Normally, + // we would find the region to allocate at right away. // // Allocations are biased: GC allocations are taken from the high end of the heap. Regular (and TLAB) // mutator allocations are taken from the middle of heap, below the memory reserved for Collector. // Humongous mutator allocations are taken from the bottom of the heap. // - // Free set maintains mutator and collector partitions. Mutator can only allocate from the - // Mutator partition. Collector prefers to allocate from the Collector partition, but may steal - // regions from the Mutator partition if the Collector partition has been depleted. + // Free set maintains mutator and collector partitions. Normally, each allocates only from its partition, + // except in special cases when the collector steals regions from the mutator partition. + + // Overwrite with non-zero (non-NULL) values only if necessary for allocation bookkeeping. switch (req.type()) { case ShenandoahAllocRequest::_alloc_tlab: - case ShenandoahAllocRequest::_alloc_shared: { - // Try to allocate in the mutator view - if (_alloc_bias_weight-- <= 0) { - // We have observed that regions not collected in previous GC cycle tend to congregate at one end or the other - // of the heap. Typically, these are the more recently engaged regions and the objects in these regions have not - // yet had a chance to die (and/or are treated as floating garbage). If we use the same allocation bias on each - // GC pass, these "most recently" engaged regions for GC pass N will also be the "most recently" engaged regions - // for GC pass N+1, and the relatively large amount of live data and/or floating garbage introduced - // during the most recent GC pass may once again prevent the region from being collected. We have found that - // alternating the allocation behavior between GC passes improves evacuation performance by 3-7% on certain - // benchmarks. In the best case, this has the effect of consuming these partially consumed regions before - // the start of the next mark cycle so all of their garbage can be efficiently reclaimed. - // - // First, finish consuming regions that are already partially consumed so as to more tightly limit ranges of - // available regions. Other potential benefits: - // 1. Eventual collection set has fewer regions because we have packed newly allocated objects into fewer regions - // 2. We preserve the "empty" regions longer into the GC cycle, reducing likelihood of allocation failures - // late in the GC cycle. - idx_t non_empty_on_left = (_partitions.leftmost_empty(ShenandoahFreeSetPartitionId::Mutator) - - _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator)); - idx_t non_empty_on_right = (_partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator) - - _partitions.rightmost_empty(ShenandoahFreeSetPartitionId::Mutator)); - _right_to_left_bias = (non_empty_on_right > non_empty_on_left); - _alloc_bias_weight = _InitialAllocBiasWeight; - } - if (_right_to_left_bias) { - // Allocate within mutator free from high memory to low so as to preserve low memory for humongous allocations - if (!_partitions.is_empty(ShenandoahFreeSetPartitionId::Mutator)) { - // Use signed idx. Otherwise, loop will never terminate. - idx_t leftmost = _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator); - for (idx_t idx = _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator); idx >= leftmost; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, idx), - "Boundaries or find_last_set_bit failed: " SSIZE_FORMAT, idx); - ShenandoahHeapRegion* r = _heap->get_region(idx); - // try_allocate_in() increases used if the allocation is successful. - HeapWord* result; - size_t min_size = (req.type() == ShenandoahAllocRequest::_alloc_tlab)? req.min_size(): req.size(); - if ((alloc_capacity(r) >= min_size) && ((result = try_allocate_in(r, req, in_new_region)) != nullptr)) { - return result; - } - idx = _partitions.find_index_of_previous_available_region(ShenandoahFreeSetPartitionId::Mutator, idx - 1); - } - } - } else { - // Allocate from low to high memory. This keeps the range of fully empty regions more tightly packed. - // Note that the most recently allocated regions tend not to be evacuated in a given GC cycle. So this - // tends to accumulate "fragmented" uncollected regions in high memory. - if (!_partitions.is_empty(ShenandoahFreeSetPartitionId::Mutator)) { - // Use signed idx. Otherwise, loop will never terminate. - idx_t rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator); - for (idx_t idx = _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator); idx <= rightmost; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, idx), - "Boundaries or find_last_set_bit failed: " SSIZE_FORMAT, idx); - ShenandoahHeapRegion* r = _heap->get_region(idx); - // try_allocate_in() increases used if the allocation is successful. - HeapWord* result; - size_t min_size = (req.type() == ShenandoahAllocRequest::_alloc_tlab)? req.min_size(): req.size(); - if ((alloc_capacity(r) >= min_size) && ((result = try_allocate_in(r, req, in_new_region)) != nullptr)) { - return result; - } - idx = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Mutator, idx + 1); - } - } - } - // There is no recovery. Mutator does not touch collector view at all. - break; - } + case ShenandoahAllocRequest::_alloc_shared: + return allocate_for_mutator(req, in_new_region); case ShenandoahAllocRequest::_alloc_gclab: - // GCLABs are for evacuation so we must be in evacuation phase. - - case ShenandoahAllocRequest::_alloc_shared_gc: { - // Fast-path: try to allocate in the collector view first - idx_t leftmost_collector = _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector); - for (idx_t idx = _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector); idx >= leftmost_collector; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Collector, idx), - "Boundaries or find_prev_last_bit failed: " SSIZE_FORMAT, idx); - HeapWord* result = try_allocate_in(_heap->get_region(idx), req, in_new_region); - if (result != nullptr) { - return result; - } - idx = _partitions.find_index_of_previous_available_region(ShenandoahFreeSetPartitionId::Collector, idx - 1); - } + case ShenandoahAllocRequest::_alloc_plab: + case ShenandoahAllocRequest::_alloc_shared_gc: + return allocate_for_collector(req, in_new_region); + default: + ShouldNotReachHere(); + } + return nullptr; +} - // No dice. Can we borrow space from mutator view? - if (!ShenandoahEvacReserveOverflow) { - return nullptr; - } +HeapWord* ShenandoahFreeSet::allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region) { + update_allocation_bias(); - // Try to steal an empty region from the mutator view. - idx_t leftmost_mutator_empty = _partitions.leftmost_empty(ShenandoahFreeSetPartitionId::Mutator); - for (idx_t idx = _partitions.rightmost_empty(ShenandoahFreeSetPartitionId::Mutator); idx >= leftmost_mutator_empty; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, idx), - "Boundaries or find_prev_last_bit failed: " SSIZE_FORMAT, idx); - ShenandoahHeapRegion* r = _heap->get_region(idx); - if (can_allocate_from(r)) { - flip_to_gc(r); - HeapWord *result = try_allocate_in(r, req, in_new_region); - if (result != nullptr) { - log_debug(gc)("Flipped region " SIZE_FORMAT " to gc for request: " PTR_FORMAT, idx, p2i(&req)); - return result; - } - } - idx = _partitions.find_index_of_previous_available_region(ShenandoahFreeSetPartitionId::Mutator, idx - 1); + if (_partitions.is_empty(ShenandoahFreeSetPartitionId::Mutator)) { + // There is no recovery. Mutator does not touch collector view at all. + return nullptr; + } + + // Try to allocate in the mutator view + if (_partitions.alloc_from_left_bias(ShenandoahFreeSetPartitionId::Mutator)) { + // Allocate from low to high memory. This keeps the range of fully empty regions more tightly packed. + // Note that the most recently allocated regions tend not to be evacuated in a given GC cycle. So this + // tends to accumulate "fragmented" uncollected regions in high memory. + ShenandoahLeftRightIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); + return allocate_from_regions(iterator, req, in_new_region); + } + + // Allocate from high to low memory. This preserves low memory for humongous allocations. + ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); + return allocate_from_regions(iterator, req, in_new_region); +} + +void ShenandoahFreeSet::update_allocation_bias() { + if (_alloc_bias_weight-- <= 0) { + // We have observed that regions not collected in previous GC cycle tend to congregate at one end or the other + // of the heap. Typically, these are the more recently engaged regions and the objects in these regions have not + // yet had a chance to die (and/or are treated as floating garbage). If we use the same allocation bias on each + // GC pass, these "most recently" engaged regions for GC pass N will also be the "most recently" engaged regions + // for GC pass N+1, and the relatively large amount of live data and/or floating garbage introduced + // during the most recent GC pass may once again prevent the region from being collected. We have found that + // alternating the allocation behavior between GC passes improves evacuation performance by 3-7% on certain + // benchmarks. In the best case, this has the effect of consuming these partially consumed regions before + // the start of the next mark cycle so all of their garbage can be efficiently reclaimed. + // + // First, finish consuming regions that are already partially consumed so as to more tightly limit ranges of + // available regions. Other potential benefits: + // 1. Eventual collection set has fewer regions because we have packed newly allocated objects into fewer regions + // 2. We preserve the "empty" regions longer into the GC cycle, reducing likelihood of allocation failures + // late in the GC cycle. + idx_t non_empty_on_left = (_partitions.leftmost_empty(ShenandoahFreeSetPartitionId::Mutator) + - _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator)); + idx_t non_empty_on_right = (_partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator) + - _partitions.rightmost_empty(ShenandoahFreeSetPartitionId::Mutator)); + _partitions.set_bias_from_left_to_right(ShenandoahFreeSetPartitionId::Mutator, (non_empty_on_right < non_empty_on_left)); + _alloc_bias_weight = INITIAL_ALLOC_BIAS_WEIGHT; + } +} + +template +HeapWord* ShenandoahFreeSet::allocate_from_regions(Iter& iterator, ShenandoahAllocRequest &req, bool &in_new_region) { + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + size_t min_size = (req.type() == ShenandoahAllocRequest::_alloc_tlab) ? req.min_size() : req.size(); + if (alloc_capacity(r) >= min_size) { + HeapWord* result = try_allocate_in(r, req, in_new_region); + if (result != nullptr) { + return result; } + } + } + return nullptr; +} - // No dice. Do not try to mix mutator and GC allocations, because adjusting region UWM - // due to GC allocations would expose unparsable mutator allocations. - break; +HeapWord* ShenandoahFreeSet::allocate_for_collector(ShenandoahAllocRequest &req, bool &in_new_region) { + // Fast-path: try to allocate in the collector view first + HeapWord* result; + result = allocate_from_partition_with_affiliation(req.affiliation(), req, in_new_region); + if (result != nullptr) { + return result; + } + + bool allow_new_region = can_allocate_in_new_region(req); + if (allow_new_region) { + // Try a free region that is dedicated to GC allocations. + result = allocate_from_partition_with_affiliation(ShenandoahAffiliation::FREE, req, in_new_region); + if (result != nullptr) { + return result; + } + } + + // No dice. Can we borrow space from mutator view? + if (!ShenandoahEvacReserveOverflow) { + return nullptr; + } + + if (!allow_new_region && req.is_old() && (_heap->young_generation()->free_unaffiliated_regions() > 0)) { + // This allows us to flip a mutator region to old_collector + allow_new_region = true; + } + + // We should expand old-gen if this can prevent an old-gen evacuation failure. We don't care so much about + // promotion failures since they can be mitigated in a subsequent GC pass. Would be nice to know if this + // allocation request is for evacuation or promotion. Individual threads limit their use of PLAB memory for + // promotions, so we already have an assurance that any additional memory set aside for old-gen will be used + // only for old-gen evacuations. + if (allow_new_region) { + // Try to steal an empty region from the mutator view. + result = try_allocate_from_mutator(req, in_new_region); + } + + // This is it. Do not try to mix mutator and GC allocations, because adjusting region UWM + // due to GC allocations would expose unparsable mutator allocations. + return result; +} + +bool ShenandoahFreeSet::can_allocate_in_new_region(const ShenandoahAllocRequest& req) { + if (!_heap->mode()->is_generational()) { + return true; + } + + assert(req.is_old() || req.is_young(), "Should request affiliation"); + return (req.is_old() && _heap->old_generation()->free_unaffiliated_regions() > 0) + || (req.is_young() && _heap->young_generation()->free_unaffiliated_regions() > 0); +} + +HeapWord* ShenandoahFreeSet::try_allocate_from_mutator(ShenandoahAllocRequest& req, bool& in_new_region) { + // The collector prefers to keep longer lived regions toward the right side of the heap, so it always + // searches for regions from right to left here. + ShenandoahRightLeftIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator, true); + for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) { + ShenandoahHeapRegion* r = _heap->get_region(idx); + if (can_allocate_from(r)) { + if (req.is_old()) { + flip_to_old_gc(r); + } else { + flip_to_gc(r); + } + // Region r is entirely empty. If try_allocate_in fails on region r, something else is really wrong. + // Don't bother to retry with other regions. + log_debug(gc, free)("Flipped region " SIZE_FORMAT " to gc for request: " PTR_FORMAT, idx, p2i(&req)); + return try_allocate_in(r, req, in_new_region); } - default: - ShouldNotReachHere(); } + return nullptr; } +// This work method takes an argument corresponding to the number of bytes +// free in a region, and returns the largest amount in heapwords that can be allocated +// such that both of the following conditions are satisfied: +// +// 1. it is a multiple of card size +// 2. any remaining shard may be filled with a filler object +// +// The idea is that the allocation starts and ends at card boundaries. Because +// a region ('s end) is card-aligned, the remainder shard that must be filled is +// at the start of the free space. +// +// This is merely a helper method to use for the purpose of such a calculation. +size_t ShenandoahFreeSet::get_usable_free_words(size_t free_bytes) const { + // e.g. card_size is 512, card_shift is 9, min_fill_size() is 8 + // free is 514 + // usable_free is 512, which is decreased to 0 + size_t usable_free = (free_bytes / CardTable::card_size()) << CardTable::card_shift(); + assert(usable_free <= free_bytes, "Sanity check"); + if ((free_bytes != usable_free) && (free_bytes - usable_free < ShenandoahHeap::min_fill_size() * HeapWordSize)) { + // After aligning to card multiples, the remainder would be smaller than + // the minimum filler object, so we'll need to take away another card's + // worth to construct a filler object. + if (usable_free >= CardTable::card_size()) { + usable_free -= CardTable::card_size(); + } else { + assert(usable_free == 0, "usable_free is a multiple of card_size and card_size > min_fill_size"); + } + } + + return usable_free / HeapWordSize; +} + +// Given a size argument, which is a multiple of card size, a request struct +// for a PLAB, and an old region, return a pointer to the allocated space for +// a PLAB which is card-aligned and where any remaining shard in the region +// has been suitably filled by a filler object. +// It is assumed (and assertion-checked) that such an allocation is always possible. +HeapWord* ShenandoahFreeSet::allocate_aligned_plab(size_t size, ShenandoahAllocRequest& req, ShenandoahHeapRegion* r) { + assert(_heap->mode()->is_generational(), "PLABs are only for generational mode"); + assert(r->is_old(), "All PLABs reside in old-gen"); + assert(!req.is_mutator_alloc(), "PLABs should not be allocated by mutators."); + assert(is_aligned(size, CardTable::card_size_in_words()), "Align by design"); + + HeapWord* result = r->allocate_aligned(size, req, CardTable::card_size()); + assert(result != nullptr, "Allocation cannot fail"); + assert(r->top() <= r->end(), "Allocation cannot span end of region"); + assert(is_aligned(result, CardTable::card_size_in_words()), "Align by design"); + return result; +} + HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, ShenandoahAllocRequest& req, bool& in_new_region) { assert (has_alloc_capacity(r), "Performance: should avoid full regions on this path: " SIZE_FORMAT, r->index()); if (_heap->is_concurrent_weak_root_in_progress() && r->is_trash()) { return nullptr; } - HeapWord* result = nullptr; try_recycle_trashed(r); in_new_region = r->is_empty(); @@ -729,37 +1008,83 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah if (in_new_region) { log_debug(gc)("Using new region (" SIZE_FORMAT ") for %s (" PTR_FORMAT ").", r->index(), ShenandoahAllocRequest::alloc_type_to_string(req.type()), p2i(&req)); + assert(!r->is_affiliated(), "New region " SIZE_FORMAT " should be unaffiliated", r->index()); + r->set_affiliation(req.affiliation()); + if (r->is_old()) { + // Any OLD region allocated during concurrent coalesce-and-fill does not need to be coalesced and filled because + // all objects allocated within this region are above TAMS (and thus are implicitly marked). In case this is an + // OLD region and concurrent preparation for mixed evacuations visits this region before the start of the next + // old-gen concurrent mark (i.e. this region is allocated following the start of old-gen concurrent mark but before + // concurrent preparations for mixed evacuations are completed), we mark this region as not requiring any + // coalesce-and-fill processing. + r->end_preemptible_coalesce_and_fill(); + _heap->old_generation()->clear_cards_for(r); + } + _heap->generation_for(r->affiliation())->increment_affiliated_region_count(); + +#ifdef ASSERT + ShenandoahMarkingContext* const ctx = _heap->complete_marking_context(); + assert(ctx->top_at_mark_start(r) == r->bottom(), "Newly established allocation region starts with TAMS equal to bottom"); + assert(ctx->is_bitmap_range_within_region_clear(ctx->top_bitmap(r), r->end()), "Bitmap above top_bitmap() must be clear"); +#endif + log_debug(gc)("Using new region (" SIZE_FORMAT ") for %s (" PTR_FORMAT ").", + r->index(), ShenandoahAllocRequest::alloc_type_to_string(req.type()), p2i(&req)); + } else { + assert(r->is_affiliated(), "Region " SIZE_FORMAT " that is not new should be affiliated", r->index()); + if (r->affiliation() != req.affiliation()) { + assert(_heap->mode()->is_generational(), "Request for %s from %s region should only happen in generational mode.", + req.affiliation_name(), r->affiliation_name()); + return nullptr; + } } // req.size() is in words, r->free() is in bytes. if (req.is_lab_alloc()) { - // This is a GCLAB or a TLAB allocation size_t adjusted_size = req.size(); - size_t free = align_down(r->free() >> LogHeapWordSize, MinObjAlignment); - if (adjusted_size > free) { - adjusted_size = free; - } - if (adjusted_size >= req.min_size()) { - result = r->allocate(adjusted_size, req.type()); - log_debug(gc)("Allocated " SIZE_FORMAT " words (adjusted from " SIZE_FORMAT ") for %s @" PTR_FORMAT - " from %s region " SIZE_FORMAT ", free bytes remaining: " SIZE_FORMAT, - adjusted_size, req.size(), ShenandoahAllocRequest::alloc_type_to_string(req.type()), p2i(result), - _partitions.partition_membership_name(r->index()), r->index(), r->free()); - assert (result != nullptr, "Allocation must succeed: free " SIZE_FORMAT ", actual " SIZE_FORMAT, free, adjusted_size); - req.set_actual_size(adjusted_size); + size_t free = r->free(); // free represents bytes available within region r + if (req.type() == ShenandoahAllocRequest::_alloc_plab) { + // This is a PLAB allocation + assert(_heap->mode()->is_generational(), "PLABs are only for generational mode"); + assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, r->index()), + "PLABS must be allocated in old_collector_free regions"); + + // Need to assure that plabs are aligned on multiple of card region + // Convert free from unaligned bytes to aligned number of words + size_t usable_free = get_usable_free_words(free); + if (adjusted_size > usable_free) { + adjusted_size = usable_free; + } + adjusted_size = align_down(adjusted_size, CardTable::card_size_in_words()); + if (adjusted_size >= req.min_size()) { + result = allocate_aligned_plab(adjusted_size, req, r); + assert(result != nullptr, "allocate must succeed"); + req.set_actual_size(adjusted_size); + } else { + // Otherwise, leave result == nullptr because the adjusted size is smaller than min size. + log_trace(gc, free)("Failed to shrink PLAB request (" SIZE_FORMAT ") in region " SIZE_FORMAT " to " SIZE_FORMAT + " because min_size() is " SIZE_FORMAT, req.size(), r->index(), adjusted_size, req.min_size()); + } } else { - log_trace(gc, free)("Failed to shrink TLAB or GCLAB request (" SIZE_FORMAT ") in region " SIZE_FORMAT " to " SIZE_FORMAT - " because min_size() is " SIZE_FORMAT, req.size(), r->index(), adjusted_size, req.min_size()); + // This is a GCLAB or a TLAB allocation + // Convert free from unaligned bytes to aligned number of words + free = align_down(free >> LogHeapWordSize, MinObjAlignment); + if (adjusted_size > free) { + adjusted_size = free; + } + if (adjusted_size >= req.min_size()) { + result = r->allocate(adjusted_size, req); + assert (result != nullptr, "Allocation must succeed: free " SIZE_FORMAT ", actual " SIZE_FORMAT, free, adjusted_size); + req.set_actual_size(adjusted_size); + } else { + log_trace(gc, free)("Failed to shrink TLAB or GCLAB request (" SIZE_FORMAT ") in region " SIZE_FORMAT " to " SIZE_FORMAT + " because min_size() is " SIZE_FORMAT, req.size(), r->index(), adjusted_size, req.min_size()); + } } } else { size_t size = req.size(); - result = r->allocate(size, req.type()); + result = r->allocate(size, req); if (result != nullptr) { // Record actual allocation size - log_debug(gc)("Allocated " SIZE_FORMAT " words for %s @" PTR_FORMAT - " from %s region " SIZE_FORMAT ", free bytes remaining: " SIZE_FORMAT, - size, ShenandoahAllocRequest::alloc_type_to_string(req.type()), p2i(result), - _partitions.partition_membership_name(r->index()), r->index(), r->free()); req.set_actual_size(size); } } @@ -767,13 +1092,22 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah if (result != nullptr) { // Allocation successful, bump stats: if (req.is_mutator_alloc()) { + assert(req.is_young(), "Mutator allocations always come from young generation."); _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, req.actual_size() * HeapWordSize); } else { assert(req.is_gc_alloc(), "Should be gc_alloc since req wasn't mutator alloc"); // For GC allocations, we advance update_watermark because the objects relocated into this memory during - // evacuation are not updated during evacuation. + // evacuation are not updated during evacuation. For both young and old regions r, it is essential that all + // PLABs be made parsable at the end of evacuation. This is enabled by retiring all plabs at end of evacuation. r->set_update_watermark(r->top()); + if (r->is_old()) { + _partitions.increase_used(ShenandoahFreeSetPartitionId::OldCollector, req.actual_size() * HeapWordSize); + assert(req.type() != ShenandoahAllocRequest::_alloc_gclab, "old-gen allocations use PLAB or shared allocation"); + // for plabs, we'll sort the difference between evac and promotion usage when we retire the plab + } else { + _partitions.increase_used(ShenandoahFreeSetPartitionId::Collector, req.actual_size() * HeapWordSize); + } } } @@ -788,9 +1122,22 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // then retire the region so that subsequent searches can find available memory more quickly. size_t idx = r->index(); - _partitions.retire_from_partition(req.is_mutator_alloc()? - ShenandoahFreeSetPartitionId::Mutator: ShenandoahFreeSetPartitionId::Collector, - idx, r->used()); + ShenandoahFreeSetPartitionId orig_partition; + if (req.is_mutator_alloc()) { + orig_partition = ShenandoahFreeSetPartitionId::Mutator; + } else if (req.type() == ShenandoahAllocRequest::_alloc_gclab) { + orig_partition = ShenandoahFreeSetPartitionId::Collector; + } else if (req.type() == ShenandoahAllocRequest::_alloc_plab) { + orig_partition = ShenandoahFreeSetPartitionId::OldCollector; + } else { + assert(req.type() == ShenandoahAllocRequest::_alloc_shared_gc, "Unexpected allocation type"); + if (req.is_old()) { + orig_partition = ShenandoahFreeSetPartitionId::OldCollector; + } else { + orig_partition = ShenandoahFreeSetPartitionId::Collector; + } + } + _partitions.retire_from_partition(orig_partition, idx, r->used()); _partitions.assert_bounds(); } return result; @@ -803,6 +1150,9 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { size_t words_size = req.size(); idx_t num = ShenandoahHeapRegion::required_regions(words_size * HeapWordSize); + assert(req.is_young(), "Humongous regions always allocated in YOUNG"); + ShenandoahGeneration* generation = _heap->generation_for(req.affiliation()); + // Check if there are enough regions left to satisfy allocation. if (num > (idx_t) _partitions.count(ShenandoahFreeSetPartitionId::Mutator)) { return nullptr; @@ -815,7 +1165,7 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { // Find the continuous interval of $num regions, starting from $beg and ending in $end, // inclusive. Contiguous allocations are biased to the beginning. idx_t beg = _partitions.find_index_of_next_available_cluster_of_regions(ShenandoahFreeSetPartitionId::Mutator, - start_range, num); + start_range, num); if (beg > last_possible_start) { // Hit the end, goodbye return nullptr; @@ -882,9 +1232,11 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { used_words = ShenandoahHeapRegion::region_size_words(); } + r->set_affiliation(req.affiliation()); + r->set_update_watermark(r->bottom()); r->set_top(r->bottom() + used_words); } - + generation->increase_affiliated_region_count(num); if (remainder != 0) { // Record this remainder as allocation waste _heap->notify_mutator_alloc_words(ShenandoahHeapRegion::region_size_words() - remainder, true); @@ -897,12 +1249,14 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, total_humongous_size); _partitions.assert_bounds(); req.set_actual_size(words_size); + if (remainder != 0) { + req.set_waste(ShenandoahHeapRegion::region_size_words() - remainder); + } return _heap->get_region(beg)->bottom(); } void ShenandoahFreeSet::try_recycle_trashed(ShenandoahHeapRegion* r) { if (r->is_trash()) { - _heap->decrease_used(r->used()); r->recycle(); } } @@ -960,6 +1314,27 @@ void ShenandoahFreeSet::recycle_trash() { } } +void ShenandoahFreeSet::flip_to_old_gc(ShenandoahHeapRegion* r) { + size_t idx = r->index(); + + assert(_partitions.partition_id_matches(idx, ShenandoahFreeSetPartitionId::Mutator), "Should be in mutator view"); + assert(can_allocate_from(r), "Should not be allocated"); + + ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); + size_t region_capacity = alloc_capacity(r); + _partitions.move_from_partition_to_partition(idx, ShenandoahFreeSetPartitionId::Mutator, + ShenandoahFreeSetPartitionId::OldCollector, region_capacity); + _partitions.assert_bounds(); + _heap->old_generation()->augment_evacuation_reserve(region_capacity); + bool transferred = gen_heap->generation_sizer()->transfer_to_old(1); + if (!transferred) { + log_warning(gc, free)("Forcing transfer of " SIZE_FORMAT " to old reserve.", idx); + gen_heap->generation_sizer()->force_transfer_to_old(1); + } + // We do not ensure that the region is no longer trash, relying on try_allocate_in(), which always comes next, + // to recycle trash before attempting to allocate anything in the region. +} + void ShenandoahFreeSet::flip_to_gc(ShenandoahHeapRegion* r) { size_t idx = r->index(); @@ -982,12 +1357,24 @@ void ShenandoahFreeSet::clear() { void ShenandoahFreeSet::clear_internal() { _partitions.make_all_regions_unavailable(); -} -void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &cset_regions) { + _alloc_bias_weight = 0; + _partitions.set_bias_from_left_to_right(ShenandoahFreeSetPartitionId::Mutator, true); + _partitions.set_bias_from_left_to_right(ShenandoahFreeSetPartitionId::Collector, false); + _partitions.set_bias_from_left_to_right(ShenandoahFreeSetPartitionId::OldCollector, false); +} - cset_regions = 0; +void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &young_cset_regions, size_t &old_cset_regions, + size_t &first_old_region, size_t &last_old_region, + size_t &old_region_count) { clear_internal(); + + first_old_region = _heap->num_regions(); + last_old_region = 0; + old_region_count = 0; + old_cset_regions = 0; + young_cset_regions = 0; + size_t region_size_bytes = _partitions.region_size_bytes(); size_t max_regions = _partitions.max_regions(); @@ -995,77 +1382,181 @@ void ShenandoahFreeSet::find_regions_with_alloc_capacity(size_t &cset_regions) { size_t mutator_rightmost = 0; size_t mutator_leftmost_empty = max_regions; size_t mutator_rightmost_empty = 0; - size_t mutator_regions = 0; size_t mutator_used = 0; - for (size_t idx = 0; idx < _heap->num_regions(); idx++) { + size_t old_collector_leftmost = max_regions; + size_t old_collector_rightmost = 0; + size_t old_collector_leftmost_empty = max_regions; + size_t old_collector_rightmost_empty = 0; + size_t old_collector_regions = 0; + size_t old_collector_used = 0; + + size_t num_regions = _heap->num_regions(); + for (size_t idx = 0; idx < num_regions; idx++) { ShenandoahHeapRegion* region = _heap->get_region(idx); if (region->is_trash()) { // Trashed regions represent regions that had been in the collection partition but have not yet been "cleaned up". // The cset regions are not "trashed" until we have finished update refs. - cset_regions++; + if (region->is_old()) { + old_cset_regions++; + } else { + assert(region->is_young(), "Trashed region should be old or young"); + young_cset_regions++; + } + } else if (region->is_old()) { + // count both humongous and regular regions, but don't count trash (cset) regions. + old_region_count++; + if (first_old_region > idx) { + first_old_region = idx; + } + last_old_region = idx; } if (region->is_alloc_allowed() || region->is_trash()) { + assert(!region->is_cset(), "Shouldn't be adding cset regions to the free set"); // Do not add regions that would almost surely fail allocation size_t ac = alloc_capacity(region); if (ac > PLAB::min_size() * HeapWordSize) { - _partitions.raw_assign_membership(idx, ShenandoahFreeSetPartitionId::Mutator); - - if (idx < mutator_leftmost) { - mutator_leftmost = idx; - } - if (idx > mutator_rightmost) { - mutator_rightmost = idx; - } - if (ac == region_size_bytes) { - if (idx < mutator_leftmost_empty) { - mutator_leftmost_empty = idx; + if (region->is_trash() || !region->is_old()) { + // Both young and old collected regions (trashed) are placed into the Mutator set + _partitions.raw_assign_membership(idx, ShenandoahFreeSetPartitionId::Mutator); + if (idx < mutator_leftmost) { + mutator_leftmost = idx; + } + if (idx > mutator_rightmost) { + mutator_rightmost = idx; + } + if (ac == region_size_bytes) { + if (idx < mutator_leftmost_empty) { + mutator_leftmost_empty = idx; + } + if (idx > mutator_rightmost_empty) { + mutator_rightmost_empty = idx; + } + } + mutator_regions++; + mutator_used += (region_size_bytes - ac); + } else { + // !region->is_trash() && region is_old() + _partitions.raw_assign_membership(idx, ShenandoahFreeSetPartitionId::OldCollector); + if (idx < old_collector_leftmost) { + old_collector_leftmost = idx; } - if (idx > mutator_rightmost_empty) { - mutator_rightmost_empty = idx; + if (idx > old_collector_rightmost) { + old_collector_rightmost = idx; } + if (ac == region_size_bytes) { + if (idx < old_collector_leftmost_empty) { + old_collector_leftmost_empty = idx; + } + if (idx > old_collector_rightmost_empty) { + old_collector_rightmost_empty = idx; + } + } + old_collector_regions++; + old_collector_used += (region_size_bytes - ac); } - mutator_regions++; - mutator_used += (region_size_bytes - ac); - - log_debug(gc)( - " Adding Region " SIZE_FORMAT " (Free: " SIZE_FORMAT "%s, Used: " SIZE_FORMAT "%s) to mutator partition", - idx, byte_size_in_proper_unit(region->free()), proper_unit_for_byte_size(region->free()), - byte_size_in_proper_unit(region->used()), proper_unit_for_byte_size(region->used())); } } } + log_debug(gc)(" At end of prep_to_rebuild, mutator_leftmost: " SIZE_FORMAT + ", mutator_rightmost: " SIZE_FORMAT + ", mutator_leftmost_empty: " SIZE_FORMAT + ", mutator_rightmost_empty: " SIZE_FORMAT + ", mutator_regions: " SIZE_FORMAT + ", mutator_used: " SIZE_FORMAT, + mutator_leftmost, mutator_rightmost, mutator_leftmost_empty, mutator_rightmost_empty, + mutator_regions, mutator_used); + + log_debug(gc)(" old_collector_leftmost: " SIZE_FORMAT + ", old_collector_rightmost: " SIZE_FORMAT + ", old_collector_leftmost_empty: " SIZE_FORMAT + ", old_collector_rightmost_empty: " SIZE_FORMAT + ", old_collector_regions: " SIZE_FORMAT + ", old_collector_used: " SIZE_FORMAT, + old_collector_leftmost, old_collector_rightmost, old_collector_leftmost_empty, old_collector_rightmost_empty, + old_collector_regions, old_collector_used); + idx_t rightmost_idx = (mutator_leftmost == max_regions)? -1: (idx_t) mutator_rightmost; idx_t rightmost_empty_idx = (mutator_leftmost_empty == max_regions)? -1: (idx_t) mutator_rightmost_empty; _partitions.establish_mutator_intervals(mutator_leftmost, rightmost_idx, mutator_leftmost_empty, rightmost_empty_idx, mutator_regions, mutator_used); + rightmost_idx = (old_collector_leftmost == max_regions)? -1: (idx_t) old_collector_rightmost; + rightmost_empty_idx = (old_collector_leftmost_empty == max_regions)? -1: (idx_t) old_collector_rightmost_empty; + _partitions.establish_old_collector_intervals(old_collector_leftmost, rightmost_idx, old_collector_leftmost_empty, + rightmost_empty_idx, old_collector_regions, old_collector_used); + log_debug(gc)(" After find_regions_with_alloc_capacity(), Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]," + " Old Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), + _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector)); +} + +// Returns number of regions transferred, adds transferred bytes to var argument bytes_transferred +size_t ShenandoahFreeSet::transfer_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId which_collector, + size_t max_xfer_regions, + size_t& bytes_transferred) { + shenandoah_assert_heaplocked(); + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + size_t transferred_regions = 0; + ShenandoahLeftRightIterator iterator(&_partitions, which_collector, true); + idx_t rightmost = _partitions.rightmost_empty(which_collector); + for (idx_t idx = iterator.current(); transferred_regions < max_xfer_regions && iterator.has_next(); idx = iterator.next()) { + // Note: can_allocate_from() denotes that region is entirely empty + if (can_allocate_from(idx)) { + _partitions.move_from_partition_to_partition(idx, which_collector, ShenandoahFreeSetPartitionId::Mutator, region_size_bytes); + transferred_regions++; + bytes_transferred += region_size_bytes; + } + } + return transferred_regions; +} + +// Returns number of regions transferred, adds transferred bytes to var argument bytes_transferred +size_t ShenandoahFreeSet::transfer_non_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId which_collector, + size_t max_xfer_regions, + size_t& bytes_transferred) { + shenandoah_assert_heaplocked(); + size_t transferred_regions = 0; + ShenandoahLeftRightIterator iterator(&_partitions, which_collector, false); + for (idx_t idx = iterator.current(); transferred_regions < max_xfer_regions && iterator.has_next(); idx = iterator.next()) { + size_t ac = alloc_capacity(idx); + if (ac > 0) { + _partitions.move_from_partition_to_partition(idx, which_collector, ShenandoahFreeSetPartitionId::Mutator, ac); + transferred_regions++; + bytes_transferred += ac; + } + } + return transferred_regions; } void ShenandoahFreeSet::move_regions_from_collector_to_mutator(size_t max_xfer_regions) { - size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - size_t collector_empty_xfer = 0; - size_t collector_not_empty_xfer = 0; + size_t collector_xfer = 0; + size_t old_collector_xfer = 0; // Process empty regions within the Collector free partition if ((max_xfer_regions > 0) && (_partitions.leftmost_empty(ShenandoahFreeSetPartitionId::Collector) <= _partitions.rightmost_empty(ShenandoahFreeSetPartitionId::Collector))) { ShenandoahHeapLocker locker(_heap->lock()); - idx_t rightmost = _partitions.rightmost_empty(ShenandoahFreeSetPartitionId::Collector); - for (idx_t idx = _partitions.leftmost_empty(ShenandoahFreeSetPartitionId::Collector); - (max_xfer_regions > 0) && (idx <= rightmost); ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Collector, idx), - "Boundaries or find_first_set_bit failed: " SSIZE_FORMAT, idx); - // Note: can_allocate_from() denotes that region is entirely empty - if (can_allocate_from(idx)) { - _partitions.move_from_partition_to_partition(idx, ShenandoahFreeSetPartitionId::Collector, - ShenandoahFreeSetPartitionId::Mutator, region_size_bytes); - max_xfer_regions--; - collector_empty_xfer += region_size_bytes; - } - idx = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Collector, idx + 1); + max_xfer_regions -= + transfer_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId::Collector, max_xfer_regions, + collector_xfer); + } + + // Process empty regions within the OldCollector free partition + if ((max_xfer_regions > 0) && + (_partitions.leftmost_empty(ShenandoahFreeSetPartitionId::OldCollector) + <= _partitions.rightmost_empty(ShenandoahFreeSetPartitionId::OldCollector))) { + ShenandoahHeapLocker locker(_heap->lock()); + size_t old_collector_regions = + transfer_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId::OldCollector, max_xfer_regions, + old_collector_xfer); + max_xfer_regions -= old_collector_regions; + if (old_collector_regions > 0) { + ShenandoahGenerationalHeap::cast(_heap)->generation_sizer()->transfer_to_young(old_collector_regions); } } @@ -1073,81 +1564,202 @@ void ShenandoahFreeSet::move_regions_from_collector_to_mutator(size_t max_xfer_r if ((max_xfer_regions > 0) && (_partitions.leftmost(ShenandoahFreeSetPartitionId::Collector) <= _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector))) { ShenandoahHeapLocker locker(_heap->lock()); - idx_t rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector); - for (idx_t idx = _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector); - (max_xfer_regions > 0) && (idx <= rightmost); ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Collector, idx), - "Boundaries or find_first_set_bit failed: " SSIZE_FORMAT, idx); - size_t ac = alloc_capacity(idx); - if (ac > 0) { - _partitions.move_from_partition_to_partition(idx, ShenandoahFreeSetPartitionId::Collector, - ShenandoahFreeSetPartitionId::Mutator, ac); - max_xfer_regions--; - collector_not_empty_xfer += ac; - } - idx = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Collector, idx + 1); - } + max_xfer_regions -= + transfer_non_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId::Collector, max_xfer_regions, + collector_xfer); } - size_t collector_xfer = collector_empty_xfer + collector_not_empty_xfer; - log_info(gc, ergo)("At start of update refs, moving " SIZE_FORMAT "%s to Mutator free partition from Collector Reserve", - byte_size_in_proper_unit(collector_xfer), proper_unit_for_byte_size(collector_xfer)); + size_t total_xfer = collector_xfer + old_collector_xfer; + log_info(gc, ergo)("At start of update refs, moving " SIZE_FORMAT "%s to Mutator free set from Collector Reserve (" + SIZE_FORMAT "%s) and from Old Collector Reserve (" SIZE_FORMAT "%s)", + byte_size_in_proper_unit(total_xfer), proper_unit_for_byte_size(total_xfer), + byte_size_in_proper_unit(collector_xfer), proper_unit_for_byte_size(collector_xfer), + byte_size_in_proper_unit(old_collector_xfer), proper_unit_for_byte_size(old_collector_xfer)); } -void ShenandoahFreeSet::prepare_to_rebuild(size_t &cset_regions) { + +// Overwrite arguments to represent the amount of memory in each generation that is about to be recycled +void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_cset_regions, size_t &old_cset_regions, + size_t &first_old_region, size_t &last_old_region, size_t &old_region_count) { shenandoah_assert_heaplocked(); + // This resets all state information, removing all regions from all sets. + clear(); + log_debug(gc, free)("Rebuilding FreeSet"); + + // This places regions that have alloc_capacity into the old_collector set if they identify as is_old() or the + // mutator set otherwise. All trashed (cset) regions are affiliated young and placed in mutator set. + find_regions_with_alloc_capacity(young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count); +} - log_debug(gc)("Rebuilding FreeSet"); +void ShenandoahFreeSet::establish_generation_sizes(size_t young_region_count, size_t old_region_count) { + assert(young_region_count + old_region_count == ShenandoahHeap::heap()->num_regions(), "Sanity"); + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + ShenandoahOldGeneration* old_gen = heap->old_generation(); + ShenandoahYoungGeneration* young_gen = heap->young_generation(); + size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - // This places regions that have alloc_capacity into the mutator partition. - find_regions_with_alloc_capacity(cset_regions); + size_t original_old_capacity = old_gen->max_capacity(); + size_t new_old_capacity = old_region_count * region_size_bytes; + size_t new_young_capacity = young_region_count * region_size_bytes; + old_gen->set_capacity(new_old_capacity); + young_gen->set_capacity(new_young_capacity); + + if (new_old_capacity > original_old_capacity) { + size_t region_count = (new_old_capacity - original_old_capacity) / region_size_bytes; + log_info(gc, ergo)("Transfer " SIZE_FORMAT " region(s) from %s to %s, yielding increased size: " PROPERFMT, + region_count, young_gen->name(), old_gen->name(), PROPERFMTARGS(new_old_capacity)); + } else if (new_old_capacity < original_old_capacity) { + size_t region_count = (original_old_capacity - new_old_capacity) / region_size_bytes; + log_info(gc, ergo)("Transfer " SIZE_FORMAT " region(s) from %s to %s, yielding increased size: " PROPERFMT, + region_count, old_gen->name(), young_gen->name(), PROPERFMTARGS(new_young_capacity)); + } + // This balances generations, so clear any pending request to balance. + old_gen->set_region_balance(0); + } } -void ShenandoahFreeSet::finish_rebuild(size_t cset_regions) { +void ShenandoahFreeSet::finish_rebuild(size_t young_cset_regions, size_t old_cset_regions, size_t old_region_count, + bool have_evacuation_reserves) { shenandoah_assert_heaplocked(); + size_t young_reserve(0), old_reserve(0); - // Our desire is to reserve this much memory for future evacuation. We may end up reserving less, if - // memory is in short supply. - - size_t reserve = _heap->max_capacity() * ShenandoahEvacReserve / 100; - size_t available_in_collector_partition = (_partitions.capacity_of(ShenandoahFreeSetPartitionId::Collector) - - _partitions.used_by(ShenandoahFreeSetPartitionId::Collector)); - size_t additional_reserve; - if (available_in_collector_partition < reserve) { - additional_reserve = reserve - available_in_collector_partition; + if (_heap->mode()->is_generational()) { + compute_young_and_old_reserves(young_cset_regions, old_cset_regions, have_evacuation_reserves, + young_reserve, old_reserve); } else { - additional_reserve = 0; + young_reserve = (_heap->max_capacity() / 100) * ShenandoahEvacReserve; + old_reserve = 0; } - reserve_regions(reserve); + // Move some of the mutator regions in the Collector and OldCollector partitions in order to satisfy + // young_reserve and old_reserve. + reserve_regions(young_reserve, old_reserve, old_region_count); + size_t young_region_count = _heap->num_regions() - old_region_count; + establish_generation_sizes(young_region_count, old_region_count); + establish_old_collector_alloc_bias(); _partitions.assert_bounds(); log_status(); } -void ShenandoahFreeSet::rebuild() { - size_t cset_regions; - prepare_to_rebuild(cset_regions); - finish_rebuild(cset_regions); +void ShenandoahFreeSet::compute_young_and_old_reserves(size_t young_cset_regions, size_t old_cset_regions, + bool have_evacuation_reserves, + size_t& young_reserve_result, size_t& old_reserve_result) const { + shenandoah_assert_generational(); + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + + ShenandoahOldGeneration* const old_generation = _heap->old_generation(); + size_t old_available = old_generation->available(); + size_t old_unaffiliated_regions = old_generation->free_unaffiliated_regions(); + ShenandoahYoungGeneration* const young_generation = _heap->young_generation(); + size_t young_capacity = young_generation->max_capacity(); + size_t young_unaffiliated_regions = young_generation->free_unaffiliated_regions(); + + // Add in the regions we anticipate to be freed by evacuation of the collection set + old_unaffiliated_regions += old_cset_regions; + young_unaffiliated_regions += young_cset_regions; + + // Consult old-region balance to make adjustments to current generation capacities and availability. + // The generation region transfers take place after we rebuild. + const ssize_t old_region_balance = old_generation->get_region_balance(); + if (old_region_balance != 0) { +#ifdef ASSERT + if (old_region_balance > 0) { + assert(old_region_balance <= checked_cast(old_unaffiliated_regions), "Cannot transfer regions that are affiliated"); + } else { + assert(0 - old_region_balance <= checked_cast(young_unaffiliated_regions), "Cannot transfer regions that are affiliated"); + } +#endif + + ssize_t xfer_bytes = old_region_balance * checked_cast(region_size_bytes); + old_available -= xfer_bytes; + old_unaffiliated_regions -= old_region_balance; + young_capacity += xfer_bytes; + young_unaffiliated_regions += old_region_balance; + } + + // All allocations taken from the old collector set are performed by GC, generally using PLABs for both + // promotions and evacuations. The partition between which old memory is reserved for evacuation and + // which is reserved for promotion is enforced using thread-local variables that prescribe intentions for + // each PLAB's available memory. + if (have_evacuation_reserves) { + // We are rebuilding at the end of final mark, having already established evacuation budgets for this GC pass. + const size_t promoted_reserve = old_generation->get_promoted_reserve(); + const size_t old_evac_reserve = old_generation->get_evacuation_reserve(); + young_reserve_result = young_generation->get_evacuation_reserve(); + old_reserve_result = promoted_reserve + old_evac_reserve; + assert(old_reserve_result <= old_available, + "Cannot reserve (" SIZE_FORMAT " + " SIZE_FORMAT") more OLD than is available: " SIZE_FORMAT, + promoted_reserve, old_evac_reserve, old_available); + } else { + // We are rebuilding at end of GC, so we set aside budgets specified on command line (or defaults) + young_reserve_result = (young_capacity * ShenandoahEvacReserve) / 100; + // The auto-sizer has already made old-gen large enough to hold all anticipated evacuations and promotions. + // Affiliated old-gen regions are already in the OldCollector free set. Add in the relevant number of + // unaffiliated regions. + old_reserve_result = old_available; + } + + // Old available regions that have less than PLAB::min_size() of available memory are not placed into the OldCollector + // free set. Because of this, old_available may not have enough memory to represent the intended reserve. Adjust + // the reserve downward to account for this possibility. This loss is part of the reason why the original budget + // was adjusted with ShenandoahOldEvacWaste and ShenandoahOldPromoWaste multipliers. + if (old_reserve_result > + _partitions.capacity_of(ShenandoahFreeSetPartitionId::OldCollector) + old_unaffiliated_regions * region_size_bytes) { + old_reserve_result = + _partitions.capacity_of(ShenandoahFreeSetPartitionId::OldCollector) + old_unaffiliated_regions * region_size_bytes; + } + + if (young_reserve_result > young_unaffiliated_regions * region_size_bytes) { + young_reserve_result = young_unaffiliated_regions * region_size_bytes; + } } -void ShenandoahFreeSet::reserve_regions(size_t to_reserve) { +// Having placed all regions that have allocation capacity into the mutator set if they identify as is_young() +// or into the old collector set if they identify as is_old(), move some of these regions from the mutator set +// into the collector set or old collector set in order to assure that the memory available for allocations within +// the collector set is at least to_reserve and the memory available for allocations within the old collector set +// is at least to_reserve_old. +void ShenandoahFreeSet::reserve_regions(size_t to_reserve, size_t to_reserve_old, size_t &old_region_count) { for (size_t i = _heap->num_regions(); i > 0; i--) { size_t idx = i - 1; ShenandoahHeapRegion* r = _heap->get_region(idx); - if (!_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, idx)) { continue; } size_t ac = alloc_capacity(r); - assert (ac > 0, "Membership in free partition implies has capacity"); + assert (ac > 0, "Membership in free set implies has capacity"); + assert (!r->is_old() || r->is_trash(), "Except for trash, mutator_is_free regions should not be affiliated OLD"); + bool move_to_old_collector = _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector) < to_reserve_old; bool move_to_collector = _partitions.available_in(ShenandoahFreeSetPartitionId::Collector) < to_reserve; - if (!move_to_collector) { - // We've satisfied to_reserve + + if (!move_to_collector && !move_to_old_collector) { + // We've satisfied both to_reserve and to_reserved_old break; } + if (move_to_old_collector) { + // We give priority to OldCollector partition because we desire to pack OldCollector regions into higher + // addresses than Collector regions. Presumably, OldCollector regions are more "stable" and less likely to + // be collected in the near future. + if (r->is_trash() || !r->is_affiliated()) { + // OLD regions that have available memory are already in the old_collector free set. + _partitions.move_from_partition_to_partition(idx, ShenandoahFreeSetPartitionId::Mutator, + ShenandoahFreeSetPartitionId::OldCollector, ac); + log_debug(gc)(" Shifting region " SIZE_FORMAT " from mutator_free to old_collector_free", idx); + log_debug(gc)(" Shifted Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]," + " Old Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), + _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector)); + old_region_count++; + continue; + } + } + if (move_to_collector) { // Note: In a previous implementation, regions were only placed into the survivor space (collector_is_free) if // they were entirely empty. This has the effect of causing new Mutator allocation to reside next to objects @@ -1160,10 +1772,21 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve) { _partitions.move_from_partition_to_partition(idx, ShenandoahFreeSetPartitionId::Mutator, ShenandoahFreeSetPartitionId::Collector, ac); log_debug(gc)(" Shifting region " SIZE_FORMAT " from mutator_free to collector_free", idx); + log_debug(gc)(" Shifted Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]," + " Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector), + _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector)); } } if (LogTarget(Info, gc, free)::is_enabled()) { + size_t old_reserve = _partitions.available_in(ShenandoahFreeSetPartitionId::OldCollector); + if (old_reserve < to_reserve_old) { + log_info(gc, free)("Wanted " PROPERFMT " for old reserve, but only reserved: " PROPERFMT, + PROPERFMTARGS(to_reserve_old), PROPERFMTARGS(old_reserve)); + } size_t reserve = _partitions.available_in(ShenandoahFreeSetPartitionId::Collector); if (reserve < to_reserve) { log_debug(gc)("Wanted " PROPERFMT " for young reserve, but only reserved: " PROPERFMT, @@ -1172,6 +1795,37 @@ void ShenandoahFreeSet::reserve_regions(size_t to_reserve) { } } +void ShenandoahFreeSet::establish_old_collector_alloc_bias() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + shenandoah_assert_heaplocked(); + + idx_t left_idx = _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector); + idx_t right_idx = _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector); + idx_t middle = (left_idx + right_idx) / 2; + size_t available_in_first_half = 0; + size_t available_in_second_half = 0; + + for (idx_t index = left_idx; index < middle; index++) { + if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, index)) { + ShenandoahHeapRegion* r = heap->get_region((size_t) index); + available_in_first_half += r->free(); + } + } + for (idx_t index = middle; index <= right_idx; index++) { + if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, index)) { + ShenandoahHeapRegion* r = heap->get_region(index); + available_in_second_half += r->free(); + } + } + + // We desire to first consume the sparsely distributed regions in order that the remaining regions are densely packed. + // Densely packing regions reduces the effort to search for a region that has sufficient memory to satisfy a new allocation + // request. Regions become sparsely distributed following a Full GC, which tends to slide all regions to the front of the + // heap rather than allowing survivor regions to remain at the high end of the heap where we intend for them to congregate. + _partitions.set_bias_from_left_to_right(ShenandoahFreeSetPartitionId::OldCollector, + (available_in_second_half > available_in_first_half)); +} + void ShenandoahFreeSet::log_status_under_lock() { // Must not be heap locked, it acquires heap lock only when log is enabled shenandoah_assert_not_heaplocked(); @@ -1189,23 +1843,26 @@ void ShenandoahFreeSet::log_status() { // Dump of the FreeSet details is only enabled if assertions are enabled if (LogTarget(Debug, gc, free)::is_enabled()) { #define BUFFER_SIZE 80 - size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - size_t consumed_collector = 0; - size_t available_collector = 0; - size_t consumed_mutator = 0; - size_t available_mutator = 0; char buffer[BUFFER_SIZE]; for (uint i = 0; i < BUFFER_SIZE; i++) { buffer[i] = '\0'; } - log_debug(gc)("FreeSet map legend: M:mutator_free C:collector_free H:humongous _:retired"); - log_debug(gc)(" mutator free range [" SIZE_FORMAT ".." SIZE_FORMAT "]," - " collector free range [" SIZE_FORMAT ".." SIZE_FORMAT "]", + + log_debug(gc)("FreeSet map legend:" + " M:mutator_free C:collector_free O:old_collector_free" + " H:humongous ~:retired old _:retired young"); + log_debug(gc)(" mutator free range [" SIZE_FORMAT ".." SIZE_FORMAT "] allocating from %s, " + " collector free range [" SIZE_FORMAT ".." SIZE_FORMAT "], " + "old collector free range [" SIZE_FORMAT ".." SIZE_FORMAT "] allocates from %s", _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator), _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator), + _partitions.alloc_from_left_bias(ShenandoahFreeSetPartitionId::Mutator)? "left to right": "right to left", _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector), - _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector)); + _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector), + _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector), + _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector), + _partitions.alloc_from_left_bias(ShenandoahFreeSetPartitionId::OldCollector)? "left to right": "right to left"); for (uint i = 0; i < _heap->num_regions(); i++) { ShenandoahHeapRegion *r = _heap->get_region(i); @@ -1215,18 +1872,27 @@ void ShenandoahFreeSet::log_status() { } if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, i)) { size_t capacity = alloc_capacity(r); - available_mutator += capacity; - consumed_mutator += region_size_bytes - capacity; - buffer[idx] = (capacity == region_size_bytes)? 'M': 'm'; + assert(!r->is_old() || r->is_trash(), "Old regions except trash regions should not be in mutator_free set"); + buffer[idx] = (capacity == ShenandoahHeapRegion::region_size_bytes()) ? 'M' : 'm'; } else if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::Collector, i)) { size_t capacity = alloc_capacity(r); - available_collector += capacity; - consumed_collector += region_size_bytes - capacity; - buffer[idx] = (capacity == region_size_bytes)? 'C': 'c'; + assert(!r->is_old() || r->is_trash(), "Old regions except trash regions should not be in collector_free set"); + buffer[idx] = (capacity == ShenandoahHeapRegion::region_size_bytes()) ? 'C' : 'c'; + } else if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, i)) { + size_t capacity = alloc_capacity(r); + buffer[idx] = (capacity == ShenandoahHeapRegion::region_size_bytes()) ? 'O' : 'o'; } else if (r->is_humongous()) { - buffer[idx] = 'h'; + if (r->is_old()) { + buffer[idx] = 'H'; + } else { + buffer[idx] = 'h'; + } } else { - buffer[idx] = '_'; + if (r->is_old()) { + buffer[idx] = '~'; + } else { + buffer[idx] = '_'; + } } } uint remnant = _heap->num_regions() % 64; @@ -1284,9 +1950,8 @@ void ShenandoahFreeSet::log_status() { // retired, the sum of used and capacities within regions that are still in the Mutator free partition may not match // my internally tracked values of used() and free(). assert(free == total_free, "Free memory should match"); - ls.print("Free: " SIZE_FORMAT "%s, Max: " SIZE_FORMAT "%s regular, " SIZE_FORMAT "%s humongous, ", - byte_size_in_proper_unit(free), proper_unit_for_byte_size(free), + byte_size_in_proper_unit(total_free), proper_unit_for_byte_size(total_free), byte_size_in_proper_unit(max), proper_unit_for_byte_size(max), byte_size_in_proper_unit(max_humongous), proper_unit_for_byte_size(max_humongous) ); @@ -1333,6 +1998,27 @@ void ShenandoahFreeSet::log_status() { byte_size_in_proper_unit(max), proper_unit_for_byte_size(max), byte_size_in_proper_unit(total_used), proper_unit_for_byte_size(total_used)); } + + if (_heap->mode()->is_generational()) { + size_t max = 0; + size_t total_free = 0; + size_t total_used = 0; + + for (idx_t idx = _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector); + idx <= _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector); idx++) { + if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, idx)) { + ShenandoahHeapRegion *r = _heap->get_region(idx); + size_t free = alloc_capacity(r); + max = MAX2(max, free); + total_free += free; + total_used += r->used(); + } + } + ls.print_cr(" Old Collector Reserve: " SIZE_FORMAT "%s, Max: " SIZE_FORMAT "%s; Used: " SIZE_FORMAT "%s", + byte_size_in_proper_unit(total_free), proper_unit_for_byte_size(total_free), + byte_size_in_proper_unit(max), proper_unit_for_byte_size(max), + byte_size_in_proper_unit(total_used), proper_unit_for_byte_size(total_used)); + } } } @@ -1344,6 +2030,7 @@ HeapWord* ShenandoahFreeSet::allocate(ShenandoahAllocRequest& req, bool& in_new_ case ShenandoahAllocRequest::_alloc_shared_gc: in_new_region = true; return allocate_contiguous(req); + case ShenandoahAllocRequest::_alloc_plab: case ShenandoahAllocRequest::_alloc_gclab: case ShenandoahAllocRequest::_alloc_tlab: in_new_region = false; @@ -1360,20 +2047,25 @@ HeapWord* ShenandoahFreeSet::allocate(ShenandoahAllocRequest& req, bool& in_new_ void ShenandoahFreeSet::print_on(outputStream* out) const { out->print_cr("Mutator Free Set: " SIZE_FORMAT "", _partitions.count(ShenandoahFreeSetPartitionId::Mutator)); - idx_t rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator); - for (idx_t index = _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator); index <= rightmost; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, index), - "Boundaries or find_first_set_bit failed: " SSIZE_FORMAT, index); + ShenandoahLeftRightIterator mutator(const_cast(&_partitions), ShenandoahFreeSetPartitionId::Mutator); + for (idx_t index = mutator.current(); mutator.has_next(); index = mutator.next()) { _heap->get_region(index)->print_on(out); - index = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Mutator, index + 1); } + out->print_cr("Collector Free Set: " SIZE_FORMAT "", _partitions.count(ShenandoahFreeSetPartitionId::Collector)); - rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector); - for (idx_t index = _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector); index <= rightmost; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Collector, index), - "Boundaries or find_first_set_bit failed: " SSIZE_FORMAT, index); + ShenandoahLeftRightIterator collector(const_cast(&_partitions), ShenandoahFreeSetPartitionId::Collector); + for (idx_t index = collector.current(); collector.has_next(); index = collector.next()) { _heap->get_region(index)->print_on(out); - index = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Collector, index + 1); + } + + if (_heap->mode()->is_generational()) { + out->print_cr("Old Collector Free Set: " SIZE_FORMAT "", _partitions.count(ShenandoahFreeSetPartitionId::OldCollector)); + for (idx_t index = _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector); + index <= _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector); index++) { + if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, index)) { + _heap->get_region(index)->print_on(out); + } + } } } @@ -1381,15 +2073,12 @@ double ShenandoahFreeSet::internal_fragmentation() { double squared = 0; double linear = 0; - idx_t rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator); - for (idx_t index = _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator); index <= rightmost; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, index), - "Boundaries or find_first_set_bit failed: " SSIZE_FORMAT, index); + ShenandoahLeftRightIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); + for (idx_t index = iterator.current(); iterator.has_next(); index = iterator.next()) { ShenandoahHeapRegion* r = _heap->get_region(index); size_t used = r->used(); squared += used * used; linear += used; - index = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Mutator, index + 1); } if (linear > 0) { @@ -1404,13 +2093,10 @@ double ShenandoahFreeSet::external_fragmentation() { idx_t last_idx = 0; size_t max_contig = 0; size_t empty_contig = 0; - size_t free = 0; - idx_t rightmost = _partitions.rightmost(ShenandoahFreeSetPartitionId::Mutator); - for (idx_t index = _partitions.leftmost(ShenandoahFreeSetPartitionId::Mutator); index <= rightmost; ) { - assert(_partitions.in_free_set(ShenandoahFreeSetPartitionId::Mutator, index), - "Boundaries or find_first_set_bit failed: " SSIZE_FORMAT, index); + ShenandoahLeftRightIterator iterator(&_partitions, ShenandoahFreeSetPartitionId::Mutator); + for (idx_t index = iterator.current(); iterator.has_next(); index = iterator.next()) { ShenandoahHeapRegion* r = _heap->get_region(index); if (r->is_empty()) { free += ShenandoahHeapRegion::region_size_bytes(); @@ -1424,7 +2110,6 @@ double ShenandoahFreeSet::external_fragmentation() { } max_contig = MAX2(max_contig, empty_contig); last_idx = index; - index = _partitions.find_index_of_next_available_region(ShenandoahFreeSetPartitionId::Mutator, index + 1); } if (free > 0) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index e4e2bb4d6e6..c69d73060c9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -34,6 +34,8 @@ enum class ShenandoahFreeSetPartitionId : uint8_t { Mutator, // Region is in the Mutator free set: available memory is available to mutators. Collector, // Region is in the Collector free set: available memory is reserved for evacuations. + OldCollector, // Region is in the Old Collector free set: + // available memory is reserved for old evacuations and for promotions.. NotFree // Region is in no free set: it has no available memory }; @@ -80,6 +82,10 @@ class ShenandoahRegionPartitions { size_t _used[UIntNumPartitions]; size_t _region_counts[UIntNumPartitions]; + // For each partition p, _left_to_right_bias is true iff allocations are normally made from lower indexed regions + // before higher indexed regions. + bool _left_to_right_bias[UIntNumPartitions]; + // Shrink the intervals associated with partition when region idx is removed from this free set inline void shrink_interval_if_boundary_modified(ShenandoahFreeSetPartitionId partition, ssize_t idx); @@ -88,6 +94,11 @@ class ShenandoahRegionPartitions { ssize_t low_idx, ssize_t high_idx); inline void expand_interval_if_boundary_modified(ShenandoahFreeSetPartitionId partition, ssize_t idx, size_t capacity); + inline bool is_mutator_partition(ShenandoahFreeSetPartitionId p); + inline bool is_young_collector_partition(ShenandoahFreeSetPartitionId p); + inline bool is_old_collector_partition(ShenandoahFreeSetPartitionId p); + inline bool available_implies_empty(size_t available); + #ifndef PRODUCT void dump_bitmap_row(ssize_t region_idx) const; void dump_bitmap_range(ssize_t start_region_idx, ssize_t end_region_idx) const; @@ -112,6 +123,13 @@ class ShenandoahRegionPartitions { ssize_t mutator_leftmost_empty, ssize_t mutator_rightmost_empty, size_t mutator_region_count, size_t mutator_used); + // Set the OldCollector intervals, usage, and capacity according to arguments. We use this at the end of rebuild_free_set() + // to avoid the overhead of making many redundant incremental adjustments to the mutator intervals as the free set is being + // rebuilt. + void establish_old_collector_intervals(ssize_t old_collector_leftmost, ssize_t old_collector_rightmost, + ssize_t old_collector_leftmost_empty, ssize_t old_collector_rightmost_empty, + size_t old_collector_region_count, size_t old_collector_used); + // Retire region idx from within partition, , leaving its capacity and used as part of the original free partition's totals. // Requires that region idx is in in the Mutator or Collector partitions. Hereafter, identifies this region as NotFree. // Any remnant of available memory at the time of retirement is added to the original partition's total of used bytes. @@ -180,6 +198,16 @@ class ShenandoahRegionPartitions { inline void increase_used(ShenandoahFreeSetPartitionId which_partition, size_t bytes); + inline void set_bias_from_left_to_right(ShenandoahFreeSetPartitionId which_partition, bool value) { + assert (which_partition < NumPartitions, "selected free set must be valid"); + _left_to_right_bias[int(which_partition)] = value; + } + + inline bool alloc_from_left_bias(ShenandoahFreeSetPartitionId which_partition) const { + assert (which_partition < NumPartitions, "selected free set must be valid"); + return _left_to_right_bias[int(which_partition)]; + } + inline size_t capacity_of(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free set must be valid"); return _capacity[int(which_partition)]; @@ -237,7 +265,7 @@ class ShenandoahRegionPartitions { // The Shenandoah garbage collector evacuates live objects out of specific regions that are identified as members of the // collection set (cset). // -// The ShenandoahFreeSet endeavors to congregrate survivor objects (objects that have been evacuated at least once) at the +// The ShenandoahFreeSet tries to colocate survivor objects (objects that have been evacuated at least once) at the // high end of memory. New mutator allocations are taken from the low end of memory. Within the mutator's range of regions, // humongous allocations are taken from the lowest addresses, and LAB (local allocation buffers) and regular shared allocations // are taken from the higher address of the mutator's range of regions. This approach allows longer lasting survivor regions @@ -260,18 +288,22 @@ class ShenandoahFreeSet : public CHeapObj { ShenandoahRegionPartitions _partitions; ShenandoahHeapRegion** _trash_regions; - // Mutator allocations are biased from left-to-right or from right-to-left based on which end of mutator range - // is most likely to hold partially used regions. In general, we want to finish consuming partially used - // regions and retire them in order to reduce the regions that must be searched for each allocation request. - bool _right_to_left_bias; + HeapWord* allocate_aligned_plab(size_t size, ShenandoahAllocRequest& req, ShenandoahHeapRegion* r); + + // Return the address of memory allocated, setting in_new_region to true iff the allocation is taken + // from a region that was previously empty. Return nullptr if memory could not be allocated. + inline HeapWord* allocate_from_partition_with_affiliation(ShenandoahAffiliation affiliation, + ShenandoahAllocRequest& req, bool& in_new_region); // We re-evaluate the left-to-right allocation bias whenever _alloc_bias_weight is less than zero. Each time // we allocate an object, we decrement the count of this value. Each time we re-evaluate whether to allocate // from right-to-left or left-to-right, we reset the value of this counter to _InitialAllocBiasWeight. ssize_t _alloc_bias_weight; - const ssize_t _InitialAllocBiasWeight = 256; + const ssize_t INITIAL_ALLOC_BIAS_WEIGHT = 256; + // Increases used memory for the partition if the allocation is successful. `in_new_region` will be set + // if this is the first allocation in the region. HeapWord* try_allocate_in(ShenandoahHeapRegion* region, ShenandoahAllocRequest& req, bool& in_new_region); // While holding the heap lock, allocate memory for a single object or LAB which is to be entirely contained @@ -287,11 +319,38 @@ class ShenandoahFreeSet : public CHeapObj { // Precondition: ShenandoahHeapRegion::requires_humongous(req.size()) HeapWord* allocate_contiguous(ShenandoahAllocRequest& req); - // Change region r from the Mutator partition to the GC's Collector partition. This requires that the region is entirely empty. + // Change region r from the Mutator partition to the GC's Collector or OldCollector partition. This requires that the + // region is entirely empty. + // // Typical usage: During evacuation, the GC may find it needs more memory than had been reserved at the start of evacuation to // hold evacuated objects. If this occurs and memory is still available in the Mutator's free set, we will flip a region from - // the Mutator free set into the Collector free set. + // the Mutator free set into the Collector or OldCollector free set. void flip_to_gc(ShenandoahHeapRegion* r); + void flip_to_old_gc(ShenandoahHeapRegion* r); + + // Handle allocation for mutator. + HeapWord* allocate_for_mutator(ShenandoahAllocRequest &req, bool &in_new_region); + + // Update allocation bias and decided whether to allocate from the left or right side of the heap. + void update_allocation_bias(); + + // Search for regions to satisfy allocation request using iterator. + template + HeapWord* allocate_from_regions(Iter& iterator, ShenandoahAllocRequest &req, bool &in_new_region); + + // Handle allocation for collector (for evacuation). + HeapWord* allocate_for_collector(ShenandoahAllocRequest& req, bool& in_new_region); + + // Search for allocation in region with same affiliation as request, using given iterator. + template + HeapWord* allocate_with_affiliation(Iter& iterator, ShenandoahAffiliation affiliation, ShenandoahAllocRequest& req, bool& in_new_region); + + // Return true if the respective generation for this request has free regions. + bool can_allocate_in_new_region(const ShenandoahAllocRequest& req); + + // Attempt to allocate memory for an evacuation from the mutator's partition. + HeapWord* try_allocate_from_mutator(ShenandoahAllocRequest& req, bool& in_new_region); + void clear_internal(); void try_recycle_trashed(ShenandoahHeapRegion *r); @@ -303,21 +362,20 @@ class ShenandoahFreeSet : public CHeapObj { inline bool has_alloc_capacity(ShenandoahHeapRegion *r) const; - // This function places all regions that have allocation capacity into the mutator_partition, identifying regions - // that have no allocation capacity as NotFree. Subsequently, we will move some of the mutator regions into the - // collector partition with the intent of packing collector memory into the highest (rightmost) addresses of the - // heap, with mutator memory consuming the lowest addresses of the heap. - void find_regions_with_alloc_capacity(size_t &cset_regions); + size_t transfer_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId which_collector, + size_t max_xfer_regions, + size_t& bytes_transferred); + size_t transfer_non_empty_regions_from_collector_set_to_mutator_set(ShenandoahFreeSetPartitionId which_collector, + size_t max_xfer_regions, + size_t& bytes_transferred); - // Having placed all regions that have allocation capacity into the mutator partition, move some of these regions from - // the mutator partition into the collector partition in order to assure that the memory available for allocations within - // the collector partition is at least to_reserve. - void reserve_regions(size_t to_reserve); - // Overwrite arguments to represent the number of regions to be reclaimed from the cset - void prepare_to_rebuild(size_t &cset_regions); + // Determine whether we prefer to allocate from left to right or from right to left within the OldCollector free-set. + void establish_old_collector_alloc_bias(); - void finish_rebuild(size_t cset_regions); + // Set max_capacity for young and old generations + void establish_generation_sizes(size_t young_region_count, size_t old_region_count); + size_t get_usable_free_words(size_t free_bytes) const; // log status, assuming lock has already been acquired by the caller. void log_status(); @@ -330,20 +388,52 @@ class ShenandoahFreeSet : public CHeapObj { inline size_t alloc_capacity(size_t idx) const; void clear(); - void rebuild(); + + // Examine the existing free set representation, capturing the current state into var arguments: + // + // young_cset_regions is the number of regions currently in the young cset if we are starting to evacuate, or zero + // old_cset_regions is the number of regions currently in the old cset if we are starting a mixed evacuation, or zero + // first_old_region is the index of the first region that is part of the OldCollector set + // last_old_region is the index of the last region that is part of the OldCollector set + // old_region_count is the number of regions in the OldCollector set that have memory available to be allocated + void prepare_to_rebuild(size_t &young_cset_regions, size_t &old_cset_regions, + size_t &first_old_region, size_t &last_old_region, size_t &old_region_count); + + // At the end of final mark, but before we begin evacuating, heuristics calculate how much memory is required to + // hold the results of evacuating to young-gen and to old-gen, and have_evacuation_reserves should be true. + // These quantities, stored as reserves for their respective generations, are consulted prior to rebuilding + // the free set (ShenandoahFreeSet) in preparation for evacuation. When the free set is rebuilt, we make sure + // to reserve sufficient memory in the collector and old_collector sets to hold evacuations. + // + // We also rebuild the free set at the end of GC, as we prepare to idle GC until the next trigger. In this case, + // have_evacuation_reserves is false because we don't yet know how much memory will need to be evacuated in the + // next GC cycle. When have_evacuation_reserves is false, the free set rebuild operation reserves for the collector + // and old_collector sets based on alternative mechanisms, such as ShenandoahEvacReserve, ShenandoahOldEvacReserve, and + // ShenandoahOldCompactionReserve. In a future planned enhancement, the reserve for old_collector set when the + // evacuation reserves are unknown, is based in part on anticipated promotion as determined by analysis of live data + // found during the previous GC pass which is one less than the current tenure age. + // + // young_cset_regions is the number of regions currently in the young cset if we are starting to evacuate, or zero + // old_cset_regions is the number of regions currently in the old cset if we are starting a mixed evacuation, or zero + // num_old_regions is the number of old-gen regions that have available memory for further allocations (excluding old cset) + // have_evacuation_reserves is true iff the desired values of young-gen and old-gen evacuation reserves and old-gen + // promotion reserve have been precomputed (and can be obtained by invoking + // ->get_evacuation_reserve() or old_gen->get_promoted_reserve() + void finish_rebuild(size_t young_cset_regions, size_t old_cset_regions, size_t num_old_regions, + bool have_evacuation_reserves = false); + + // When a region is promoted in place, we add the region's available memory if it is greater than plab_min_size() + // into the old collector partition by invoking this method. + void add_promoted_in_place_region_to_old_collector(ShenandoahHeapRegion* region); // Move up to cset_regions number of regions from being available to the collector to being available to the mutator. // // Typical usage: At the end of evacuation, when the collector no longer needs the regions that had been reserved // for evacuation, invoke this to make regions available for mutator allocations. - // - // Note that we plan to replenish the Collector reserve at the end of update refs, at which time all - // of the regions recycled from the collection set will be available. If the very unlikely event that there - // are fewer regions in the collection set than remain in the collector set, we limit the transfer in order - // to assure that the replenished Collector reserve can be sufficiently large. void move_regions_from_collector_to_mutator(size_t cset_regions); void recycle_trash(); + // Acquire heap lock and log status, assuming heap lock is not acquired by the caller. void log_status_under_lock(); @@ -355,7 +445,6 @@ class ShenandoahFreeSet : public CHeapObj { } HeapWord* allocate(ShenandoahAllocRequest& req, bool& in_new_region); - size_t unsafe_peek_free() const; /* * Internal fragmentation metric: describes how fragmented the heap regions are. @@ -396,6 +485,28 @@ class ShenandoahFreeSet : public CHeapObj { double external_fragmentation(); void print_on(outputStream* out) const; + + // This function places all regions that have allocation capacity into the mutator partition, or if the region + // is already affiliated with old, into the old collector partition, identifying regions that have no allocation + // capacity as NotFree. Capture the modified state of the freeset into var arguments: + // + // young_cset_regions is the number of regions currently in the young cset if we are starting to evacuate, or zero + // old_cset_regions is the number of regions currently in the old cset if we are starting a mixed evacuation, or zero + // first_old_region is the index of the first region that is part of the OldCollector set + // last_old_region is the index of the last region that is part of the OldCollector set + // old_region_count is the number of regions in the OldCollector set that have memory available to be allocated + void find_regions_with_alloc_capacity(size_t &young_cset_regions, size_t &old_cset_regions, + size_t &first_old_region, size_t &last_old_region, size_t &old_region_count); + + // Ensure that Collector has at least to_reserve bytes of available memory, and OldCollector has at least old_reserve + // bytes of available memory. On input, old_region_count holds the number of regions already present in the + // OldCollector partition. Upon return, old_region_count holds the updated number of regions in the OldCollector partition. + void reserve_regions(size_t to_reserve, size_t old_reserve, size_t &old_region_count); + + // Reserve space for evacuations, with regions reserved for old evacuations placed to the right + // of regions reserved of young evacuations. + void compute_young_and_old_reserves(size_t young_cset_regions, size_t old_cset_regions, bool have_evacuation_reserves, + size_t &young_reserve_result, size_t &old_reserve_result) const; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHFREESET_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 53cb8e5d20f..3e880271529 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -36,11 +37,15 @@ #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahFullGC.hpp" +#include "gc/shenandoah/shenandoahGenerationalFullGC.hpp" +#include "gc/shenandoah/shenandoahGlobalGeneration.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" @@ -57,7 +62,6 @@ #include "memory/universe.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/javaThread.hpp" #include "runtime/orderAccess.hpp" #include "runtime/vmThread.hpp" #include "utilities/copy.hpp" @@ -109,6 +113,10 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::handle_completion(heap); + } + metrics.snap_after(); if (metrics.is_good_progress()) { @@ -120,13 +128,17 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) { } // Regardless if progress was made, we record that we completed a "successful" full GC. - heap->heuristics()->record_success_full(); + heap->global_generation()->heuristics()->record_success_full(); heap->shenandoah_policy()->record_success_full(); } void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::prepare(); + } + if (ShenandoahVerify) { heap->verifier()->verify_before_fullgc(); } @@ -169,10 +181,9 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { } assert(!heap->is_update_refs_in_progress(), "sanity"); - // b. Cancel concurrent mark, if in progress + // b. Cancel all concurrent marks, if in progress if (heap->is_concurrent_mark_in_progress()) { - ShenandoahConcurrentGC::cancel(); - heap->set_concurrent_mark_in_progress(false); + heap->cancel_concurrent_mark(); } assert(!heap->is_concurrent_mark_in_progress(), "sanity"); @@ -182,17 +193,21 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { } // d. Reset the bitmaps for new marking - heap->reset_mark_bitmap(); + heap->global_generation()->reset_mark_bitmap(); assert(heap->marking_context()->is_bitmap_clear(), "sanity"); - assert(!heap->marking_context()->is_complete(), "sanity"); + assert(!heap->global_generation()->is_mark_complete(), "sanity"); // e. Abandon reference discovery and clear all discovered references. - ShenandoahReferenceProcessor* rp = heap->ref_processor(); + ShenandoahReferenceProcessor* rp = heap->global_generation()->ref_processor(); rp->abandon_partial_discovery(); // f. Sync pinned region status from the CP marks heap->sync_pinned_region_status(); + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::restore_top_before_promote(heap); + } + // The rest of prologue: _preserved_marks->init(heap->workers()->active_workers()); @@ -200,6 +215,7 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) { } if (UseTLAB) { + // Note: PLABs are also retired with GCLABs in generational mode. heap->gclabs_retire(ResizeTLAB); heap->tlabs_retire(ResizeTLAB); } @@ -273,12 +289,12 @@ class ShenandoahPrepareForMarkClosure: public ShenandoahHeapRegionClosure { public: ShenandoahPrepareForMarkClosure() : _ctx(ShenandoahHeap::heap()->marking_context()) {} - void heap_region_do(ShenandoahHeapRegion *r) { + void heap_region_do(ShenandoahHeapRegion *r) override { _ctx->capture_top_at_mark_start(r); r->clear_live_data(); } - bool is_thread_safe() { return true; } + bool is_thread_safe() override { return true; } }; void ShenandoahFullGC::phase1_mark_heap() { @@ -287,18 +303,23 @@ void ShenandoahFullGC::phase1_mark_heap() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahPrepareForMarkClosure cl; + ShenandoahPrepareForMarkClosure prepare_for_mark; + ShenandoahExcludeRegionClosure cl(&prepare_for_mark); heap->parallel_heap_region_iterate(&cl); - heap->set_unload_classes(heap->heuristics()->can_unload_classes()); + heap->set_unload_classes(heap->global_generation()->heuristics()->can_unload_classes()); - ShenandoahReferenceProcessor* rp = heap->ref_processor(); + ShenandoahReferenceProcessor* rp = heap->global_generation()->ref_processor(); // enable ("weak") refs discovery rp->set_soft_reference_policy(true); // forcefully purge all soft references - ShenandoahSTWMark mark(true /*full_gc*/); + ShenandoahSTWMark mark(heap->global_generation(), true /*full_gc*/); mark.mark(); heap->parallel_cleaning(true /* full_gc */); + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ShenandoahGenerationalFullGC::log_live_in_old(heap); + } } class ShenandoahPrepareForCompactionObjectClosure : public ObjectClosure { @@ -426,8 +447,14 @@ void ShenandoahPrepareForCompactionTask::work(uint worker_id) { GrowableArray empty_regions((int)_heap->num_regions()); - ShenandoahPrepareForCompactionObjectClosure cl(_preserved_marks->get(worker_id), empty_regions, from_region); - prepare_for_compaction(cl, empty_regions, it, from_region); + if (_heap->mode()->is_generational()) { + ShenandoahPrepareForGenerationalCompactionObjectClosure cl(_preserved_marks->get(worker_id), + empty_regions, from_region, worker_id); + prepare_for_compaction(cl, empty_regions, it, from_region); + } else { + ShenandoahPrepareForCompactionObjectClosure cl(_preserved_marks->get(worker_id), empty_regions, from_region); + prepare_for_compaction(cl, empty_regions, it, from_region); + } } template @@ -474,6 +501,7 @@ void ShenandoahFullGC::calculate_target_humongous_objects() { size_t to_begin = heap->num_regions(); size_t to_end = heap->num_regions(); + log_debug(gc)("Full GC calculating target humongous objects from end " SIZE_FORMAT, to_end); for (size_t c = heap->num_regions(); c > 0; c--) { ShenandoahHeapRegion *r = heap->get_region(c - 1); if (r->is_humongous_continuation() || (r->new_top() == r->bottom())) { @@ -516,6 +544,7 @@ class ShenandoahEnsureHeapActiveClosure: public ShenandoahHeapRegionClosure { r->recycle(); } if (r->is_cset()) { + // Leave affiliation unchanged r->make_regular_bypass(); } if (r->is_empty_uncommitted()) { @@ -539,21 +568,18 @@ class ShenandoahTrashImmediateGarbageClosure: public ShenandoahHeapRegionClosure _heap(ShenandoahHeap::heap()), _ctx(ShenandoahHeap::heap()->complete_marking_context()) {} - void heap_region_do(ShenandoahHeapRegion* r) { + void heap_region_do(ShenandoahHeapRegion* r) override { if (r->is_humongous_start()) { oop humongous_obj = cast_to_oop(r->bottom()); if (!_ctx->is_marked(humongous_obj)) { - assert(!r->has_live(), - "Region " SIZE_FORMAT " is not marked, should not have live", r->index()); + assert(!r->has_live(), "Region " SIZE_FORMAT " is not marked, should not have live", r->index()); _heap->trash_humongous_region_at(r); } else { - assert(r->has_live(), - "Region " SIZE_FORMAT " should have live", r->index()); + assert(r->has_live(), "Region " SIZE_FORMAT " should have live", r->index()); } } else if (r->is_humongous_continuation()) { // If we hit continuation, the non-live humongous starts should have been trashed already - assert(r->humongous_start_region()->has_live(), - "Region " SIZE_FORMAT " should have live", r->index()); + assert(r->humongous_start_region()->has_live(), "Region " SIZE_FORMAT " should have live", r->index()); } else if (r->is_regular()) { if (!r->has_live()) { r->make_trash_immediate(); @@ -716,8 +742,9 @@ void ShenandoahFullGC::phase2_calculate_target_addresses(ShenandoahHeapRegionSet { // Trash the immediately collectible regions before computing addresses - ShenandoahTrashImmediateGarbageClosure tigcl; - heap->heap_region_iterate(&tigcl); + ShenandoahTrashImmediateGarbageClosure trash_immediate_garbage; + ShenandoahExcludeRegionClosure cl(&trash_immediate_garbage); + heap->heap_region_iterate(&cl); // Make sure regions are in good state: committed, active, clean. // This is needed because we are potentially sliding the data through them. @@ -805,6 +832,9 @@ class ShenandoahAdjustPointersTask : public WorkerTask { if (!r->is_humongous_continuation() && r->has_live()) { _heap->marked_object_iterate(r, &obj_cl); } + if (_heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::maybe_coalesce_and_fill_region(r); + } r = _regions.next(); } } @@ -909,10 +939,21 @@ class ShenandoahCompactObjectsTask : public WorkerTask { class ShenandoahPostCompactClosure : public ShenandoahHeapRegionClosure { private: ShenandoahHeap* const _heap; - size_t _live; + bool _is_generational; + size_t _young_regions, _young_usage, _young_humongous_waste; + size_t _old_regions, _old_usage, _old_humongous_waste; public: - ShenandoahPostCompactClosure() : _heap(ShenandoahHeap::heap()), _live(0) { + ShenandoahPostCompactClosure() : _heap(ShenandoahHeap::heap()), + _is_generational(_heap->mode()->is_generational()), + _young_regions(0), + _young_usage(0), + _young_humongous_waste(0), + _old_regions(0), + _old_usage(0), + _old_humongous_waste(0) + { + _heap->free_set()->clear(); } void heap_region_do(ShenandoahHeapRegion* r) { @@ -931,6 +972,10 @@ class ShenandoahPostCompactClosure : public ShenandoahHeapRegionClosure { // Make empty regions that have been allocated into regular if (r->is_empty() && live > 0) { + if (!_is_generational) { + r->make_affiliated_maybe(); + } + // else, generational mode compaction has already established affiliation. r->make_regular_bypass(); if (ZapUnusedHeapArea) { SpaceMangler::mangle_region(MemRegion(r->top(), r->end())); @@ -946,15 +991,32 @@ class ShenandoahPostCompactClosure : public ShenandoahHeapRegionClosure { if (r->is_trash()) { live = 0; r->recycle(); + } else { + if (r->is_old()) { + ShenandoahGenerationalFullGC::account_for_region(r, _old_regions, _old_usage, _old_humongous_waste); + } else if (r->is_young()) { + ShenandoahGenerationalFullGC::account_for_region(r, _young_regions, _young_usage, _young_humongous_waste); + } } - r->set_live_data(live); r->reset_alloc_metadata(); - _live += live; } - size_t get_live() { - return _live; + void update_generation_usage() { + if (_is_generational) { + _heap->old_generation()->establish_usage(_old_regions, _old_usage, _old_humongous_waste); + _heap->young_generation()->establish_usage(_young_regions, _young_usage, _young_humongous_waste); + } else { + assert(_old_regions == 0, "Old regions only expected in generational mode"); + assert(_old_usage == 0, "Old usage only expected in generational mode"); + assert(_old_humongous_waste == 0, "Old humongous waste only expected in generational mode"); + } + + // In generational mode, global usage should be the sum of young and old. This is also true + // for non-generational modes except that there are no old regions. + _heap->global_generation()->establish_usage(_old_regions + _young_regions, + _old_usage + _young_usage, + _old_humongous_waste + _young_humongous_waste); } }; @@ -985,6 +1047,7 @@ void ShenandoahFullGC::compact_humongous_objects() { assert(old_start != new_start, "must be real move"); assert(r->is_stw_move_allowed(), "Region " SIZE_FORMAT " should be movable", r->index()); + log_debug(gc)("Full GC compaction moves humongous object from region " SIZE_FORMAT " to region " SIZE_FORMAT, old_start, new_start); Copy::aligned_conjoint_words(r->bottom(), heap->get_region(new_start)->bottom(), words_size); ContinuationGCSupport::relativize_stack_chunk(cast_to_oop(r->bottom())); @@ -992,8 +1055,10 @@ void ShenandoahFullGC::compact_humongous_objects() { new_obj->init_mark(); { + ShenandoahAffiliation original_affiliation = r->affiliation(); for (size_t c = old_start; c <= old_end; c++) { ShenandoahHeapRegion* r = heap->get_region(c); + // Leave humongous region affiliation unchanged. r->make_regular_bypass(); r->set_top(r->bottom()); } @@ -1001,9 +1066,9 @@ void ShenandoahFullGC::compact_humongous_objects() { for (size_t c = new_start; c <= new_end; c++) { ShenandoahHeapRegion* r = heap->get_region(c); if (c == new_start) { - r->make_humongous_start_bypass(); + r->make_humongous_start_bypass(original_affiliation); } else { - r->make_humongous_cont_bypass(); + r->make_humongous_cont_bypass(original_affiliation); } // Trailing region may be non-full, record the remainder there @@ -1088,13 +1153,35 @@ void ShenandoahFullGC::phase5_epilog() { ShenandoahGCPhase phase(ShenandoahPhaseTimings::full_gc_copy_objects_rebuild); ShenandoahPostCompactClosure post_compact; heap->heap_region_iterate(&post_compact); - heap->set_used(post_compact.get_live()); + post_compact.update_generation_usage(); + + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::balance_generations_after_gc(heap); + } heap->collection_set()->clear(); - heap->free_set()->rebuild(); - heap->clear_cancelled_gc(); + size_t young_cset_regions, old_cset_regions; + size_t first_old, last_old, num_old; + heap->free_set()->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + + // We also do not expand old generation size following Full GC because we have scrambled age populations and + // no longer have objects separated by age into distinct regions. + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::compute_balances(); + } + + heap->free_set()->finish_rebuild(young_cset_regions, old_cset_regions, num_old); + + heap->clear_cancelled_gc(true /* clear oom handler */); } _preserved_marks->restore(heap->workers()); _preserved_marks->reclaim(); + + // We defer generation resizing actions until after cset regions have been recycled. We do this even following an + // abbreviated cycle. + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::balance_generations_after_rebuilding_free_set(); + ShenandoahGenerationalFullGC::rebuild_remembered_set(heap); + } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp index 719abde4b16..99ee88d98d1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGC.cpp @@ -39,6 +39,8 @@ const char* ShenandoahGC::degen_point_to_string(ShenandoahDegenPoint point) { return ""; case _degenerated_outside_cycle: return "Outside of Cycle"; + case _degenerated_roots: + return "Roots"; case _degenerated_mark: return "Mark"; case _degenerated_evac: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp index a1714b52372..37b22848917 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp @@ -50,6 +50,7 @@ class ShenandoahGC : public StackObj { enum ShenandoahDegenPoint { _degenerated_unset, _degenerated_outside_cycle, + _degenerated_roots, _degenerated_mark, _degenerated_evac, _degenerated_updaterefs, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp new file mode 100644 index 00000000000..1c644a9accc --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -0,0 +1,1035 @@ +/* + * Copyright Amazon.com Inc. 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" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahCollectionSetPreselector.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahVerifier.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" + +#include "utilities/quickSort.hpp" + + +class ShenandoahResetUpdateRegionStateClosure : public ShenandoahHeapRegionClosure { +private: + ShenandoahHeap* _heap; + ShenandoahMarkingContext* const _ctx; +public: + ShenandoahResetUpdateRegionStateClosure() : + _heap(ShenandoahHeap::heap()), + _ctx(_heap->marking_context()) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + if (r->is_active()) { + // Reset live data and set TAMS optimistically. We would recheck these under the pause + // anyway to capture any updates that happened since now. + _ctx->capture_top_at_mark_start(r); + r->clear_live_data(); + } + } + + bool is_thread_safe() override { return true; } +}; + +class ShenandoahResetBitmapTask : public WorkerTask { +private: + ShenandoahRegionIterator _regions; + ShenandoahGeneration* _generation; + +public: + ShenandoahResetBitmapTask(ShenandoahGeneration* generation) : + WorkerTask("Shenandoah Reset Bitmap"), _generation(generation) {} + + void work(uint worker_id) { + ShenandoahHeapRegion* region = _regions.next(); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahMarkingContext* const ctx = heap->marking_context(); + while (region != nullptr) { + auto const affiliation = region->affiliation(); + bool needs_reset = affiliation == FREE || _generation->contains(affiliation); + if (needs_reset && heap->is_bitmap_slice_committed(region)) { + ctx->clear_bitmap(region); + } + region = _regions.next(); + } + } +}; + +// Copy the write-version of the card-table into the read-version, clearing the +// write-copy. +class ShenandoahMergeWriteTable: public ShenandoahHeapRegionClosure { +private: + ShenandoahScanRemembered* _scanner; +public: + ShenandoahMergeWriteTable(ShenandoahScanRemembered* scanner) : _scanner(scanner) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + assert(r->is_old(), "Don't waste time doing this for non-old regions"); + _scanner->merge_write_table(r->bottom(), ShenandoahHeapRegion::region_size_words()); + } + + bool is_thread_safe() override { + return true; + } +}; + +class ShenandoahCopyWriteCardTableToRead: public ShenandoahHeapRegionClosure { +private: + ShenandoahScanRemembered* _scanner; +public: + ShenandoahCopyWriteCardTableToRead(ShenandoahScanRemembered* scanner) : _scanner(scanner) {} + + void heap_region_do(ShenandoahHeapRegion* region) override { + assert(region->is_old(), "Don't waste time doing this for non-old regions"); + _scanner->reset_remset(region->bottom(), ShenandoahHeapRegion::region_size_words()); + } + + bool is_thread_safe() override { return true; } +}; + +// Add [TAMS, top) volume over young regions. Used to correct age 0 cohort census +// for adaptive tenuring when census is taken during marking. +// In non-product builds, for the purposes of verification, we also collect the total +// live objects in young regions as well. +class ShenandoahUpdateCensusZeroCohortClosure : public ShenandoahHeapRegionClosure { +private: + ShenandoahMarkingContext* const _ctx; + // Population size units are words (not bytes) + size_t _age0_pop; // running tally of age0 population size + size_t _total_pop; // total live population size +public: + explicit ShenandoahUpdateCensusZeroCohortClosure(ShenandoahMarkingContext* ctx) + : _ctx(ctx), _age0_pop(0), _total_pop(0) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + if (_ctx != nullptr && r->is_active()) { + assert(r->is_young(), "Young regions only"); + HeapWord* tams = _ctx->top_at_mark_start(r); + HeapWord* top = r->top(); + if (top > tams) { + _age0_pop += pointer_delta(top, tams); + } + // TODO: check significance of _ctx != nullptr above, can that + // spoof _total_pop in some corner cases? + NOT_PRODUCT(_total_pop += r->get_live_data_words();) + } + } + + size_t get_age0_population() const { return _age0_pop; } + size_t get_total_population() const { return _total_pop; } +}; + +void ShenandoahGeneration::confirm_heuristics_mode() { + if (_heuristics->is_diagnostic() && !UnlockDiagnosticVMOptions) { + vm_exit_during_initialization( + err_msg("Heuristics \"%s\" is diagnostic, and must be enabled via -XX:+UnlockDiagnosticVMOptions.", + _heuristics->name())); + } + if (_heuristics->is_experimental() && !UnlockExperimentalVMOptions) { + vm_exit_during_initialization( + err_msg("Heuristics \"%s\" is experimental, and must be enabled via -XX:+UnlockExperimentalVMOptions.", + _heuristics->name())); + } +} + +ShenandoahHeuristics* ShenandoahGeneration::initialize_heuristics(ShenandoahMode* gc_mode) { + _heuristics = gc_mode->initialize_heuristics(this); + _heuristics->set_guaranteed_gc_interval(ShenandoahGuaranteedGCInterval); + confirm_heuristics_mode(); + return _heuristics; +} + +size_t ShenandoahGeneration::bytes_allocated_since_gc_start() const { + return Atomic::load(&_bytes_allocated_since_gc_start); +} + +void ShenandoahGeneration::reset_bytes_allocated_since_gc_start() { + Atomic::store(&_bytes_allocated_since_gc_start, (size_t)0); +} + +void ShenandoahGeneration::increase_allocated(size_t bytes) { + Atomic::add(&_bytes_allocated_since_gc_start, bytes, memory_order_relaxed); +} + +void ShenandoahGeneration::set_evacuation_reserve(size_t new_val) { + _evacuation_reserve = new_val; +} + +size_t ShenandoahGeneration::get_evacuation_reserve() const { + return _evacuation_reserve; +} + +void ShenandoahGeneration::augment_evacuation_reserve(size_t increment) { + _evacuation_reserve += increment; +} + +void ShenandoahGeneration::log_status(const char *msg) const { + typedef LogTarget(Info, gc, ergo) LogGcInfo; + + if (!LogGcInfo::is_enabled()) { + return; + } + + // Not under a lock here, so read each of these once to make sure + // byte size in proper unit and proper unit for byte size are consistent. + size_t v_used = used(); + size_t v_used_regions = used_regions_size(); + size_t v_soft_max_capacity = soft_max_capacity(); + size_t v_max_capacity = max_capacity(); + size_t v_available = available(); + size_t v_humongous_waste = get_humongous_waste(); + LogGcInfo::print("%s: %s generation used: " SIZE_FORMAT "%s, used regions: " SIZE_FORMAT "%s, " + "humongous waste: " SIZE_FORMAT "%s, soft capacity: " SIZE_FORMAT "%s, max capacity: " SIZE_FORMAT "%s, " + "available: " SIZE_FORMAT "%s", msg, name(), + byte_size_in_proper_unit(v_used), proper_unit_for_byte_size(v_used), + byte_size_in_proper_unit(v_used_regions), proper_unit_for_byte_size(v_used_regions), + byte_size_in_proper_unit(v_humongous_waste), proper_unit_for_byte_size(v_humongous_waste), + byte_size_in_proper_unit(v_soft_max_capacity), proper_unit_for_byte_size(v_soft_max_capacity), + byte_size_in_proper_unit(v_max_capacity), proper_unit_for_byte_size(v_max_capacity), + byte_size_in_proper_unit(v_available), proper_unit_for_byte_size(v_available)); +} + +void ShenandoahGeneration::reset_mark_bitmap() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + heap->assert_gc_workers(heap->workers()->active_workers()); + + set_mark_incomplete(); + + ShenandoahResetBitmapTask task(this); + heap->workers()->run_task(&task); +} + +// The ideal is to swap the remembered set so the safepoint effort is no more than a few pointer manipulations. +// However, limitations in the implementation of the mutator write-barrier make it difficult to simply change the +// location of the card table. So the interim implementation of swap_remembered_set will copy the write-table +// onto the read-table and will then clear the write-table. +void ShenandoahGeneration::swap_remembered_set() { + // Must be sure that marking is complete before we swap remembered set. + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + heap->assert_gc_workers(heap->workers()->active_workers()); + shenandoah_assert_safepoint(); + + ShenandoahOldGeneration* old_generation = heap->old_generation(); + ShenandoahCopyWriteCardTableToRead task(old_generation->card_scan()); + old_generation->parallel_heap_region_iterate(&task); +} + +// Copy the write-version of the card-table into the read-version, clearing the +// write-version. The work is done at a safepoint and in parallel by the GC +// worker threads. +void ShenandoahGeneration::merge_write_table() { + // This should only happen for degenerated cycles + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + heap->assert_gc_workers(heap->workers()->active_workers()); + shenandoah_assert_safepoint(); + + ShenandoahOldGeneration* old_generation = heap->old_generation(); + ShenandoahMergeWriteTable task(old_generation->card_scan()); + old_generation->parallel_heap_region_iterate(&task); +} + +void ShenandoahGeneration::prepare_gc() { + + reset_mark_bitmap(); + + // Capture Top At Mark Start for this generation (typically young) and reset mark bitmap. + ShenandoahResetUpdateRegionStateClosure cl; + parallel_heap_region_iterate_free(&cl); +} + +void ShenandoahGeneration::parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) { + ShenandoahHeap::heap()->parallel_heap_region_iterate(cl); +} + +void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap) { + shenandoah_assert_generational(); + + ShenandoahOldGeneration* const old_generation = heap->old_generation(); + ShenandoahYoungGeneration* const young_generation = heap->young_generation(); + + // During initialization and phase changes, it is more likely that fewer objects die young and old-gen + // memory is not yet full (or is in the process of being replaced). During these times especially, it + // is beneficial to loan memory from old-gen to young-gen during the evacuation and update-refs phases + // of execution. + + // Calculate EvacuationReserve before PromotionReserve. Evacuation is more critical than promotion. + // If we cannot evacuate old-gen, we will not be able to reclaim old-gen memory. Promotions are less + // critical. If we cannot promote, there may be degradation of young-gen memory because old objects + // accumulate there until they can be promoted. This increases the young-gen marking and evacuation work. + + // First priority is to reclaim the easy garbage out of young-gen. + + // maximum_young_evacuation_reserve is upper bound on memory to be evacuated out of young + const size_t maximum_young_evacuation_reserve = (young_generation->max_capacity() * ShenandoahEvacReserve) / 100; + const size_t young_evacuation_reserve = MIN2(maximum_young_evacuation_reserve, young_generation->available_with_reserve()); + + // maximum_old_evacuation_reserve is an upper bound on memory evacuated from old and evacuated to old (promoted), + // clamped by the old generation space available. + // + // Here's the algebra. + // Let SOEP = ShenandoahOldEvacRatioPercent, + // OE = old evac, + // YE = young evac, and + // TE = total evac = OE + YE + // By definition: + // SOEP/100 = OE/TE + // = OE/(OE+YE) + // => SOEP/(100-SOEP) = OE/((OE+YE)-OE) // componendo-dividendo: If a/b = c/d, then a/(b-a) = c/(d-c) + // = OE/YE + // => OE = YE*SOEP/(100-SOEP) + + // We have to be careful in the event that SOEP is set to 100 by the user. + assert(ShenandoahOldEvacRatioPercent <= 100, "Error"); + const size_t old_available = old_generation->available(); + const size_t maximum_old_evacuation_reserve = (ShenandoahOldEvacRatioPercent == 100) ? + old_available : MIN2((maximum_young_evacuation_reserve * ShenandoahOldEvacRatioPercent) / (100 - ShenandoahOldEvacRatioPercent), + old_available); + + + // Second priority is to reclaim garbage out of old-gen if there are old-gen collection candidates. Third priority + // is to promote as much as we have room to promote. However, if old-gen memory is in short supply, this means young + // GC is operating under "duress" and was unable to transfer the memory that we would normally expect. In this case, + // old-gen will refrain from compacting itself in order to allow a quicker young-gen cycle (by avoiding the update-refs + // through ALL of old-gen). If there is some memory available in old-gen, we will use this for promotions as promotions + // do not add to the update-refs burden of GC. + + size_t old_evacuation_reserve, old_promo_reserve; + if (is_global()) { + // Global GC is typically triggered by user invocation of System.gc(), and typically indicates that there is lots + // of garbage to be reclaimed because we are starting a new phase of execution. Marking for global GC may take + // significantly longer than typical young marking because we must mark through all old objects. To expedite + // evacuation and update-refs, we give emphasis to reclaiming garbage first, wherever that garbage is found. + // Global GC will adjust generation sizes to accommodate the collection set it chooses. + + // Set old_promo_reserve to enforce that no regions are preselected for promotion. Such regions typically + // have relatively high memory utilization. We still call select_aged_regions() because this will prepare for + // promotions in place, if relevant. + old_promo_reserve = 0; + + // Dedicate all available old memory to old_evacuation reserve. This may be small, because old-gen is only + // expanded based on an existing mixed evacuation workload at the end of the previous GC cycle. We'll expand + // the budget for evacuation of old during GLOBAL cset selection. + old_evacuation_reserve = maximum_old_evacuation_reserve; + } else if (old_generation->has_unprocessed_collection_candidates()) { + // We reserved all old-gen memory at end of previous GC to hold anticipated evacuations to old-gen. If this is + // mixed evacuation, reserve all of this memory for compaction of old-gen and do not promote. Prioritize compaction + // over promotion in order to defragment OLD so that it will be better prepared to efficiently receive promoted memory. + old_evacuation_reserve = maximum_old_evacuation_reserve; + old_promo_reserve = 0; + } else { + // Make all old-evacuation memory for promotion, but if we can't use it all for promotion, we'll allow some evacuation. + old_evacuation_reserve = 0; + old_promo_reserve = maximum_old_evacuation_reserve; + } + assert(old_evacuation_reserve <= old_available, "Error"); + + // We see too many old-evacuation failures if we force ourselves to evacuate into regions that are not initially empty. + // So we limit the old-evacuation reserve to unfragmented memory. Even so, old-evacuation is free to fill in nooks and + // crannies within existing partially used regions and it generally tries to do so. + const size_t old_free_unfragmented = old_generation->free_unaffiliated_regions() * ShenandoahHeapRegion::region_size_bytes(); + if (old_evacuation_reserve > old_free_unfragmented) { + const size_t delta = old_evacuation_reserve - old_free_unfragmented; + old_evacuation_reserve -= delta; + // Let promo consume fragments of old-gen memory if not global + if (!is_global()) { + old_promo_reserve += delta; + } + } + + // Preselect regions for promotion by evacuation (obtaining the live data to seed promoted_reserve), + // and identify regions that will promote in place. These use the tenuring threshold. + const size_t consumed_by_advance_promotion = select_aged_regions(old_promo_reserve); + assert(consumed_by_advance_promotion <= maximum_old_evacuation_reserve, "Cannot promote more than available old-gen memory"); + + // Note that unused old_promo_reserve might not be entirely consumed_by_advance_promotion. Do not transfer this + // to old_evacuation_reserve because this memory is likely very fragmented, and we do not want to increase the likelihood + // of old evacuation failure. + young_generation->set_evacuation_reserve(young_evacuation_reserve); + old_generation->set_evacuation_reserve(old_evacuation_reserve); + old_generation->set_promoted_reserve(consumed_by_advance_promotion); + + // There is no need to expand OLD because all memory used here was set aside at end of previous GC, except in the + // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. +} + +// Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note +// that young_generation->available() now knows about recently discovered immediate garbage. +// +void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, ShenandoahCollectionSet* const collection_set) { + shenandoah_assert_generational(); + // We may find that old_evacuation_reserve and/or loaned_for_young_evacuation are not fully consumed, in which case we may + // be able to increase regions_available_to_loan + + // The role of adjust_evacuation_budgets() is to compute the correct value of regions_available_to_loan and to make + // effective use of this memory, including the remnant memory within these regions that may result from rounding loan to + // integral number of regions. Excess memory that is available to be loaned is applied to an allocation supplement, + // which allows mutators to allocate memory beyond the current capacity of young-gen on the promise that the loan + // will be repaid as soon as we finish updating references for the recently evacuated collection set. + + // We cannot recalculate regions_available_to_loan by simply dividing old_generation->available() by region_size_bytes + // because the available memory may be distributed between many partially occupied regions that are already holding old-gen + // objects. Memory in partially occupied regions is not "available" to be loaned. Note that an increase in old-gen + // available that results from a decrease in memory consumed by old evacuation is not necessarily available to be loaned + // to young-gen. + + size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + ShenandoahOldGeneration* const old_generation = heap->old_generation(); + ShenandoahYoungGeneration* const young_generation = heap->young_generation(); + + size_t old_evacuated = collection_set->get_old_bytes_reserved_for_evacuation(); + size_t old_evacuated_committed = (size_t) (ShenandoahOldEvacWaste * double(old_evacuated)); + size_t old_evacuation_reserve = old_generation->get_evacuation_reserve(); + + if (old_evacuated_committed > old_evacuation_reserve) { + // This should only happen due to round-off errors when enforcing ShenandoahOldEvacWaste + assert(old_evacuated_committed <= (33 * old_evacuation_reserve) / 32, + "Round-off errors should be less than 3.125%%, committed: " SIZE_FORMAT ", reserved: " SIZE_FORMAT, + old_evacuated_committed, old_evacuation_reserve); + old_evacuated_committed = old_evacuation_reserve; + // Leave old_evac_reserve as previously configured + } else if (old_evacuated_committed < old_evacuation_reserve) { + // This happens if the old-gen collection consumes less than full budget. + old_evacuation_reserve = old_evacuated_committed; + old_generation->set_evacuation_reserve(old_evacuation_reserve); + } + + size_t young_advance_promoted = collection_set->get_young_bytes_to_be_promoted(); + size_t young_advance_promoted_reserve_used = (size_t) (ShenandoahPromoEvacWaste * double(young_advance_promoted)); + + size_t young_evacuated = collection_set->get_young_bytes_reserved_for_evacuation(); + size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); + + size_t total_young_available = young_generation->available_with_reserve(); + assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate more than is available in young"); + young_generation->set_evacuation_reserve(young_evacuated_reserve_used); + + size_t old_available = old_generation->available(); + // Now that we've established the collection set, we know how much memory is really required by old-gen for evacuation + // and promotion reserves. Try shrinking OLD now in case that gives us a bit more runway for mutator allocations during + // evac and update phases. + size_t old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; + + if (old_available < old_consumed) { + // This can happen due to round-off errors when adding the results of truncated integer arithmetic. + // We've already truncated old_evacuated_committed. Truncate young_advance_promoted_reserve_used here. + assert(young_advance_promoted_reserve_used <= (33 * (old_available - old_evacuated_committed)) / 32, + "Round-off errors should be less than 3.125%%, committed: " SIZE_FORMAT ", reserved: " SIZE_FORMAT, + young_advance_promoted_reserve_used, old_available - old_evacuated_committed); + young_advance_promoted_reserve_used = old_available - old_evacuated_committed; + old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; + } + + assert(old_available >= old_consumed, "Cannot consume (" SIZE_FORMAT ") more than is available (" SIZE_FORMAT ")", + old_consumed, old_available); + size_t excess_old = old_available - old_consumed; + size_t unaffiliated_old_regions = old_generation->free_unaffiliated_regions(); + size_t unaffiliated_old = unaffiliated_old_regions * region_size_bytes; + assert(old_available >= unaffiliated_old, "Unaffiliated old is a subset of old available"); + + // Make sure old_evac_committed is unaffiliated + if (old_evacuated_committed > 0) { + if (unaffiliated_old > old_evacuated_committed) { + size_t giveaway = unaffiliated_old - old_evacuated_committed; + size_t giveaway_regions = giveaway / region_size_bytes; // round down + if (giveaway_regions > 0) { + excess_old = MIN2(excess_old, giveaway_regions * region_size_bytes); + } else { + excess_old = 0; + } + } else { + excess_old = 0; + } + } + + // If we find that OLD has excess regions, give them back to YOUNG now to reduce likelihood we run out of allocation + // runway during evacuation and update-refs. + size_t regions_to_xfer = 0; + if (excess_old > unaffiliated_old) { + // we can give back unaffiliated_old (all of unaffiliated is excess) + if (unaffiliated_old_regions > 0) { + regions_to_xfer = unaffiliated_old_regions; + } + } else if (unaffiliated_old_regions > 0) { + // excess_old < unaffiliated old: we can give back MIN(excess_old/region_size_bytes, unaffiliated_old_regions) + size_t excess_regions = excess_old / region_size_bytes; + regions_to_xfer = MIN2(excess_regions, unaffiliated_old_regions); + } + + if (regions_to_xfer > 0) { + bool result = ShenandoahGenerationalHeap::cast(heap)->generation_sizer()->transfer_to_young(regions_to_xfer); + assert(excess_old >= regions_to_xfer * region_size_bytes, + "Cannot transfer (" SIZE_FORMAT ", " SIZE_FORMAT ") more than excess old (" SIZE_FORMAT ")", + regions_to_xfer, region_size_bytes, excess_old); + excess_old -= regions_to_xfer * region_size_bytes; + log_debug(gc, ergo)("%s transferred " SIZE_FORMAT " excess regions to young before start of evacuation", + result? "Successfully": "Unsuccessfully", regions_to_xfer); + } + + // Add in the excess_old memory to hold unanticipated promotions, if any. If there are more unanticipated + // promotions than fit in reserved memory, they will be deferred until a future GC pass. + size_t total_promotion_reserve = young_advance_promoted_reserve_used + excess_old; + old_generation->set_promoted_reserve(total_promotion_reserve); + old_generation->reset_promoted_expended(); +} + +typedef struct { + ShenandoahHeapRegion* _region; + size_t _live_data; +} AgedRegionData; + +static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { + if (a._live_data < b._live_data) + return -1; + else if (a._live_data > b._live_data) + return 1; + else return 0; +} + +inline void assert_no_in_place_promotions() { +#ifdef ASSERT + class ShenandoahNoInPlacePromotions : public ShenandoahHeapRegionClosure { + public: + void heap_region_do(ShenandoahHeapRegion *r) override { + assert(r->get_top_before_promote() == nullptr, + "Region " SIZE_FORMAT " should not be ready for in-place promotion", r->index()); + } + } cl; + ShenandoahHeap::heap()->heap_region_iterate(&cl); +#endif +} + +// Preselect for inclusion into the collection set regions whose age is at or above tenure age which contain more than +// ShenandoahOldGarbageThreshold amounts of garbage. We identify these regions by setting the appropriate entry of +// the collection set's preselected regions array to true. All entries are initialized to false before calling this +// function. +// +// During the subsequent selection of the collection set, we give priority to these promotion set candidates. +// Without this prioritization, we found that the aged regions tend to be ignored because they typically have +// much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are +// repeatedly excluded from the collection set, the amount of live memory within the young generation tends to +// accumulate and this has the undesirable side effect of causing young-generation collections to require much more +// CPU and wall-clock time. +// +// A second benefit of treating aged regions differently than other regions during collection set selection is +// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation +// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be +// reserved in the young generation. +size_t ShenandoahGeneration::select_aged_regions(size_t old_available) { + + // There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle. + assert_no_in_place_promotions(); + + auto const heap = ShenandoahGenerationalHeap::heap(); + bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions(); + ShenandoahMarkingContext* const ctx = heap->marking_context(); + + const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); + const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100; + + size_t old_consumed = 0; + size_t promo_potential = 0; + size_t candidates = 0; + + // Tracks the padding of space above top in regions eligible for promotion in place + size_t promote_in_place_pad = 0; + + // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require + // less evacuation effort. This prioritizes garbage first, expanding the allocation pool early before we reclaim regions that + // have more live data. + const size_t num_regions = heap->num_regions(); + + ResourceMark rm; + AgedRegionData* sorted_regions = NEW_RESOURCE_ARRAY(AgedRegionData, num_regions); + + for (size_t i = 0; i < num_regions; i++) { + ShenandoahHeapRegion* const r = heap->get_region(i); + if (r->is_empty() || !r->has_live() || !r->is_young() || !r->is_regular()) { + // skip over regions that aren't regular young with some live data + continue; + } + if (r->age() >= tenuring_threshold) { + if ((r->garbage() < old_garbage_threshold)) { + // This tenure-worthy region has too little garbage, so we do not want to expend the copying effort to + // reclaim the garbage; instead this region may be eligible for promotion-in-place to the + // old generation. + HeapWord* tams = ctx->top_at_mark_start(r); + HeapWord* original_top = r->top(); + if (!heap->is_concurrent_old_mark_in_progress() && tams == original_top) { + // No allocations from this region have been made during concurrent mark. It meets all the criteria + // for in-place-promotion. Though we only need the value of top when we fill the end of the region, + // we use this field to indicate that this region should be promoted in place during the evacuation + // phase. + r->save_top_before_promote(); + + size_t remnant_size = r->free() / HeapWordSize; + if (remnant_size > ShenandoahHeap::min_fill_size()) { + ShenandoahHeap::fill_with_object(original_top, remnant_size); + // Fill the remnant memory within this region to assure no allocations prior to promote in place. Otherwise, + // newly allocated objects will not be parsable when promote in place tries to register them. Furthermore, any + // new allocations would not necessarily be eligible for promotion. This addresses both issues. + r->set_top(r->end()); + promote_in_place_pad += remnant_size * HeapWordSize; + } else { + // Since the remnant is so small that it cannot be filled, we don't have to worry about any accidental + // allocations occurring within this region before the region is promoted in place. + } + } + // Else, we do not promote this region (either in place or by copy) because it has received new allocations. + + // During evacuation, we exclude from promotion regions for which age > tenure threshold, garbage < garbage-threshold, + // and get_top_before_promote() != tams + } else { + // Record this promotion-eligible candidate region. After sorting and selecting the best candidates below, + // we may still decide to exclude this promotion-eligible region from the current collection set. If this + // happens, we will consider this region as part of the anticipated promotion potential for the next GC + // pass; see further below. + sorted_regions[candidates]._region = r; + sorted_regions[candidates++]._live_data = r->get_live_data_bytes(); + } + } else { + // We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold. + // Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to + // old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that + // the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes + // place during a subsequent GC pass because more garbage is found within the region between now and then. This + // should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold + // is to choose the youngest age that demonstrates no "significant" further loss of population since the previous + // age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population + // samples, whereas we expect to observe exponential population decay for ages younger than the tenure age. + // + // In the case that certain regions which were anticipated to be promoted in place need to be promoted by + // evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of + // these regions. The likely outcome is that these regions will not be selected for evacuation or promotion + // in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause + // us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle. + if (heap->is_aging_cycle() && (r->age() + 1 == tenuring_threshold)) { + if (r->garbage() >= old_garbage_threshold) { + promo_potential += r->get_live_data_bytes(); + } + } + } + // Note that we keep going even if one region is excluded from selection. + // Subsequent regions may be selected if they have smaller live data. + } + // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions + // that qualify to be promoted by evacuation. + if (candidates > 0) { + size_t selected_regions = 0; + size_t selected_live = 0; + QuickSort::sort(sorted_regions, candidates, compare_by_aged_live); + for (size_t i = 0; i < candidates; i++) { + ShenandoahHeapRegion* const region = sorted_regions[i]._region; + size_t region_live_data = sorted_regions[i]._live_data; + size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste); + if (old_consumed + promotion_need <= old_available) { + old_consumed += promotion_need; + candidate_regions_for_promotion_by_copy[region->index()] = true; + selected_regions++; + selected_live += region_live_data; + } else { + // We rejected this promotable region from the collection set because we had no room to hold its copy. + // Add this region to promo potential for next GC. + promo_potential += region_live_data; + assert(!candidate_regions_for_promotion_by_copy[region->index()], "Shouldn't be selected"); + } + // We keep going even if one region is excluded from selection because we need to accumulate all eligible + // regions that are not preselected into promo_potential + } + log_debug(gc)("Preselected " SIZE_FORMAT " regions containing " SIZE_FORMAT " live bytes," + " consuming: " SIZE_FORMAT " of budgeted: " SIZE_FORMAT, + selected_regions, selected_live, old_consumed, old_available); + } + + heap->old_generation()->set_pad_for_promote_in_place(promote_in_place_pad); + heap->old_generation()->set_promotion_potential(promo_potential); + return old_consumed; +} + +void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahCollectionSet* collection_set = heap->collection_set(); + bool is_generational = heap->mode()->is_generational(); + + assert(!heap->is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); + assert(!is_old(), "Only YOUNG and GLOBAL GC perform evacuations"); + { + ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_update_region_states : + ShenandoahPhaseTimings::degen_gc_final_update_region_states); + ShenandoahFinalMarkUpdateRegionStateClosure cl(complete_marking_context()); + parallel_heap_region_iterate(&cl); + + if (is_young()) { + // We always need to update the watermark for old regions. If there + // are mixed collections pending, we also need to synchronize the + // pinned status for old regions. Since we are already visiting every + // old region here, go ahead and sync the pin status too. + ShenandoahFinalMarkUpdateRegionStateClosure old_cl(nullptr); + heap->old_generation()->parallel_heap_region_iterate(&old_cl); + } + } + + // Tally the census counts and compute the adaptive tenuring threshold + if (is_generational && ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) { + // Objects above TAMS weren't included in the age census. Since they were all + // allocated in this cycle they belong in the age 0 cohort. We walk over all + // young regions and sum the volume of objects between TAMS and top. + ShenandoahUpdateCensusZeroCohortClosure age0_cl(complete_marking_context()); + heap->young_generation()->heap_region_iterate(&age0_cl); + size_t age0_pop = age0_cl.get_age0_population(); + + // Update the global census, including the missed age 0 cohort above, + // along with the census done during marking, and compute the tenuring threshold. + ShenandoahAgeCensus* census = ShenandoahGenerationalHeap::heap()->age_census(); + census->update_census(age0_pop); +#ifndef PRODUCT + size_t total_pop = age0_cl.get_total_population(); + size_t total_census = census->get_total(); + // Usually total_pop > total_census, but not by too much. + // We use integer division so anything up to just less than 2 is considered + // reasonable, and the "+1" is to avoid divide-by-zero. + assert((total_pop+1)/(total_census+1) == 1, "Extreme divergence: " + SIZE_FORMAT "/" SIZE_FORMAT, total_pop, total_census); +#endif + } + + { + ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::choose_cset : + ShenandoahPhaseTimings::degen_gc_choose_cset); + + collection_set->clear(); + ShenandoahHeapLocker locker(heap->lock()); + if (is_generational) { + // Seed the collection set with resource area-allocated + // preselected regions, which are removed when we exit this scope. + ShenandoahCollectionSetPreselector preselector(collection_set, heap->num_regions()); + + // Find the amount that will be promoted, regions that will be promoted in + // place, and preselect older regions that will be promoted by evacuation. + compute_evacuation_budgets(heap); + + // Choose the collection set, including the regions preselected above for + // promotion into the old generation. + _heuristics->choose_collection_set(collection_set); + if (!collection_set->is_empty()) { + // only make use of evacuation budgets when we are evacuating + adjust_evacuation_budgets(heap, collection_set); + } + + if (is_global()) { + // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so + // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will + // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, + // we prepare for old collections by remembering which regions are old at this time. Note that any objects + // promoted into old regions will be above TAMS, and so will be considered marked. However, free regions that + // become old after this point will not be covered correctly by the mark bitmap, so we must be careful not to + // coalesce those regions. Only the old regions which are not part of the collection set at this point are + // eligible for coalescing. As implemented now, this has the side effect of possibly initiating mixed-evacuations + // after a global cycle for old regions that were not included in this collection set. + heap->old_generation()->prepare_for_mixed_collections_after_global_gc(); + } + } else { + _heuristics->choose_collection_set(collection_set); + } + } + + + { + ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : + ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); + ShenandoahHeapLocker locker(heap->lock()); + size_t young_cset_regions, old_cset_regions; + + // We are preparing for evacuation. At this time, we ignore cset region tallies. + size_t first_old, last_old, num_old; + heap->free_set()->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + // Free set construction uses reserve quantities, because they are known to be valid here + heap->free_set()->finish_rebuild(young_cset_regions, old_cset_regions, num_old, true); + } +} + +bool ShenandoahGeneration::is_bitmap_clear() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahMarkingContext* context = heap->marking_context(); + const size_t num_regions = heap->num_regions(); + for (size_t idx = 0; idx < num_regions; idx++) { + ShenandoahHeapRegion* r = heap->get_region(idx); + if (contains(r) && r->is_affiliated()) { + if (heap->is_bitmap_slice_committed(r) && (context->top_at_mark_start(r) > r->bottom()) && + !context->is_bitmap_range_within_region_clear(r->bottom(), r->end())) { + return false; + } + } + } + return true; +} + +bool ShenandoahGeneration::is_mark_complete() { + return _is_marking_complete.is_set(); +} + +void ShenandoahGeneration::set_mark_complete() { + _is_marking_complete.set(); +} + +void ShenandoahGeneration::set_mark_incomplete() { + _is_marking_complete.unset(); +} + +ShenandoahMarkingContext* ShenandoahGeneration::complete_marking_context() { + assert(is_mark_complete(), "Marking must be completed."); + return ShenandoahHeap::heap()->marking_context(); +} + +void ShenandoahGeneration::cancel_marking() { + log_info(gc)("Cancel marking: %s", name()); + if (is_concurrent_mark_in_progress()) { + set_mark_incomplete(); + } + _task_queues->clear(); + ref_processor()->abandon_partial_discovery(); + set_concurrent_mark_in_progress(false); +} + +ShenandoahGeneration::ShenandoahGeneration(ShenandoahGenerationType type, + uint max_workers, + size_t max_capacity, + size_t soft_max_capacity) : + _type(type), + _task_queues(new ShenandoahObjToScanQueueSet(max_workers)), + _ref_processor(new ShenandoahReferenceProcessor(MAX2(max_workers, 1U))), + _affiliated_region_count(0), _humongous_waste(0), _evacuation_reserve(0), + _used(0), _bytes_allocated_since_gc_start(0), + _max_capacity(max_capacity), _soft_max_capacity(soft_max_capacity), + _heuristics(nullptr) +{ + _is_marking_complete.set(); + assert(max_workers > 0, "At least one queue"); + for (uint i = 0; i < max_workers; ++i) { + ShenandoahObjToScanQueue* task_queue = new ShenandoahObjToScanQueue(); + _task_queues->register_queue(i, task_queue); + } +} + +ShenandoahGeneration::~ShenandoahGeneration() { + for (uint i = 0; i < _task_queues->size(); ++i) { + ShenandoahObjToScanQueue* q = _task_queues->queue(i); + delete q; + } + delete _task_queues; +} + +void ShenandoahGeneration::reserve_task_queues(uint workers) { + _task_queues->reserve(workers); +} + +ShenandoahObjToScanQueueSet* ShenandoahGeneration::old_gen_task_queues() const { + return nullptr; +} + +void ShenandoahGeneration::scan_remembered_set(bool is_concurrent) { + assert(is_young(), "Should only scan remembered set for young generation."); + + ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); + uint nworkers = heap->workers()->active_workers(); + reserve_task_queues(nworkers); + + ShenandoahReferenceProcessor* rp = ref_processor(); + ShenandoahRegionChunkIterator work_list(nworkers); + ShenandoahScanRememberedTask task(task_queues(), old_gen_task_queues(), rp, &work_list, is_concurrent); + heap->assert_gc_workers(nworkers); + heap->workers()->run_task(&task); + if (ShenandoahEnableCardStats) { + ShenandoahScanRemembered* scanner = heap->old_generation()->card_scan(); + assert(scanner != nullptr, "Not generational"); + scanner->log_card_stats(nworkers, CARD_STAT_SCAN_RS); + } +} + +size_t ShenandoahGeneration::increment_affiliated_region_count() { + shenandoah_assert_heaplocked_or_safepoint(); + // During full gc, multiple GC worker threads may change region affiliations without a lock. No lock is enforced + // on read and write of _affiliated_region_count. At the end of full gc, a single thread overwrites the count with + // a coherent value. + _affiliated_region_count++; + return _affiliated_region_count; +} + +size_t ShenandoahGeneration::decrement_affiliated_region_count() { + shenandoah_assert_heaplocked_or_safepoint(); + // During full gc, multiple GC worker threads may change region affiliations without a lock. No lock is enforced + // on read and write of _affiliated_region_count. At the end of full gc, a single thread overwrites the count with + // a coherent value. + _affiliated_region_count--; + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_used + _humongous_waste <= _affiliated_region_count * ShenandoahHeapRegion::region_size_bytes()), + "used + humongous cannot exceed regions"); + return _affiliated_region_count; +} + +size_t ShenandoahGeneration::increase_affiliated_region_count(size_t delta) { + shenandoah_assert_heaplocked_or_safepoint(); + _affiliated_region_count += delta; + return _affiliated_region_count; +} + +size_t ShenandoahGeneration::decrease_affiliated_region_count(size_t delta) { + shenandoah_assert_heaplocked_or_safepoint(); + assert(_affiliated_region_count >= delta, "Affiliated region count cannot be negative"); + + _affiliated_region_count -= delta; + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_used + _humongous_waste <= _affiliated_region_count * ShenandoahHeapRegion::region_size_bytes()), + "used + humongous cannot exceed regions"); + return _affiliated_region_count; +} + +void ShenandoahGeneration::establish_usage(size_t num_regions, size_t num_bytes, size_t humongous_waste) { + assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "must be at a safepoint"); + _affiliated_region_count = num_regions; + _used = num_bytes; + _humongous_waste = humongous_waste; +} + +void ShenandoahGeneration::increase_used(size_t bytes) { + Atomic::add(&_used, bytes); +} + +void ShenandoahGeneration::increase_humongous_waste(size_t bytes) { + if (bytes > 0) { + Atomic::add(&_humongous_waste, bytes); + } +} + +void ShenandoahGeneration::decrease_humongous_waste(size_t bytes) { + if (bytes > 0) { + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || (_humongous_waste >= bytes), + "Waste (" SIZE_FORMAT ") cannot be negative (after subtracting " SIZE_FORMAT ")", _humongous_waste, bytes); + Atomic::sub(&_humongous_waste, bytes); + } +} + +void ShenandoahGeneration::decrease_used(size_t bytes) { + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_used >= bytes), "cannot reduce bytes used by generation below zero"); + Atomic::sub(&_used, bytes); +} + +size_t ShenandoahGeneration::used_regions() const { + return _affiliated_region_count; +} + +size_t ShenandoahGeneration::free_unaffiliated_regions() const { + size_t result = max_capacity() / ShenandoahHeapRegion::region_size_bytes(); + if (_affiliated_region_count > result) { + result = 0; + } else { + result -= _affiliated_region_count; + } + return result; +} + +size_t ShenandoahGeneration::used_regions_size() const { + return _affiliated_region_count * ShenandoahHeapRegion::region_size_bytes(); +} + +size_t ShenandoahGeneration::available() const { + return available(max_capacity()); +} + +// For ShenandoahYoungGeneration, Include the young available that may have been reserved for the Collector. +size_t ShenandoahGeneration::available_with_reserve() const { + return available(max_capacity()); +} + +size_t ShenandoahGeneration::soft_available() const { + return available(soft_max_capacity()); +} + +size_t ShenandoahGeneration::available(size_t capacity) const { + size_t in_use = used() + get_humongous_waste(); + return in_use > capacity ? 0 : capacity - in_use; +} + +size_t ShenandoahGeneration::increase_capacity(size_t increment) { + shenandoah_assert_heaplocked_or_safepoint(); + + // We do not enforce that new capacity >= heap->max_size_for(this). The maximum generation size is treated as a rule of thumb + // which may be violated during certain transitions, such as when we are forcing transfers for the purpose of promoting regions + // in place. + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_max_capacity + increment <= ShenandoahHeap::heap()->max_capacity()), "Generation cannot be larger than heap size"); + assert(increment % ShenandoahHeapRegion::region_size_bytes() == 0, "Generation capacity must be multiple of region size"); + _max_capacity += increment; + + // This detects arithmetic wraparound on _used + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_affiliated_region_count * ShenandoahHeapRegion::region_size_bytes() >= _used), + "Affiliated regions must hold more than what is currently used"); + return _max_capacity; +} + +size_t ShenandoahGeneration::set_capacity(size_t byte_size) { + shenandoah_assert_heaplocked_or_safepoint(); + _max_capacity = byte_size; + return _max_capacity; +} + +size_t ShenandoahGeneration::decrease_capacity(size_t decrement) { + shenandoah_assert_heaplocked_or_safepoint(); + + // We do not enforce that new capacity >= heap->min_size_for(this). The minimum generation size is treated as a rule of thumb + // which may be violated during certain transitions, such as when we are forcing transfers for the purpose of promoting regions + // in place. + assert(decrement % ShenandoahHeapRegion::region_size_bytes() == 0, "Generation capacity must be multiple of region size"); + assert(_max_capacity >= decrement, "Generation capacity cannot be negative"); + + _max_capacity -= decrement; + + // This detects arithmetic wraparound on _used + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_affiliated_region_count * ShenandoahHeapRegion::region_size_bytes() >= _used), + "Affiliated regions must hold more than what is currently used"); + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_used <= _max_capacity), "Cannot use more than capacity"); + assert(ShenandoahHeap::heap()->is_full_gc_in_progress() || + (_affiliated_region_count * ShenandoahHeapRegion::region_size_bytes() <= _max_capacity), + "Cannot use more than capacity"); + return _max_capacity; +} + +void ShenandoahGeneration::record_success_concurrent(bool abbreviated) { + heuristics()->record_success_concurrent(); + ShenandoahHeap::heap()->shenandoah_policy()->record_success_concurrent(is_young(), abbreviated); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp new file mode 100644 index 00000000000..f43c295dff5 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -0,0 +1,244 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP + +#include "memory/allocation.hpp" +#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" +#include "gc/shenandoah/shenandoahAffiliation.hpp" +#include "gc/shenandoah/shenandoahGenerationType.hpp" +#include "gc/shenandoah/shenandoahLock.hpp" +#include "gc/shenandoah/shenandoahMarkingContext.hpp" + +class ShenandoahCollectionSet; +class ShenandoahHeap; +class ShenandoahHeapRegion; +class ShenandoahHeapRegionClosure; +class ShenandoahHeuristics; +class ShenandoahMode; +class ShenandoahReferenceProcessor; + + +class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo { + friend class VMStructs; +private: + ShenandoahGenerationType const _type; + + // Marking task queues and completeness + ShenandoahObjToScanQueueSet* _task_queues; + ShenandoahSharedFlag _is_marking_complete; + + ShenandoahReferenceProcessor* const _ref_processor; + + size_t _affiliated_region_count; + + // How much free memory is left in the last region of humongous objects. + // This is _not_ included in used, but it _is_ deducted from available, + // which gives the heuristics a more accurate view of how much memory remains + // for allocation. This figure is also included the heap status logging. + // The units are bytes. The value is only changed on a safepoint or under the + // heap lock. + size_t _humongous_waste; + + // Bytes reserved within this generation to hold evacuated objects from the collection set + size_t _evacuation_reserve; + +protected: + // Usage + + volatile size_t _used; + volatile size_t _bytes_allocated_since_gc_start; + size_t _max_capacity; + size_t _soft_max_capacity; + + ShenandoahHeuristics* _heuristics; + +private: + // Compute evacuation budgets prior to choosing collection set. + void compute_evacuation_budgets(ShenandoahHeap* heap); + + // Adjust evacuation budgets after choosing collection set. + void adjust_evacuation_budgets(ShenandoahHeap* heap, + ShenandoahCollectionSet* collection_set); + + // Preselect for possible inclusion into the collection set exactly the most + // garbage-dense regions, including those that satisfy criteria 1 & 2 below, + // and whose live bytes will fit within old_available budget: + // Criterion 1. region age >= tenuring threshold + // Criterion 2. region garbage percentage > ShenandoahOldGarbageThreshold + // + // Identifies regions eligible for promotion in place, + // being those of at least tenuring_threshold age that have lower garbage + // density. + // + // Updates promotion_potential and pad_for_promote_in_place fields + // of the heap. Returns bytes of live object memory in the preselected + // regions, which are marked in the preselected_regions() indicator + // array of the heap's collection set, which should be initialized + // to false. + size_t select_aged_regions(size_t old_available); + + size_t available(size_t capacity) const; + + public: + ShenandoahGeneration(ShenandoahGenerationType type, + uint max_workers, + size_t max_capacity, + size_t soft_max_capacity); + ~ShenandoahGeneration(); + + bool is_young() const { return _type == YOUNG; } + bool is_old() const { return _type == OLD; } + bool is_global() const { return _type == GLOBAL || _type == NON_GEN; } + + // see description in field declaration + void set_evacuation_reserve(size_t new_val); + size_t get_evacuation_reserve() const; + void augment_evacuation_reserve(size_t increment); + + inline ShenandoahGenerationType type() const { return _type; } + + virtual ShenandoahHeuristics* heuristics() const { return _heuristics; } + + ShenandoahReferenceProcessor* ref_processor() { return _ref_processor; } + + virtual ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode); + + size_t soft_max_capacity() const override { return _soft_max_capacity; } + size_t max_capacity() const override { return _max_capacity; } + virtual size_t used_regions() const; + virtual size_t used_regions_size() const; + virtual size_t free_unaffiliated_regions() const; + size_t used() const override { return _used; } + size_t available() const override; + size_t available_with_reserve() const; + size_t used_including_humongous_waste() const { + return used() + get_humongous_waste(); + } + + // Returns the memory available based on the _soft_ max heap capacity (soft_max_heap - used). + // The soft max heap size may be adjusted lower than the max heap size to cause the trigger + // to believe it has less memory available than is _really_ available. Lowering the soft + // max heap size will cause the adaptive heuristic to run more frequent cycles. + size_t soft_available() const override; + + size_t bytes_allocated_since_gc_start() const override; + void reset_bytes_allocated_since_gc_start(); + void increase_allocated(size_t bytes); + + // These methods change the capacity of the generation by adding or subtracting the given number of bytes from the current + // capacity, returning the capacity of the generation following the change. + size_t increase_capacity(size_t increment); + size_t decrease_capacity(size_t decrement); + + // Set the capacity of the generation, returning the value set + size_t set_capacity(size_t byte_size); + + void log_status(const char* msg) const; + + // Used directly by FullGC + void reset_mark_bitmap(); + + // Used by concurrent and degenerated GC to reset remembered set. + void swap_remembered_set(); + + // Update the read cards with the state of the write table (write table is not cleared). + void merge_write_table(); + + // Called before init mark, expected to prepare regions for marking. + virtual void prepare_gc(); + + // Called during final mark, chooses collection set, rebuilds free set. + virtual void prepare_regions_and_collection_set(bool concurrent); + + // Cancel marking (used by Full collect and when cancelling cycle). + virtual void cancel_marking(); + + virtual bool contains(ShenandoahAffiliation affiliation) const = 0; + + // Return true if this region is affiliated with this generation. + virtual bool contains(ShenandoahHeapRegion* region) const = 0; + + // Return true if this object is affiliated with this generation. + virtual bool contains(oop obj) const = 0; + + // Apply closure to all regions affiliated with this generation. + virtual void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) = 0; + + // Apply closure to all regions affiliated with this generation (include free regions); + virtual void parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl); + + // Apply closure to all regions affiliated with this generation (single threaded). + virtual void heap_region_iterate(ShenandoahHeapRegionClosure* cl) = 0; + + // This is public to support cancellation of marking when a Full cycle is started. + virtual void set_concurrent_mark_in_progress(bool in_progress) = 0; + + // Check the bitmap only for regions belong to this generation. + bool is_bitmap_clear(); + + // We need to track the status of marking for different generations. + bool is_mark_complete(); + virtual void set_mark_complete(); + virtual void set_mark_incomplete(); + + ShenandoahMarkingContext* complete_marking_context(); + + // Task queues + ShenandoahObjToScanQueueSet* task_queues() const { return _task_queues; } + virtual void reserve_task_queues(uint workers); + virtual ShenandoahObjToScanQueueSet* old_gen_task_queues() const; + + // Scan remembered set at start of concurrent young-gen marking. + void scan_remembered_set(bool is_concurrent); + + // Return the updated value of affiliated_region_count + size_t increment_affiliated_region_count(); + + // Return the updated value of affiliated_region_count + size_t decrement_affiliated_region_count(); + + // Return the updated value of affiliated_region_count + size_t increase_affiliated_region_count(size_t delta); + + // Return the updated value of affiliated_region_count + size_t decrease_affiliated_region_count(size_t delta); + + void establish_usage(size_t num_regions, size_t num_bytes, size_t humongous_waste); + + void increase_used(size_t bytes); + void decrease_used(size_t bytes); + + void increase_humongous_waste(size_t bytes); + void decrease_humongous_waste(size_t bytes); + size_t get_humongous_waste() const { return _humongous_waste; } + + virtual bool is_concurrent_mark_in_progress() = 0; + void confirm_heuristics_mode(); + + virtual void record_success_concurrent(bool abbreviated); +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp new file mode 100644 index 00000000000..dfbc6b673ff --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp @@ -0,0 +1,209 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationSizer.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shared/gc_globals.hpp" +#include "logging/log.hpp" +#include "runtime/globals_extension.hpp" + + +ShenandoahGenerationSizer::ShenandoahGenerationSizer() + : _sizer_kind(SizerDefaults), + _min_desired_young_regions(0), + _max_desired_young_regions(0) { + + if (FLAG_IS_CMDLINE(NewRatio)) { + if (FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) { + log_warning(gc, ergo)("-XX:NewSize and -XX:MaxNewSize override -XX:NewRatio"); + } else { + _sizer_kind = SizerNewRatio; + return; + } + } + + if (NewSize > MaxNewSize) { + if (FLAG_IS_CMDLINE(MaxNewSize)) { + log_warning(gc, ergo)("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). " + "A new max generation size of " SIZE_FORMAT "k will be used.", + NewSize/K, MaxNewSize/K, NewSize/K); + } + FLAG_SET_ERGO(MaxNewSize, NewSize); + } + + if (FLAG_IS_CMDLINE(NewSize)) { + _min_desired_young_regions = MAX2(uint(NewSize / ShenandoahHeapRegion::region_size_bytes()), 1U); + if (FLAG_IS_CMDLINE(MaxNewSize)) { + _max_desired_young_regions = MAX2(uint(MaxNewSize / ShenandoahHeapRegion::region_size_bytes()), 1U); + _sizer_kind = SizerMaxAndNewSize; + } else { + _sizer_kind = SizerNewSizeOnly; + } + } else if (FLAG_IS_CMDLINE(MaxNewSize)) { + _max_desired_young_regions = MAX2(uint(MaxNewSize / ShenandoahHeapRegion::region_size_bytes()), 1U); + _sizer_kind = SizerMaxNewSizeOnly; + } +} + +size_t ShenandoahGenerationSizer::calculate_min_young_regions(size_t heap_region_count) { + size_t min_young_regions = (heap_region_count * ShenandoahMinYoungPercentage) / 100; + return MAX2(min_young_regions, (size_t) 1U); +} + +size_t ShenandoahGenerationSizer::calculate_max_young_regions(size_t heap_region_count) { + size_t max_young_regions = (heap_region_count * ShenandoahMaxYoungPercentage) / 100; + return MAX2(max_young_regions, (size_t) 1U); +} + +void ShenandoahGenerationSizer::recalculate_min_max_young_length(size_t heap_region_count) { + assert(heap_region_count > 0, "Heap must be initialized"); + + switch (_sizer_kind) { + case SizerDefaults: + _min_desired_young_regions = calculate_min_young_regions(heap_region_count); + _max_desired_young_regions = calculate_max_young_regions(heap_region_count); + break; + case SizerNewSizeOnly: + _max_desired_young_regions = calculate_max_young_regions(heap_region_count); + _max_desired_young_regions = MAX2(_min_desired_young_regions, _max_desired_young_regions); + break; + case SizerMaxNewSizeOnly: + _min_desired_young_regions = calculate_min_young_regions(heap_region_count); + _min_desired_young_regions = MIN2(_min_desired_young_regions, _max_desired_young_regions); + break; + case SizerMaxAndNewSize: + // Do nothing. Values set on the command line, don't update them at runtime. + break; + case SizerNewRatio: + _min_desired_young_regions = MAX2(uint(heap_region_count / (NewRatio + 1)), 1U); + _max_desired_young_regions = _min_desired_young_regions; + break; + default: + ShouldNotReachHere(); + } + + assert(_min_desired_young_regions <= _max_desired_young_regions, "Invalid min/max young gen size values"); +} + +void ShenandoahGenerationSizer::heap_size_changed(size_t heap_size) { + recalculate_min_max_young_length(heap_size / ShenandoahHeapRegion::region_size_bytes()); +} + +bool ShenandoahGenerationSizer::transfer_regions(ShenandoahGeneration* src, ShenandoahGeneration* dst, size_t regions) const { + const size_t bytes_to_transfer = regions * ShenandoahHeapRegion::region_size_bytes(); + + if (src->free_unaffiliated_regions() < regions) { + // Source does not have enough free regions for this transfer. The caller should have + // already capped the transfer based on available unaffiliated regions. + return false; + } + + if (dst->max_capacity() + bytes_to_transfer > max_size_for(dst)) { + // This transfer would cause the destination generation to grow above its configured maximum size. + return false; + } + + if (src->max_capacity() - bytes_to_transfer < min_size_for(src)) { + // This transfer would cause the source generation to shrink below its configured minimum size. + return false; + } + + src->decrease_capacity(bytes_to_transfer); + dst->increase_capacity(bytes_to_transfer); + const size_t new_size = dst->max_capacity(); + log_info(gc, ergo)("Transfer " SIZE_FORMAT " region(s) from %s to %s, yielding increased size: " PROPERFMT, + regions, src->name(), dst->name(), PROPERFMTARGS(new_size)); + return true; +} + + +size_t ShenandoahGenerationSizer::max_size_for(ShenandoahGeneration* generation) const { + switch (generation->type()) { + case YOUNG: + return max_young_size(); + case OLD: + // On the command line, max size of OLD is specified indirectly, by setting a minimum size of young. + // OLD is what remains within the heap after YOUNG has been sized. + return ShenandoahHeap::heap()->max_capacity() - min_young_size(); + default: + ShouldNotReachHere(); + return 0; + } +} + +size_t ShenandoahGenerationSizer::min_size_for(ShenandoahGeneration* generation) const { + switch (generation->type()) { + case YOUNG: + return min_young_size(); + case OLD: + // On the command line, min size of OLD is specified indirectly, by setting a maximum size of young. + // OLD is what remains within the heap after YOUNG has been sized. + return ShenandoahHeap::heap()->max_capacity() - max_young_size(); + default: + ShouldNotReachHere(); + return 0; + } +} + + +// Returns true iff transfer is successful +bool ShenandoahGenerationSizer::transfer_to_old(size_t regions) const { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + return transfer_regions(heap->young_generation(), heap->old_generation(), regions); +} + +// This is used when promoting humongous or highly utilized regular regions in place. It is not required in this situation +// that the transferred regions be unaffiliated. +void ShenandoahGenerationSizer::force_transfer_to_old(size_t regions) const { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + ShenandoahGeneration* old_gen = heap->old_generation(); + ShenandoahGeneration* young_gen = heap->young_generation(); + const size_t bytes_to_transfer = regions * ShenandoahHeapRegion::region_size_bytes(); + + young_gen->decrease_capacity(bytes_to_transfer); + old_gen->increase_capacity(bytes_to_transfer); + const size_t new_size = old_gen->max_capacity(); + log_info(gc, ergo)("Forcing transfer of " SIZE_FORMAT " region(s) from %s to %s, yielding increased size: " PROPERFMT, + regions, young_gen->name(), old_gen->name(), PROPERFMTARGS(new_size)); +} + + +bool ShenandoahGenerationSizer::transfer_to_young(size_t regions) const { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + return transfer_regions(heap->old_generation(), heap->young_generation(), regions); +} + +size_t ShenandoahGenerationSizer::min_young_size() const { + return min_young_regions() * ShenandoahHeapRegion::region_size_bytes(); +} + +size_t ShenandoahGenerationSizer::max_young_size() const { + return max_young_regions() * ShenandoahHeapRegion::region_size_bytes(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.hpp new file mode 100644 index 00000000000..5752422bb77 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.hpp @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONSIZER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONSIZER_HPP + +#include "utilities/globalDefinitions.hpp" + +class ShenandoahGeneration; +class ShenandoahGenerationalHeap; + +class ShenandoahGenerationSizer { +private: + enum SizerKind { + SizerDefaults, + SizerNewSizeOnly, + SizerMaxNewSizeOnly, + SizerMaxAndNewSize, + SizerNewRatio + }; + SizerKind _sizer_kind; + + size_t _min_desired_young_regions; + size_t _max_desired_young_regions; + + static size_t calculate_min_young_regions(size_t heap_region_count); + static size_t calculate_max_young_regions(size_t heap_region_count); + + // Update the given values for minimum and maximum young gen length in regions + // given the number of heap regions depending on the kind of sizing algorithm. + void recalculate_min_max_young_length(size_t heap_region_count); + + // This will attempt to transfer regions from the `src` generation to `dst` generation. + // If the transfer would violate the configured minimum size for the source or the configured + // maximum size of the destination, it will not perform the transfer and will return false. + // Returns true if the transfer is performed. + bool transfer_regions(ShenandoahGeneration* src, ShenandoahGeneration* dst, size_t regions) const; + + // Return the configured maximum size in bytes for the given generation. + size_t max_size_for(ShenandoahGeneration* generation) const; + + // Return the configured minimum size in bytes for the given generation. + size_t min_size_for(ShenandoahGeneration* generation) const; + +public: + ShenandoahGenerationSizer(); + + // Calculate the maximum length of the young gen given the number of regions + // depending on the sizing algorithm. + void heap_size_changed(size_t heap_size); + + // Minimum size of young generation in bytes as multiple of region size. + size_t min_young_size() const; + size_t min_young_regions() const { + return _min_desired_young_regions; + } + + // Maximum size of young generation in bytes as multiple of region size. + size_t max_young_size() const; + size_t max_young_regions() const { + return _max_desired_young_regions; + } + + // True if transfer succeeds, else false. See transfer_regions. + bool transfer_to_young(size_t regions) const; + bool transfer_to_old(size_t regions) const; + + // force transfer is used when we promote humongous objects. May violate min/max limits on generation sizes + void force_transfer_to_old(size_t regions) const; +}; + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONSIZER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationType.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationType.hpp index 1132fd5eb4d..cfc2b36cf1d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationType.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationType.hpp @@ -26,13 +26,22 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONTYPE_HPP enum ShenandoahGenerationType { - NON_GEN // non-generational + NON_GEN, // non-generational + GLOBAL, // generational: Global + YOUNG, // generational: Young + OLD // generational: Old }; inline const char* shenandoah_generation_name(ShenandoahGenerationType mode) { switch (mode) { case NON_GEN: return "Non-Generational"; + case GLOBAL: + return "Global"; + case OLD: + return "Old"; + case YOUNG: + return "Young"; default: ShouldNotReachHere(); return "Unknown"; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp new file mode 100644 index 00000000000..ef0fbf671a0 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -0,0 +1,841 @@ +/* + * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright Amazon.com Inc. 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" +#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahConcurrentGC.hpp" +#include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" +#include "gc/shenandoah/shenandoahDegeneratedGC.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahFullGC.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahOldGC.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahPacer.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "logging/log.hpp" +#include "memory/metaspaceUtils.hpp" +#include "memory/metaspaceStats.hpp" +#include "runtime/atomic.hpp" + +ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : + ShenandoahController(), + _control_lock(Mutex::nosafepoint - 2, "ShenandoahControlGC_lock", true), + _regulator_lock(Mutex::nosafepoint - 2, "ShenandoahRegulatorGC_lock", true), + _requested_gc_cause(GCCause::_no_gc), + _requested_generation(GLOBAL), + _degen_point(ShenandoahGC::_degenerated_outside_cycle), + _degen_generation(nullptr), + _mode(none) { + shenandoah_assert_generational(); + set_name("Shenandoah Control Thread"); + create_and_start(); +} + +void ShenandoahGenerationalControlThread::run_service() { + ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); + + const GCMode default_mode = concurrent_normal; + ShenandoahGenerationType generation = GLOBAL; + + double last_shrink_time = os::elapsedTime(); + uint age_period = 0; + + // Shrink period avoids constantly polling regions for shrinking. + // Having a period 10x lower than the delay would mean we hit the + // shrinking with lag of less than 1/10-th of true delay. + // ShenandoahUncommitDelay is in msecs, but shrink_period is in seconds. + const double shrink_period = (double)ShenandoahUncommitDelay / 1000 / 10; + + ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy(); + + // Heuristics are notified of allocation failures here and other outcomes + // of the cycle. They're also used here to control whether the Nth consecutive + // degenerated cycle should be 'promoted' to a full cycle. The decision to + // trigger a cycle or not is evaluated on the regulator thread. + ShenandoahHeuristics* global_heuristics = heap->global_generation()->heuristics(); + while (!in_graceful_shutdown() && !should_terminate()) { + // Figure out if we have pending requests. + const bool alloc_failure_pending = _alloc_failure_gc.is_set(); + const bool humongous_alloc_failure_pending = _humongous_alloc_failure_gc.is_set(); + + GCCause::Cause cause = Atomic::xchg(&_requested_gc_cause, GCCause::_no_gc); + + const bool is_gc_requested = ShenandoahCollectorPolicy::is_requested_gc(cause); + + // This control loop iteration has seen this much allocation. + const size_t allocs_seen = reset_allocs_seen(); + + // Check if we have seen a new target for soft max heap size. + const bool soft_max_changed = heap->check_soft_max_changed(); + + // Choose which GC mode to run in. The block below should select a single mode. + set_gc_mode(none); + ShenandoahGC::ShenandoahDegenPoint degen_point = ShenandoahGC::_degenerated_unset; + + if (alloc_failure_pending) { + // Allocation failure takes precedence: we have to deal with it first thing + cause = GCCause::_allocation_failure; + + // Consume the degen point, and seed it with default value + degen_point = _degen_point; + _degen_point = ShenandoahGC::_degenerated_outside_cycle; + + if (degen_point == ShenandoahGC::_degenerated_outside_cycle) { + _degen_generation = heap->young_generation(); + } else { + assert(_degen_generation != nullptr, "Need to know which generation to resume"); + } + + ShenandoahHeuristics* heuristics = _degen_generation->heuristics(); + generation = _degen_generation->type(); + bool old_gen_evacuation_failed = heap->old_generation()->clear_failed_evacuation(); + + heuristics->log_trigger("Handle Allocation Failure"); + + // Do not bother with degenerated cycle if old generation evacuation failed or if humongous allocation failed + if (ShenandoahDegeneratedGC && heuristics->should_degenerate_cycle() && + !old_gen_evacuation_failed && !humongous_alloc_failure_pending) { + heuristics->record_allocation_failure_gc(); + policy->record_alloc_failure_to_degenerated(degen_point); + set_gc_mode(stw_degenerated); + } else { + heuristics->record_allocation_failure_gc(); + policy->record_alloc_failure_to_full(); + generation = GLOBAL; + set_gc_mode(stw_full); + } + } else if (is_gc_requested) { + generation = GLOBAL; + global_heuristics->log_trigger("GC request (%s)", GCCause::to_string(cause)); + global_heuristics->record_requested_gc(); + + if (ShenandoahCollectorPolicy::should_run_full_gc(cause)) { + set_gc_mode(stw_full); + } else { + set_gc_mode(default_mode); + // Unload and clean up everything + heap->set_unload_classes(global_heuristics->can_unload_classes()); + } + } else { + // We should only be here if the regulator requested a cycle or if + // there is an old generation mark in progress. + if (cause == GCCause::_shenandoah_concurrent_gc) { + if (_requested_generation == OLD && heap->old_generation()->is_doing_mixed_evacuations()) { + // If a request to start an old cycle arrived while an old cycle was running, but _before_ + // it chose any regions for evacuation we don't want to start a new old cycle. Rather, we want + // the heuristic to run a young collection so that we can evacuate some old regions. + assert(!heap->is_concurrent_old_mark_in_progress(), "Should not be running mixed collections and concurrent marking"); + generation = YOUNG; + } else { + generation = _requested_generation; + } + + // preemption was requested or this is a regular cycle + set_gc_mode(default_mode); + + // Don't start a new old marking if there is one already in progress + if (generation == OLD && heap->is_concurrent_old_mark_in_progress()) { + set_gc_mode(servicing_old); + } + + if (generation == GLOBAL) { + heap->set_unload_classes(global_heuristics->should_unload_classes()); + } else { + heap->set_unload_classes(false); + } + } else if (heap->is_concurrent_old_mark_in_progress() || heap->is_prepare_for_old_mark_in_progress()) { + // Nobody asked us to do anything, but we have an old-generation mark or old-generation preparation for + // mixed evacuation in progress, so resume working on that. + log_info(gc)("Resume old GC: marking is%s in progress, preparing is%s in progress", + heap->is_concurrent_old_mark_in_progress() ? "" : " NOT", + heap->is_prepare_for_old_mark_in_progress() ? "" : " NOT"); + + cause = GCCause::_shenandoah_concurrent_gc; + generation = OLD; + set_gc_mode(servicing_old); + heap->set_unload_classes(false); + } + } + + const bool gc_requested = (gc_mode() != none); + assert (!gc_requested || cause != GCCause::_no_gc, "GC cause should be set"); + + if (gc_requested) { + // Blow away all soft references on this cycle, if handling allocation failure, + // either implicit or explicit GC request, or we are requested to do so unconditionally. + if (generation == GLOBAL && (alloc_failure_pending || is_gc_requested || ShenandoahAlwaysClearSoftRefs)) { + heap->soft_ref_policy()->set_should_clear_all_soft_refs(true); + } + + // GC is starting, bump the internal ID + update_gc_id(); + + heap->reset_bytes_allocated_since_gc_start(); + + MetaspaceCombinedStats meta_sizes = MetaspaceUtils::get_combined_statistics(); + + // If GC was requested, we are sampling the counters even without actual triggers + // from allocation machinery. This captures GC phases more accurately. + heap->set_forced_counters_update(true); + + // If GC was requested, we better dump freeset data for performance debugging + heap->free_set()->log_status_under_lock(); + + // In case this is a degenerated cycle, remember whether original cycle was aging. + const bool was_aging_cycle = heap->is_aging_cycle(); + heap->set_aging_cycle(false); + + switch (gc_mode()) { + case concurrent_normal: { + // At this point: + // if (generation == YOUNG), this is a normal YOUNG cycle + // if (generation == OLD), this is a bootstrap OLD cycle + // if (generation == GLOBAL), this is a GLOBAL cycle triggered by System.gc() + // In all three cases, we want to age old objects if this is an aging cycle + if (age_period-- == 0) { + heap->set_aging_cycle(true); + age_period = ShenandoahAgingCyclePeriod - 1; + } + service_concurrent_normal_cycle(heap, generation, cause); + break; + } + case stw_degenerated: { + heap->set_aging_cycle(was_aging_cycle); + service_stw_degenerated_cycle(cause, degen_point); + break; + } + case stw_full: { + if (age_period-- == 0) { + heap->set_aging_cycle(true); + age_period = ShenandoahAgingCyclePeriod - 1; + } + service_stw_full_cycle(cause); + break; + } + case servicing_old: { + assert(generation == OLD, "Expected old generation here"); + GCIdMark gc_id_mark; + service_concurrent_old_cycle(heap, cause); + break; + } + default: + ShouldNotReachHere(); + } + + // If this was the requested GC cycle, notify waiters about it + if (is_gc_requested) { + notify_gc_waiters(); + } + + // If this was the allocation failure GC cycle, notify waiters about it + if (alloc_failure_pending) { + notify_alloc_failure_waiters(); + } + + // Report current free set state at the end of cycle, whether + // it is a normal completion, or the abort. + heap->free_set()->log_status_under_lock(); + + // Notify Universe about new heap usage. This has implications for + // global soft refs policy, and we better report it every time heap + // usage goes down. + heap->update_capacity_and_used_at_gc(); + + // Signal that we have completed a visit to all live objects. + heap->record_whole_heap_examined_timestamp(); + + // Disable forced counters update, and update counters one more time + // to capture the state at the end of GC session. + heap->handle_force_counters_update(); + heap->set_forced_counters_update(false); + + // Retract forceful part of soft refs policy + heap->soft_ref_policy()->set_should_clear_all_soft_refs(false); + + // Clear metaspace oom flag, if current cycle unloaded classes + if (heap->unload_classes()) { + global_heuristics->clear_metaspace_oom(); + } + + process_phase_timings(heap); + + // Print Metaspace change following GC (if logging is enabled). + MetaspaceUtils::print_metaspace_change(meta_sizes); + + // GC is over, we are at idle now + if (ShenandoahPacing) { + heap->pacer()->setup_for_idle(); + } + } else { + // Report to pacer that we have seen this many words allocated + if (ShenandoahPacing && (allocs_seen > 0)) { + heap->pacer()->report_alloc(allocs_seen); + } + } + + const double current = os::elapsedTime(); + + if (ShenandoahUncommit && (is_gc_requested || soft_max_changed || (current - last_shrink_time > shrink_period))) { + // Explicit GC tries to uncommit everything down to min capacity. + // Soft max change tries to uncommit everything down to target capacity. + // Periodic uncommit tries to uncommit suitable regions down to min capacity. + + double shrink_before = (is_gc_requested || soft_max_changed) ? + current : + current - (ShenandoahUncommitDelay / 1000.0); + + size_t shrink_until = soft_max_changed ? + heap->soft_max_capacity() : + heap->min_capacity(); + + heap->maybe_uncommit(shrink_before, shrink_until); + heap->phase_timings()->flush_cycle_to_global(); + last_shrink_time = current; + } + + // Wait for ShenandoahControlIntervalMax unless there was an allocation failure or another request was made mid-cycle. + if (!is_alloc_failure_gc() && _requested_gc_cause == GCCause::_no_gc) { + // The timed wait is necessary because this thread has a responsibility to send + // 'alloc_words' to the pacer when it does not perform a GC. + MonitorLocker lock(&_control_lock, Mutex::_no_safepoint_check_flag); + lock.wait(ShenandoahControlIntervalMax); + } + } + + // Wait for the actual stop(), can't leave run_service() earlier. + while (!should_terminate()) { + os::naked_short_sleep(ShenandoahControlIntervalMin); + } +} + +void ShenandoahGenerationalControlThread::process_phase_timings(const ShenandoahGenerationalHeap* heap) { + // Commit worker statistics to cycle data + heap->phase_timings()->flush_par_workers_to_cycle(); + if (ShenandoahPacing) { + heap->pacer()->flush_stats_to_cycle(); + } + + ShenandoahEvacuationTracker* evac_tracker = heap->evac_tracker(); + ShenandoahCycleStats evac_stats = evac_tracker->flush_cycle_to_global(); + + // Print GC stats for current cycle + { + LogTarget(Info, gc, stats) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + heap->phase_timings()->print_cycle_on(&ls); + evac_tracker->print_evacuations_on(&ls, &evac_stats.workers, + &evac_stats.mutators); + if (ShenandoahPacing) { + heap->pacer()->print_cycle_on(&ls); + } + } + } + + // Commit statistics to globals + heap->phase_timings()->flush_cycle_to_global(); +} + +// Young and old concurrent cycles are initiated by the regulator. Implicit +// and explicit GC requests are handled by the controller thread and always +// run a global cycle (which is concurrent by default, but may be overridden +// by command line options). Old cycles always degenerate to a global cycle. +// Young cycles are degenerated to complete the young cycle. Young +// and old degen may upgrade to Full GC. Full GC may also be +// triggered directly by a System.gc() invocation. +// +// +// +-----+ Idle +-----+-----------+---------------------+ +// | + | | | +// | | | | | +// | | v | | +// | | Bootstrap Old +-- | ------------+ | +// | | + | | | +// | | | | | | +// | v v v v | +// | Resume Old <----------+ Young +--> Young Degen | +// | + + ^ + + | +// v | | | | | | +// Global <-+ | +----------------------------+ | | +// + | | | +// | v v | +// +---> Global Degen +--------------------> Full <----+ +// +void ShenandoahGenerationalControlThread::service_concurrent_normal_cycle(ShenandoahGenerationalHeap* heap, + const ShenandoahGenerationType generation, + GCCause::Cause cause) { + GCIdMark gc_id_mark; + switch (generation) { + case YOUNG: { + // Run a young cycle. This might or might not, have interrupted an ongoing + // concurrent mark in the old generation. We need to think about promotions + // in this case. Promoted objects should be above the TAMS in the old regions + // they end up in, but we have to be sure we don't promote into any regions + // that are in the cset. + log_info(gc, ergo)("Start GC cycle (Young)"); + service_concurrent_cycle(heap->young_generation(), cause, false); + break; + } + case OLD: { + log_info(gc, ergo)("Start GC cycle (Old)"); + service_concurrent_old_cycle(heap, cause); + break; + } + case GLOBAL: { + log_info(gc, ergo)("Start GC cycle (Global)"); + service_concurrent_cycle(heap->global_generation(), cause, false); + break; + } + default: + ShouldNotReachHere(); + } +} + +void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(ShenandoahGenerationalHeap* heap, GCCause::Cause &cause) { + ShenandoahOldGeneration* old_generation = heap->old_generation(); + ShenandoahYoungGeneration* young_generation = heap->young_generation(); + ShenandoahOldGeneration::State original_state = old_generation->state(); + + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + + switch (original_state) { + case ShenandoahOldGeneration::FILLING: { + ShenandoahGCSession session(cause, old_generation); + _allow_old_preemption.set(); + old_generation->entry_coalesce_and_fill(); + _allow_old_preemption.unset(); + + // Before bootstrapping begins, we must acknowledge any cancellation request. + // If the gc has not been cancelled, this does nothing. If it has been cancelled, + // this will clear the cancellation request and exit before starting the bootstrap + // phase. This will allow the young GC cycle to proceed normally. If we do not + // acknowledge the cancellation request, the subsequent young cycle will observe + // the request and essentially cancel itself. + if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) { + log_info(gc)("Preparation for old generation cycle was cancelled"); + return; + } + + // Coalescing threads completed and nothing was cancelled. it is safe to transition from this state. + old_generation->transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + return; + } + case ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP: + old_generation->transition_to(ShenandoahOldGeneration::BOOTSTRAPPING); + case ShenandoahOldGeneration::BOOTSTRAPPING: { + // Configure the young generation's concurrent mark to put objects in + // old regions into the concurrent mark queues associated with the old + // generation. The young cycle will run as normal except that rather than + // ignore old references it will mark and enqueue them in the old concurrent + // task queues but it will not traverse them. + set_gc_mode(bootstrapping_old); + young_generation->set_old_gen_task_queues(old_generation->task_queues()); + ShenandoahGCSession session(cause, young_generation); + service_concurrent_cycle(heap, young_generation, cause, true); + process_phase_timings(heap); + if (heap->cancelled_gc()) { + // Young generation bootstrap cycle has failed. Concurrent mark for old generation + // is going to resume after degenerated bootstrap cycle completes. + log_info(gc)("Bootstrap cycle for old generation was cancelled"); + return; + } + + // Reset the degenerated point. Normally this would happen at the top + // of the control loop, but here we have just completed a young cycle + // which has bootstrapped the old concurrent marking. + _degen_point = ShenandoahGC::_degenerated_outside_cycle; + + // From here we will 'resume' the old concurrent mark. This will skip reset + // and init mark for the concurrent mark. All of that work will have been + // done by the bootstrapping young cycle. + set_gc_mode(servicing_old); + old_generation->transition_to(ShenandoahOldGeneration::MARKING); + } + case ShenandoahOldGeneration::MARKING: { + ShenandoahGCSession session(cause, old_generation); + bool marking_complete = resume_concurrent_old_cycle(old_generation, cause); + if (marking_complete) { + assert(old_generation->state() != ShenandoahOldGeneration::MARKING, "Should not still be marking"); + if (original_state == ShenandoahOldGeneration::MARKING) { + heap->mmu_tracker()->record_old_marking_increment(true); + heap->log_heap_status("At end of Concurrent Old Marking finishing increment"); + } + } else if (original_state == ShenandoahOldGeneration::MARKING) { + heap->mmu_tracker()->record_old_marking_increment(false); + heap->log_heap_status("At end of Concurrent Old Marking increment"); + } + break; + } + default: + fatal("Unexpected state for old GC: %s", ShenandoahOldGeneration::state_name(old_generation->state())); + } +} + +bool ShenandoahGenerationalControlThread::resume_concurrent_old_cycle(ShenandoahOldGeneration* generation, GCCause::Cause cause) { + assert(ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress(), "Old mark should be in progress"); + log_debug(gc)("Resuming old generation with " UINT32_FORMAT " marking tasks queued", generation->task_queues()->tasks()); + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + // We can only tolerate being cancelled during concurrent marking or during preparation for mixed + // evacuation. This flag here (passed by reference) is used to control precisely where the regulator + // is allowed to cancel a GC. + ShenandoahOldGC gc(generation, _allow_old_preemption); + if (gc.collect(cause)) { + heap->notify_gc_progress(); + generation->record_success_concurrent(false); + } + + if (heap->cancelled_gc()) { + // It's possible the gc cycle was cancelled after the last time + // the collection checked for cancellation. In which case, the + // old gc cycle is still completed, and we have to deal with this + // cancellation. We set the degeneration point to be outside + // the cycle because if this is an allocation failure, that is + // what must be done (there is no degenerated old cycle). If the + // cancellation was due to a heuristic wanting to start a young + // cycle, then we are not actually going to a degenerated cycle, + // so the degenerated point doesn't matter here. + check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle); + if (_requested_gc_cause == GCCause::_shenandoah_concurrent_gc) { + heap->shenandoah_policy()->record_interrupted_old(); + } + return false; + } + return true; +} + +void ShenandoahGenerationalControlThread::service_concurrent_cycle(ShenandoahGeneration* generation, GCCause::Cause cause, bool do_old_gc_bootstrap) { + // Normal cycle goes via all concurrent phases. If allocation failure (af) happens during + // any of the concurrent phases, it first degrades to Degenerated GC and completes GC there. + // If second allocation failure happens during Degenerated GC cycle (for example, when GC + // tries to evac something and no memory is available), cycle degrades to Full GC. + // + // There are also a shortcut through the normal cycle: immediate garbage shortcut, when + // heuristics says there are no regions to compact, and all the collection comes from immediately + // reclaimable regions. + // + // ................................................................................................ + // + // (immediate garbage shortcut) Concurrent GC + // /-------------------------------------------\ + // | | + // | | + // | | + // | v + // [START] ----> Conc Mark ----o----> Conc Evac --o--> Conc Update-Refs ---o----> [END] + // | | | ^ + // | (af) | (af) | (af) | + // ..................|....................|.................|..............|....................... + // | | | | + // | | | | Degenerated GC + // v v v | + // STW Mark ----------> STW Evac ----> STW Update-Refs ----->o + // | | | ^ + // | (af) | (af) | (af) | + // ..................|....................|.................|..............|....................... + // | | | | + // | v | | Full GC + // \------------------->o<----------------/ | + // | | + // v | + // Full GC --------------------------/ + // + if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) return; + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahGCSession session(cause, generation); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + + service_concurrent_cycle(heap, generation, cause, do_old_gc_bootstrap); +} + +void ShenandoahGenerationalControlThread::service_concurrent_cycle(ShenandoahHeap* heap, + ShenandoahGeneration* generation, + GCCause::Cause& cause, + bool do_old_gc_bootstrap) { + assert(!generation->is_old(), "Old GC takes a different control path"); + + ShenandoahConcurrentGC gc(generation, do_old_gc_bootstrap); + if (gc.collect(cause)) { + // Cycle is complete + heap->notify_gc_progress(); + generation->record_success_concurrent(gc.abbreviated()); + } else { + assert(heap->cancelled_gc(), "Must have been cancelled"); + check_cancellation_or_degen(gc.degen_point()); + + // Concurrent young-gen collection degenerates to young + // collection. Same for global collections. + _degen_generation = generation; + } + const char* msg; + ShenandoahMmuTracker* mmu_tracker = heap->mmu_tracker(); + if (generation->is_young()) { + if (heap->cancelled_gc()) { + msg = (do_old_gc_bootstrap) ? "At end of Interrupted Concurrent Bootstrap GC" : + "At end of Interrupted Concurrent Young GC"; + } else { + // We only record GC results if GC was successful + msg = (do_old_gc_bootstrap) ? "At end of Concurrent Bootstrap GC" : + "At end of Concurrent Young GC"; + if (heap->collection_set()->has_old_regions()) { + mmu_tracker->record_mixed(get_gc_id()); + } else if (do_old_gc_bootstrap) { + mmu_tracker->record_bootstrap(get_gc_id()); + } else { + mmu_tracker->record_young(get_gc_id()); + } + } + } else { + assert(generation->is_global(), "If not young, must be GLOBAL"); + assert(!do_old_gc_bootstrap, "Do not bootstrap with GLOBAL GC"); + if (heap->cancelled_gc()) { + msg = "At end of Interrupted Concurrent GLOBAL GC"; + } else { + // We only record GC results if GC was successful + msg = "At end of Concurrent Global GC"; + mmu_tracker->record_global(get_gc_id()); + } + } + heap->log_heap_status(msg); +} + +bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (!heap->cancelled_gc()) { + return false; + } + + if (in_graceful_shutdown()) { + return true; + } + + assert(_degen_point == ShenandoahGC::_degenerated_outside_cycle, + "Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point)); + + if (is_alloc_failure_gc()) { + _degen_point = point; + _preemption_requested.unset(); + return true; + } + + if (_preemption_requested.is_set()) { + assert(_requested_generation == YOUNG, "Only young GCs may preempt old."); + _preemption_requested.unset(); + + // Old generation marking is only cancellable during concurrent marking. + // Once final mark is complete, the code does not check again for cancellation. + // If old generation was cancelled for an allocation failure, we wouldn't + // make it to this case. The calling code is responsible for forcing a + // cancellation due to allocation failure into a degenerated cycle. + _degen_point = point; + heap->clear_cancelled_gc(false /* clear oom handler */); + return true; + } + + fatal("Cancel GC either for alloc failure GC, or gracefully exiting, or to pause old generation marking"); + return false; +} + +void ShenandoahGenerationalControlThread::stop_service() { + // Nothing to do here. +} + +void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause cause) { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + + GCIdMark gc_id_mark; + ShenandoahGCSession session(cause, heap->global_generation()); + + ShenandoahFullGC gc; + gc.collect(cause); +} + +void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(GCCause::Cause cause, + ShenandoahGC::ShenandoahDegenPoint point) { + assert(point != ShenandoahGC::_degenerated_unset, "Degenerated point should be set"); + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + + GCIdMark gc_id_mark; + ShenandoahGCSession session(cause, _degen_generation); + + ShenandoahDegenGC gc(point, _degen_generation); + gc.collect(cause); + + assert(heap->young_generation()->task_queues()->is_empty(), "Unexpected young generation marking tasks"); + if (_degen_generation->is_global()) { + assert(heap->old_generation()->task_queues()->is_empty(), "Unexpected old generation marking tasks"); + assert(heap->global_generation()->task_queues()->is_empty(), "Unexpected global generation marking tasks"); + } else { + assert(_degen_generation->is_young(), "Expected degenerated young cycle, if not global."); + ShenandoahOldGeneration* old = heap->old_generation(); + if (old->is_bootstrapping()) { + old->transition_to(ShenandoahOldGeneration::MARKING); + } + } +} + +void ShenandoahGenerationalControlThread::request_gc(GCCause::Cause cause) { + if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) { + handle_requested_gc(cause); + } +} + +bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenerationType generation) { + if (_preemption_requested.is_set() || _requested_gc_cause != GCCause::_no_gc || ShenandoahHeap::heap()->cancelled_gc()) { + // Ignore subsequent requests from the heuristics + log_debug(gc, thread)("Reject request for concurrent gc: preemption_requested: %s, gc_requested: %s, gc_cancelled: %s", + BOOL_TO_STR(_preemption_requested.is_set()), + GCCause::to_string(_requested_gc_cause), + BOOL_TO_STR(ShenandoahHeap::heap()->cancelled_gc())); + return false; + } + + if (gc_mode() == none) { + GCCause::Cause existing = Atomic::cmpxchg(&_requested_gc_cause, GCCause::_no_gc, GCCause::_shenandoah_concurrent_gc); + if (existing != GCCause::_no_gc) { + log_debug(gc, thread)("Reject request for concurrent gc because another gc is pending: %s", GCCause::to_string(existing)); + return false; + } + + _requested_generation = generation; + notify_control_thread(); + + MonitorLocker ml(&_regulator_lock, Mutex::_no_safepoint_check_flag); + while (gc_mode() == none) { + ml.wait(); + } + return true; + } + + if (preempt_old_marking(generation)) { + assert(gc_mode() == servicing_old, "Expected to be servicing old, but was: %s.", gc_mode_name(gc_mode())); + GCCause::Cause existing = Atomic::cmpxchg(&_requested_gc_cause, GCCause::_no_gc, GCCause::_shenandoah_concurrent_gc); + if (existing != GCCause::_no_gc) { + log_debug(gc, thread)("Reject request to interrupt old gc because another gc is pending: %s", GCCause::to_string(existing)); + return false; + } + + log_info(gc)("Preempting old generation mark to allow %s GC", shenandoah_generation_name(generation)); + _requested_generation = generation; + _preemption_requested.set(); + ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc); + notify_control_thread(); + + MonitorLocker ml(&_regulator_lock, Mutex::_no_safepoint_check_flag); + while (gc_mode() == servicing_old) { + ml.wait(); + } + return true; + } + + log_debug(gc, thread)("Reject request for concurrent gc: mode: %s, allow_old_preemption: %s", + gc_mode_name(gc_mode()), + BOOL_TO_STR(_allow_old_preemption.is_set())); + return false; +} + +void ShenandoahGenerationalControlThread::notify_control_thread() { + MonitorLocker locker(&_control_lock, Mutex::_no_safepoint_check_flag); + _control_lock.notify(); +} + +bool ShenandoahGenerationalControlThread::preempt_old_marking(ShenandoahGenerationType generation) { + return (generation == YOUNG) && _allow_old_preemption.try_unset(); +} + +void ShenandoahGenerationalControlThread::handle_requested_gc(GCCause::Cause cause) { + // For normal requested GCs (System.gc) we want to block the caller. However, + // for whitebox requested GC, we want to initiate the GC and return immediately. + // The whitebox caller thread will arrange for itself to wait until the GC notifies + // it that has reached the requested breakpoint (phase in the GC). + if (cause == GCCause::_wb_breakpoint) { + Atomic::xchg(&_requested_gc_cause, cause); + notify_control_thread(); + return; + } + + // Make sure we have at least one complete GC cycle before unblocking + // from the explicit GC request. + // + // This is especially important for weak references cleanup and/or native + // resources (e.g. DirectByteBuffers) machinery: when explicit GC request + // comes very late in the already running cycle, it would miss lots of new + // opportunities for cleanup that were made available before the caller + // requested the GC. + + MonitorLocker ml(&_gc_waiters_lock); + size_t current_gc_id = get_gc_id(); + size_t required_gc_id = current_gc_id + 1; + while (current_gc_id < required_gc_id) { + // This races with the regulator thread to start a concurrent gc and the + // control thread to clear it at the start of a cycle. Threads here are + // allowed to escalate a heuristic's request for concurrent gc. + GCCause::Cause existing = Atomic::xchg(&_requested_gc_cause, cause); + if (existing != GCCause::_no_gc) { + log_debug(gc, thread)("GC request supersedes existing request: %s", GCCause::to_string(existing)); + } + + notify_control_thread(); + ml.wait(); + current_gc_id = get_gc_id(); + } +} + +void ShenandoahGenerationalControlThread::notify_gc_waiters() { + MonitorLocker ml(&_gc_waiters_lock); + ml.notify_all(); +} + +const char* ShenandoahGenerationalControlThread::gc_mode_name(ShenandoahGenerationalControlThread::GCMode mode) { + switch (mode) { + case none: return "idle"; + case concurrent_normal: return "normal"; + case stw_degenerated: return "degenerated"; + case stw_full: return "full"; + case servicing_old: return "old"; + case bootstrapping_old: return "bootstrap"; + default: return "unknown"; + } +} + +void ShenandoahGenerationalControlThread::set_gc_mode(ShenandoahGenerationalControlThread::GCMode new_mode) { + if (_mode != new_mode) { + log_debug(gc)("Transition from: %s to: %s", gc_mode_name(_mode), gc_mode_name(new_mode)); + MonitorLocker ml(&_regulator_lock, Mutex::_no_safepoint_check_flag); + _mode = new_mode; + ml.notify_all(); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp new file mode 100644 index 00000000000..295882fe6d9 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALCONTROLTHREAD_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALCONTROLTHREAD_HPP + +#include "gc/shared/gcCause.hpp" +#include "gc/shenandoah/shenandoahController.hpp" +#include "gc/shenandoah/shenandoahGenerationType.hpp" +#include "gc/shenandoah/shenandoahGC.hpp" +#include "gc/shenandoah/shenandoahPadding.hpp" +#include "gc/shenandoah/shenandoahSharedVariables.hpp" + +class ShenandoahOldGeneration; +class ShenandoahGeneration; +class ShenandoahGenerationalHeap; +class ShenandoahHeap; + +class ShenandoahGenerationalControlThread: public ShenandoahController { + friend class VMStructs; + +public: + typedef enum { + none, + concurrent_normal, + stw_degenerated, + stw_full, + bootstrapping_old, + servicing_old + } GCMode; + +private: + Monitor _control_lock; + Monitor _regulator_lock; + + ShenandoahSharedFlag _allow_old_preemption; + ShenandoahSharedFlag _preemption_requested; + + GCCause::Cause _requested_gc_cause; + volatile ShenandoahGenerationType _requested_generation; + ShenandoahGC::ShenandoahDegenPoint _degen_point; + ShenandoahGeneration* _degen_generation; + + shenandoah_padding(0); + volatile GCMode _mode; + shenandoah_padding(1); + +public: + ShenandoahGenerationalControlThread(); + + void run_service() override; + void stop_service() override; + + void request_gc(GCCause::Cause cause) override; + + // Return true if the request to start a concurrent GC for the given generation succeeded. + bool request_concurrent_gc(ShenandoahGenerationType generation); + + GCMode gc_mode() { + return _mode; + } +private: + + // Returns true if the cycle has been cancelled or degenerated. + bool check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point); + + // Returns true if the old generation marking completed (i.e., final mark executed for old generation). + bool resume_concurrent_old_cycle(ShenandoahOldGeneration* generation, GCCause::Cause cause); + void service_concurrent_cycle(ShenandoahGeneration* generation, GCCause::Cause cause, bool reset_old_bitmap_specially); + void service_stw_full_cycle(GCCause::Cause cause); + void service_stw_degenerated_cycle(GCCause::Cause cause, ShenandoahGC::ShenandoahDegenPoint point); + + void notify_gc_waiters(); + + // Handle GC request. + // Blocks until GC is over. + void handle_requested_gc(GCCause::Cause cause); + + bool is_explicit_gc(GCCause::Cause cause) const; + bool is_implicit_gc(GCCause::Cause cause) const; + + // Returns true if the old generation marking was interrupted to allow a young cycle. + bool preempt_old_marking(ShenandoahGenerationType generation); + + void process_phase_timings(const ShenandoahGenerationalHeap* heap); + + void service_concurrent_normal_cycle(ShenandoahGenerationalHeap* heap, + ShenandoahGenerationType generation, + GCCause::Cause cause); + + void service_concurrent_old_cycle(ShenandoahGenerationalHeap* heap, + GCCause::Cause &cause); + + void set_gc_mode(GCMode new_mode); + + static const char* gc_mode_name(GCMode mode); + + void notify_control_thread(); + + void service_concurrent_cycle(ShenandoahHeap* heap, + ShenandoahGeneration* generation, + GCCause::Cause &cause, + bool do_old_gc_bootstrap); + +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALCONTROLTHREAD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp new file mode 100644 index 00000000000..81bb5c56a86 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -0,0 +1,326 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPacer.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" + +class ShenandoahConcurrentEvacuator : public ObjectClosure { +private: + ShenandoahGenerationalHeap* const _heap; + Thread* const _thread; +public: + explicit ShenandoahConcurrentEvacuator(ShenandoahGenerationalHeap* heap) : + _heap(heap), _thread(Thread::current()) {} + + void do_object(oop p) override { + shenandoah_assert_marked(nullptr, p); + if (!p->is_forwarded()) { + _heap->evacuate_object(p, _thread); + } + } +}; + +ShenandoahGenerationalEvacuationTask::ShenandoahGenerationalEvacuationTask(ShenandoahGenerationalHeap* heap, + ShenandoahRegionIterator* iterator, + bool concurrent, bool only_promote_regions) : + WorkerTask("Shenandoah Evacuation"), + _heap(heap), + _regions(iterator), + _concurrent(concurrent), + _only_promote_regions(only_promote_regions), + _tenuring_threshold(0) +{ + shenandoah_assert_generational(); + _tenuring_threshold = _heap->age_census()->tenuring_threshold(); +} + +void ShenandoahGenerationalEvacuationTask::work(uint worker_id) { + if (_concurrent) { + ShenandoahConcurrentWorkerSession worker_session(worker_id); + ShenandoahSuspendibleThreadSetJoiner stsj; + do_work(); + } else { + ShenandoahParallelWorkerSession worker_session(worker_id); + do_work(); + } +} + +void ShenandoahGenerationalEvacuationTask::do_work() { + if (_only_promote_regions) { + // No allocations will be made, do not enter oom-during-evac protocol. + assert(ShenandoahHeap::heap()->collection_set()->is_empty(), "Should not have a collection set here"); + promote_regions(); + } else { + assert(!ShenandoahHeap::heap()->collection_set()->is_empty(), "Should have a collection set here"); + ShenandoahEvacOOMScope oom_evac_scope; + evacuate_and_promote_regions(); + } +} + +void log_region(const ShenandoahHeapRegion* r, LogStream* ls) { + ls->print_cr("GenerationalEvacuationTask, looking at %s region " SIZE_FORMAT ", (age: %d) [%s, %s, %s]", + r->is_old()? "old": r->is_young()? "young": "free", r->index(), r->age(), + r->is_active()? "active": "inactive", + r->is_humongous()? (r->is_humongous_start()? "humongous_start": "humongous_continuation"): "regular", + r->is_cset()? "cset": "not-cset"); +} + +void ShenandoahGenerationalEvacuationTask::promote_regions() { + ShenandoahHeapRegion* r; + LogTarget(Debug, gc) lt; + + while ((r = _regions->next()) != nullptr) { + if (lt.is_enabled()) { + LogStream ls(lt); + log_region(r, &ls); + } + + maybe_promote_region(r); + + if (_heap->check_cancelled_gc_and_yield(_concurrent)) { + break; + } + } +} + +void ShenandoahGenerationalEvacuationTask::evacuate_and_promote_regions() { + LogTarget(Debug, gc) lt; + ShenandoahConcurrentEvacuator cl(_heap); + ShenandoahHeapRegion* r; + + while ((r = _regions->next()) != nullptr) { + if (lt.is_enabled()) { + LogStream ls(lt); + log_region(r, &ls); + } + + if (r->is_cset()) { + assert(r->has_live(), "Region " SIZE_FORMAT " should have been reclaimed early", r->index()); + _heap->marked_object_iterate(r, &cl); + if (ShenandoahPacing) { + _heap->pacer()->report_evac(r->used() >> LogHeapWordSize); + } + } else { + maybe_promote_region(r); + } + + if (_heap->check_cancelled_gc_and_yield(_concurrent)) { + break; + } + } +} + + +void ShenandoahGenerationalEvacuationTask::maybe_promote_region(ShenandoahHeapRegion* r) { + if (r->is_young() && r->is_active() && (r->age() >= _tenuring_threshold)) { + if (r->is_humongous_start()) { + // We promote humongous_start regions along with their affiliated continuations during evacuation rather than + // doing this work during a safepoint. We cannot put humongous regions into the collection set because that + // triggers the load-reference barrier (LRB) to copy on reference fetch. + // + // Aged humongous continuation regions are handled with their start region. If an aged regular region has + // more garbage than ShenandoahOldGarbageThreshold, we'll promote by evacuation. If there is room for evacuation + // in this cycle, the region will be in the collection set. If there is not room, the region will be promoted + // by evacuation in some future GC cycle. + promote_humongous(r); + } else if (r->is_regular() && (r->get_top_before_promote() != nullptr)) { + // Likewise, we cannot put promote-in-place regions into the collection set because that would also trigger + // the LRB to copy on reference fetch. + // + // If an aged regular region has received allocations during the current cycle, we do not promote because the + // newly allocated objects do not have appropriate age; this region's age will be reset to zero at end of cycle. + promote_in_place(r); + } + } +} + +// When we promote a region in place, we can continue to use the established marking context to guide subsequent remembered +// set scans of this region's content. The region will be coalesced and filled prior to the next old-gen marking effort. +// We identify the entirety of the region as DIRTY to force the next remembered set scan to identify the "interesting pointers" +// contained herein. +void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion* region) { + ShenandoahMarkingContext* const marking_context = _heap->complete_marking_context(); + HeapWord* const tams = marking_context->top_at_mark_start(region); + + { + const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100; + shenandoah_assert_generations_reconciled(); + assert(!_heap->is_concurrent_old_mark_in_progress(), "Cannot promote in place during old marking"); + assert(region->garbage_before_padded_for_promote() < old_garbage_threshold, "Region " SIZE_FORMAT " has too much garbage for promotion", region->index()); + assert(region->is_young(), "Only young regions can be promoted"); + assert(region->is_regular(), "Use different service to promote humongous regions"); + assert(region->age() >= _tenuring_threshold, "Only promote regions that are sufficiently aged"); + assert(region->get_top_before_promote() == tams, "Region " SIZE_FORMAT " has been used for allocations before promotion", region->index()); + } + + ShenandoahOldGeneration* const old_gen = _heap->old_generation(); + ShenandoahYoungGeneration* const young_gen = _heap->young_generation(); + + // Rebuild the remembered set information and mark the entire range as DIRTY. We do NOT scan the content of this + // range to determine which cards need to be DIRTY. That would force us to scan the region twice, once now, and + // once during the subsequent remembered set scan. Instead, we blindly (conservatively) mark everything as DIRTY + // now and then sort out the CLEAN pages during the next remembered set scan. + // + // Rebuilding the remembered set consists of clearing all object registrations (reset_object_range()) here, + // then registering every live object and every coalesced range of free objects in the loop that follows. + ShenandoahScanRemembered* const scanner = old_gen->card_scan(); + scanner->reset_object_range(region->bottom(), region->end()); + scanner->mark_range_as_dirty(region->bottom(), region->get_top_before_promote() - region->bottom()); + + HeapWord* obj_addr = region->bottom(); + while (obj_addr < tams) { + oop obj = cast_to_oop(obj_addr); + if (marking_context->is_marked(obj)) { + assert(obj->klass() != nullptr, "klass should not be NULL"); + // This thread is responsible for registering all objects in this region. No need for lock. + scanner->register_object_without_lock(obj_addr); + obj_addr += obj->size(); + } else { + HeapWord* next_marked_obj = marking_context->get_next_marked_addr(obj_addr, tams); + assert(next_marked_obj <= tams, "next marked object cannot exceed tams"); + size_t fill_size = next_marked_obj - obj_addr; + assert(fill_size >= ShenandoahHeap::min_fill_size(), "previously allocated objects known to be larger than min_size"); + ShenandoahHeap::fill_with_object(obj_addr, fill_size); + scanner->register_object_without_lock(obj_addr); + obj_addr = next_marked_obj; + } + } + // We do not need to scan above TAMS because restored top equals tams + assert(obj_addr == tams, "Expect loop to terminate when obj_addr equals tams"); + + + { + ShenandoahHeapLocker locker(_heap->lock()); + + HeapWord* update_watermark = region->get_update_watermark(); + + // Now that this region is affiliated with old, we can allow it to receive allocations, though it may not be in the + // is_collector_free range. + region->restore_top_before_promote(); + + size_t region_used = region->used(); + + // The update_watermark was likely established while we had the artificially high value of top. Make it sane now. + assert(update_watermark >= region->top(), "original top cannot exceed preserved update_watermark"); + region->set_update_watermark(region->top()); + + // Unconditionally transfer one region from young to old. This represents the newly promoted region. + // This expands old and shrinks new by the size of one region. Strictly, we do not "need" to expand old + // if there are already enough unaffiliated regions in old to account for this newly promoted region. + // However, if we do not transfer the capacities, we end up reducing the amount of memory that would have + // otherwise been available to hold old evacuations, because old available is max_capacity - used and now + // we would be trading a fully empty region for a partially used region. + young_gen->decrease_used(region_used); + young_gen->decrement_affiliated_region_count(); + + // transfer_to_old() increases capacity of old and decreases capacity of young + _heap->generation_sizer()->force_transfer_to_old(1); + region->set_affiliation(OLD_GENERATION); + + old_gen->increment_affiliated_region_count(); + old_gen->increase_used(region_used); + + // add_old_collector_free_region() increases promoted_reserve() if available space exceeds plab_min_size() + _heap->free_set()->add_promoted_in_place_region_to_old_collector(region); + } +} + +void ShenandoahGenerationalEvacuationTask::promote_humongous(ShenandoahHeapRegion* region) { + ShenandoahMarkingContext* marking_context = _heap->marking_context(); + oop obj = cast_to_oop(region->bottom()); + assert(_heap->gc_generation()->is_mark_complete(), "sanity"); + shenandoah_assert_generations_reconciled(); + assert(region->is_young(), "Only young regions can be promoted"); + assert(region->is_humongous_start(), "Should not promote humongous continuation in isolation"); + assert(region->age() >= _tenuring_threshold, "Only promote regions that are sufficiently aged"); + assert(marking_context->is_marked(obj), "promoted humongous object should be alive"); + + const size_t used_bytes = obj->size() * HeapWordSize; + const size_t spanned_regions = ShenandoahHeapRegion::required_regions(used_bytes); + const size_t humongous_waste = spanned_regions * ShenandoahHeapRegion::region_size_bytes() - obj->size() * HeapWordSize; + const size_t index_limit = region->index() + spanned_regions; + + ShenandoahOldGeneration* const old_gen = _heap->old_generation(); + ShenandoahGeneration* const young_gen = _heap->young_generation(); + { + // We need to grab the heap lock in order to avoid a race when changing the affiliations of spanned_regions from + // young to old. + ShenandoahHeapLocker locker(_heap->lock()); + + // We promote humongous objects unconditionally, without checking for availability. We adjust + // usage totals, including humongous waste, after evacuation is done. + log_debug(gc)("promoting humongous region " SIZE_FORMAT ", spanning " SIZE_FORMAT, region->index(), spanned_regions); + + young_gen->decrease_used(used_bytes); + young_gen->decrease_humongous_waste(humongous_waste); + young_gen->decrease_affiliated_region_count(spanned_regions); + + // transfer_to_old() increases capacity of old and decreases capacity of young + _heap->generation_sizer()->force_transfer_to_old(spanned_regions); + + // For this region and each humongous continuation region spanned by this humongous object, change + // affiliation to OLD_GENERATION and adjust the generation-use tallies. The remnant of memory + // in the last humongous region that is not spanned by obj is currently not used. + for (size_t i = region->index(); i < index_limit; i++) { + ShenandoahHeapRegion* r = _heap->get_region(i); + log_debug(gc)("promoting humongous region " SIZE_FORMAT ", from " PTR_FORMAT " to " PTR_FORMAT, + r->index(), p2i(r->bottom()), p2i(r->top())); + // We mark the entire humongous object's range as dirty after loop terminates, so no need to dirty the range here + r->set_affiliation(OLD_GENERATION); + } + + old_gen->increase_affiliated_region_count(spanned_regions); + old_gen->increase_used(used_bytes); + old_gen->increase_humongous_waste(humongous_waste); + } + + // Since this region may have served previously as OLD, it may hold obsolete object range info. + HeapWord* const humongous_bottom = region->bottom(); + ShenandoahScanRemembered* const scanner = old_gen->card_scan(); + scanner->reset_object_range(humongous_bottom, humongous_bottom + spanned_regions * ShenandoahHeapRegion::region_size_words()); + // Since the humongous region holds only one object, no lock is necessary for this register_object() invocation. + scanner->register_object_without_lock(humongous_bottom); + + if (obj->is_typeArray()) { + // Primitive arrays don't need to be scanned. + log_debug(gc)("Clean cards for promoted humongous object (Region " SIZE_FORMAT ") from " PTR_FORMAT " to " PTR_FORMAT, + region->index(), p2i(humongous_bottom), p2i(humongous_bottom + obj->size())); + scanner->mark_range_as_clean(humongous_bottom, obj->size()); + } else { + log_debug(gc)("Dirty cards for promoted humongous object (Region " SIZE_FORMAT ") from " PTR_FORMAT " to " PTR_FORMAT, + region->index(), p2i(humongous_bottom), p2i(humongous_bottom + obj->size())); + scanner->mark_range_as_dirty(humongous_bottom, obj->size()); + } +} + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp new file mode 100644 index 00000000000..abe2fc0110c --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALEVACUATIONTASK_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALEVACUATIONTASK_HPP + +#include "gc/shared/workerThread.hpp" + +class ShenandoahGenerationalHeap; +class ShenandoahHeapRegion; +class ShenandoahRegionIterator; + +// Unlike ShenandoahEvacuationTask, this iterates over all regions rather than just the collection set. +// This is needed in order to promote humongous start regions if age() >= tenure threshold. +class ShenandoahGenerationalEvacuationTask : public WorkerTask { +private: + ShenandoahGenerationalHeap* const _heap; + ShenandoahRegionIterator* _regions; + bool _concurrent; + bool _only_promote_regions; + uint _tenuring_threshold; + +public: + ShenandoahGenerationalEvacuationTask(ShenandoahGenerationalHeap* sh, + ShenandoahRegionIterator* iterator, + bool concurrent, bool only_promote_regions); + void work(uint worker_id) override; +private: + void do_work(); + void promote_regions(); + void evacuate_and_promote_regions(); + void maybe_promote_region(ShenandoahHeapRegion* region); + void promote_in_place(ShenandoahHeapRegion* region); + void promote_humongous(ShenandoahHeapRegion* region); +}; + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALEVACUATIONTASK_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp new file mode 100644 index 00000000000..fe38c996bd8 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp @@ -0,0 +1,390 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shared/fullGCForwarding.inline.hpp" +#include "gc/shared/preservedMarks.inline.hpp" +#include "gc/shenandoah/shenandoahGenerationalFullGC.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" + +#ifdef ASSERT +void assert_regions_used_not_more_than_capacity(ShenandoahGeneration* generation) { + assert(generation->used_regions_size() <= generation->max_capacity(), + "%s generation affiliated regions must be less than capacity", generation->name()); +} + +void assert_usage_not_more_than_regions_used(ShenandoahGeneration* generation) { + assert(generation->used_including_humongous_waste() <= generation->used_regions_size(), + "%s consumed can be no larger than span of affiliated regions", generation->name()); +} +#else +void assert_regions_used_not_more_than_capacity(ShenandoahGeneration* generation) {} +void assert_usage_not_more_than_regions_used(ShenandoahGeneration* generation) {} +#endif + + +void ShenandoahGenerationalFullGC::prepare() { + auto heap = ShenandoahGenerationalHeap::heap(); + // Since we may arrive here from degenerated GC failure of either young or old, establish generation as GLOBAL. + heap->set_gc_generation(heap->global_generation()); + heap->set_active_generation(); + + // No need for old_gen->increase_used() as this was done when plabs were allocated. + heap->reset_generation_reserves(); + + // Full GC supersedes any marking or coalescing in old generation. + heap->old_generation()->cancel_gc(); +} + +void ShenandoahGenerationalFullGC::handle_completion(ShenandoahHeap* heap) { + // Full GC should reset time since last gc for young and old heuristics + ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::cast(heap); + ShenandoahYoungGeneration* young = gen_heap->young_generation(); + ShenandoahOldGeneration* old = gen_heap->old_generation(); + young->heuristics()->record_cycle_end(); + old->heuristics()->record_cycle_end(); + + gen_heap->mmu_tracker()->record_full(GCId::current()); + gen_heap->log_heap_status("At end of Full GC"); + + assert(old->is_idle(), "After full GC, old generation should be idle."); + + // Since we allow temporary violation of these constraints during Full GC, we want to enforce that the assertions are + // made valid by the time Full GC completes. + assert_regions_used_not_more_than_capacity(old); + assert_regions_used_not_more_than_capacity(young); + assert_usage_not_more_than_regions_used(old); + assert_usage_not_more_than_regions_used(young); + + // Establish baseline for next old-has-grown trigger. + old->set_live_bytes_after_last_mark(old->used_including_humongous_waste()); +} + +void ShenandoahGenerationalFullGC::rebuild_remembered_set(ShenandoahHeap* heap) { + ShenandoahGCPhase phase(ShenandoahPhaseTimings::full_gc_reconstruct_remembered_set); + ShenandoahRegionIterator regions; + ShenandoahReconstructRememberedSetTask task(®ions); + heap->workers()->run_task(&task); + + // Rebuilding the remembered set recomputes all the card offsets for objects. + // The adjust pointers phase coalesces and fills all necessary regions. In case + // we came to the full GC from an incomplete global cycle, we need to indicate + // that the old regions are parsable. + heap->old_generation()->set_parsable(true); +} + +void ShenandoahGenerationalFullGC::balance_generations_after_gc(ShenandoahHeap* heap) { + ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::cast(heap); + ShenandoahOldGeneration* const old_gen = gen_heap->old_generation(); + + size_t old_usage = old_gen->used_regions_size(); + size_t old_capacity = old_gen->max_capacity(); + + assert(old_usage % ShenandoahHeapRegion::region_size_bytes() == 0, "Old usage must align with region size"); + assert(old_capacity % ShenandoahHeapRegion::region_size_bytes() == 0, "Old capacity must align with region size"); + + if (old_capacity > old_usage) { + size_t excess_old_regions = (old_capacity - old_usage) / ShenandoahHeapRegion::region_size_bytes(); + gen_heap->generation_sizer()->transfer_to_young(excess_old_regions); + } else if (old_capacity < old_usage) { + size_t old_regions_deficit = (old_usage - old_capacity) / ShenandoahHeapRegion::region_size_bytes(); + gen_heap->generation_sizer()->force_transfer_to_old(old_regions_deficit); + } + + log_info(gc, ergo)("FullGC done: young usage: " PROPERFMT ", old usage: " PROPERFMT, + PROPERFMTARGS(gen_heap->young_generation()->used()), + PROPERFMTARGS(old_gen->used())); +} + +void ShenandoahGenerationalFullGC::balance_generations_after_rebuilding_free_set() { + auto result = ShenandoahGenerationalHeap::heap()->balance_generations(); + LogTarget(Info, gc, ergo) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + result.print_on("Full GC", &ls); + } +} + +void ShenandoahGenerationalFullGC::log_live_in_old(ShenandoahHeap* heap) { + LogTarget(Debug, gc) lt; + if (lt.is_enabled()) { + size_t live_bytes_in_old = 0; + for (size_t i = 0; i < heap->num_regions(); i++) { + ShenandoahHeapRegion* r = heap->get_region(i); + if (r->is_old()) { + live_bytes_in_old += r->get_live_data_bytes(); + } + } + log_debug(gc)("Live bytes in old after STW mark: " PROPERFMT, PROPERFMTARGS(live_bytes_in_old)); + } +} + +void ShenandoahGenerationalFullGC::restore_top_before_promote(ShenandoahHeap* heap) { + for (size_t i = 0; i < heap->num_regions(); i++) { + ShenandoahHeapRegion* r = heap->get_region(i); + if (r->get_top_before_promote() != nullptr) { + r->restore_top_before_promote(); + } + } +} + +void ShenandoahGenerationalFullGC::account_for_region(ShenandoahHeapRegion* r, size_t ®ion_count, size_t ®ion_usage, size_t &humongous_waste) { + region_count++; + region_usage += r->used(); + if (r->is_humongous_start()) { + // For each humongous object, we take this path once regardless of how many regions it spans. + HeapWord* obj_addr = r->bottom(); + oop obj = cast_to_oop(obj_addr); + size_t word_size = obj->size(); + size_t region_size_words = ShenandoahHeapRegion::region_size_words(); + size_t overreach = word_size % region_size_words; + if (overreach != 0) { + humongous_waste += (region_size_words - overreach) * HeapWordSize; + } + // else, this humongous object aligns exactly on region size, so no waste. + } +} + +void ShenandoahGenerationalFullGC::maybe_coalesce_and_fill_region(ShenandoahHeapRegion* r) { + if (r->is_pinned() && r->is_old() && r->is_active() && !r->is_humongous()) { + r->begin_preemptible_coalesce_and_fill(); + r->oop_coalesce_and_fill(false); + } +} + +void ShenandoahGenerationalFullGC::compute_balances() { + auto heap = ShenandoahGenerationalHeap::heap(); + + // In case this Full GC resulted from degeneration, clear the tally on anticipated promotion. + heap->old_generation()->set_promotion_potential(0); + // Invoke this in case we are able to transfer memory from OLD to YOUNG. + heap->compute_old_generation_balance(0, 0); +} + +ShenandoahPrepareForGenerationalCompactionObjectClosure::ShenandoahPrepareForGenerationalCompactionObjectClosure(PreservedMarks* preserved_marks, + GrowableArray& empty_regions, + ShenandoahHeapRegion* from_region, uint worker_id) : + _preserved_marks(preserved_marks), + _heap(ShenandoahGenerationalHeap::heap()), + _tenuring_threshold(0), + _empty_regions(empty_regions), + _empty_regions_pos(0), + _old_to_region(nullptr), + _young_to_region(nullptr), + _from_region(nullptr), + _from_affiliation(ShenandoahAffiliation::FREE), + _old_compact_point(nullptr), + _young_compact_point(nullptr), + _worker_id(worker_id) { + assert(from_region != nullptr, "Worker needs from_region"); + // assert from_region has live? + if (from_region->is_old()) { + _old_to_region = from_region; + _old_compact_point = from_region->bottom(); + } else if (from_region->is_young()) { + _young_to_region = from_region; + _young_compact_point = from_region->bottom(); + } + + _tenuring_threshold = _heap->age_census()->tenuring_threshold(); +} + +void ShenandoahPrepareForGenerationalCompactionObjectClosure::set_from_region(ShenandoahHeapRegion* from_region) { + log_debug(gc)("Worker %u compacting %s Region " SIZE_FORMAT " which had used " SIZE_FORMAT " and %s live", + _worker_id, from_region->affiliation_name(), + from_region->index(), from_region->used(), from_region->has_live()? "has": "does not have"); + + _from_region = from_region; + _from_affiliation = from_region->affiliation(); + if (_from_region->has_live()) { + if (_from_affiliation == ShenandoahAffiliation::OLD_GENERATION) { + if (_old_to_region == nullptr) { + _old_to_region = from_region; + _old_compact_point = from_region->bottom(); + } + } else { + assert(_from_affiliation == ShenandoahAffiliation::YOUNG_GENERATION, "from_region must be OLD or YOUNG"); + if (_young_to_region == nullptr) { + _young_to_region = from_region; + _young_compact_point = from_region->bottom(); + } + } + } // else, we won't iterate over this _from_region so we don't need to set up to region to hold copies +} + +void ShenandoahPrepareForGenerationalCompactionObjectClosure::finish() { + finish_old_region(); + finish_young_region(); +} + +void ShenandoahPrepareForGenerationalCompactionObjectClosure::finish_old_region() { + if (_old_to_region != nullptr) { + log_debug(gc)("Planned compaction into Old Region " SIZE_FORMAT ", used: " SIZE_FORMAT " tabulated by worker %u", + _old_to_region->index(), _old_compact_point - _old_to_region->bottom(), _worker_id); + _old_to_region->set_new_top(_old_compact_point); + _old_to_region = nullptr; + } +} + +void ShenandoahPrepareForGenerationalCompactionObjectClosure::finish_young_region() { + if (_young_to_region != nullptr) { + log_debug(gc)("Worker %u planned compaction into Young Region " SIZE_FORMAT ", used: " SIZE_FORMAT, + _worker_id, _young_to_region->index(), _young_compact_point - _young_to_region->bottom()); + _young_to_region->set_new_top(_young_compact_point); + _young_to_region = nullptr; + } +} + +bool ShenandoahPrepareForGenerationalCompactionObjectClosure::is_compact_same_region() { + return (_from_region == _old_to_region) || (_from_region == _young_to_region); +} + +void ShenandoahPrepareForGenerationalCompactionObjectClosure::do_object(oop p) { + assert(_from_region != nullptr, "must set before work"); + assert((_from_region->bottom() <= cast_from_oop(p)) && (cast_from_oop(p) < _from_region->top()), + "Object must reside in _from_region"); + assert(_heap->complete_marking_context()->is_marked(p), "must be marked"); + assert(!_heap->complete_marking_context()->allocated_after_mark_start(p), "must be truly marked"); + + size_t obj_size = p->size(); + uint from_region_age = _from_region->age(); + uint object_age = p->age(); + + bool promote_object = false; + if ((_from_affiliation == ShenandoahAffiliation::YOUNG_GENERATION) && + (from_region_age + object_age >= _tenuring_threshold)) { + if ((_old_to_region != nullptr) && (_old_compact_point + obj_size > _old_to_region->end())) { + finish_old_region(); + _old_to_region = nullptr; + } + if (_old_to_region == nullptr) { + if (_empty_regions_pos < _empty_regions.length()) { + ShenandoahHeapRegion* new_to_region = _empty_regions.at(_empty_regions_pos); + _empty_regions_pos++; + new_to_region->set_affiliation(OLD_GENERATION); + _old_to_region = new_to_region; + _old_compact_point = _old_to_region->bottom(); + promote_object = true; + } + // Else this worker thread does not yet have any empty regions into which this aged object can be promoted so + // we leave promote_object as false, deferring the promotion. + } else { + promote_object = true; + } + } + + if (promote_object || (_from_affiliation == ShenandoahAffiliation::OLD_GENERATION)) { + assert(_old_to_region != nullptr, "_old_to_region should not be nullptr when evacuating to OLD region"); + if (_old_compact_point + obj_size > _old_to_region->end()) { + ShenandoahHeapRegion* new_to_region; + + log_debug(gc)("Worker %u finishing old region " SIZE_FORMAT ", compact_point: " PTR_FORMAT ", obj_size: " SIZE_FORMAT + ", &compact_point[obj_size]: " PTR_FORMAT ", region end: " PTR_FORMAT, _worker_id, _old_to_region->index(), + p2i(_old_compact_point), obj_size, p2i(_old_compact_point + obj_size), p2i(_old_to_region->end())); + + // Object does not fit. Get a new _old_to_region. + finish_old_region(); + if (_empty_regions_pos < _empty_regions.length()) { + new_to_region = _empty_regions.at(_empty_regions_pos); + _empty_regions_pos++; + new_to_region->set_affiliation(OLD_GENERATION); + } else { + // If we've exhausted the previously selected _old_to_region, we know that the _old_to_region is distinct + // from _from_region. That's because there is always room for _from_region to be compacted into itself. + // Since we're out of empty regions, let's use _from_region to hold the results of its own compaction. + new_to_region = _from_region; + } + + assert(new_to_region != _old_to_region, "must not reuse same OLD to-region"); + assert(new_to_region != nullptr, "must not be nullptr"); + _old_to_region = new_to_region; + _old_compact_point = _old_to_region->bottom(); + } + + // Object fits into current region, record new location, if object does not move: + assert(_old_compact_point + obj_size <= _old_to_region->end(), "must fit"); + shenandoah_assert_not_forwarded(nullptr, p); + if (_old_compact_point != cast_from_oop(p)) { + _preserved_marks->push_if_necessary(p, p->mark()); + FullGCForwarding::forward_to(p, cast_to_oop(_old_compact_point)); + } + _old_compact_point += obj_size; + } else { + assert(_from_affiliation == ShenandoahAffiliation::YOUNG_GENERATION, + "_from_region must be OLD_GENERATION or YOUNG_GENERATION"); + assert(_young_to_region != nullptr, "_young_to_region should not be nullptr when compacting YOUNG _from_region"); + + // After full gc compaction, all regions have age 0. Embed the region's age into the object's age in order to preserve + // tenuring progress. + if (_heap->is_aging_cycle()) { + ShenandoahHeap::increase_object_age(p, from_region_age + 1); + } else { + ShenandoahHeap::increase_object_age(p, from_region_age); + } + + if (_young_compact_point + obj_size > _young_to_region->end()) { + ShenandoahHeapRegion* new_to_region; + + log_debug(gc)("Worker %u finishing young region " SIZE_FORMAT ", compact_point: " PTR_FORMAT ", obj_size: " SIZE_FORMAT + ", &compact_point[obj_size]: " PTR_FORMAT ", region end: " PTR_FORMAT, _worker_id, _young_to_region->index(), + p2i(_young_compact_point), obj_size, p2i(_young_compact_point + obj_size), p2i(_young_to_region->end())); + + // Object does not fit. Get a new _young_to_region. + finish_young_region(); + if (_empty_regions_pos < _empty_regions.length()) { + new_to_region = _empty_regions.at(_empty_regions_pos); + _empty_regions_pos++; + new_to_region->set_affiliation(YOUNG_GENERATION); + } else { + // If we've exhausted the previously selected _young_to_region, we know that the _young_to_region is distinct + // from _from_region. That's because there is always room for _from_region to be compacted into itself. + // Since we're out of empty regions, let's use _from_region to hold the results of its own compaction. + new_to_region = _from_region; + } + + assert(new_to_region != _young_to_region, "must not reuse same OLD to-region"); + assert(new_to_region != nullptr, "must not be nullptr"); + _young_to_region = new_to_region; + _young_compact_point = _young_to_region->bottom(); + } + + // Object fits into current region, record new location, if object does not move: + assert(_young_compact_point + obj_size <= _young_to_region->end(), "must fit"); + shenandoah_assert_not_forwarded(nullptr, p); + + if (_young_compact_point != cast_from_oop(p)) { + _preserved_marks->push_if_necessary(p, p->mark()); + FullGCForwarding::forward_to(p, cast_to_oop(_young_compact_point)); + } + _young_compact_point += obj_size; + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp new file mode 100644 index 00000000000..d74bcefaaf2 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp @@ -0,0 +1,122 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALFULLGC_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALFULLGC_HPP + +#include "gc/shared/preservedMarks.hpp" +#include "memory/iterator.hpp" +#include "oops/oop.inline.hpp" +#include "utilities/growableArray.hpp" + +class ShenandoahHeap; +class ShenandoahHeapRegion; + +class ShenandoahGenerationalFullGC { +public: + // Prepares the generational mode heap for a full collection. + static void prepare(); + + // Full GC may have compacted objects in the old generation, so we need to rebuild the card tables. + static void rebuild_remembered_set(ShenandoahHeap* heap); + + // Records end of cycle for young and old and establishes size of live bytes in old + static void handle_completion(ShenandoahHeap* heap); + + // Full GC may have promoted regions and may have temporarily violated constraints on the usage and + // capacity of the old generation. This method will balance the accounting of regions between the + // young and old generations. This is somewhat vestigial, but the outcome of this method is used + // when rebuilding the free sets. + static void balance_generations_after_gc(ShenandoahHeap* heap); + + // This will compute the target size for the old generation. It will be expressed in terms of + // a region surplus and deficit, which will be redistributed accordingly after rebuilding the + // free set. + static void compute_balances(); + + // Rebuilding the free set may have resulted in regions being pulled in to the old generation + // evacuation reserve. For this reason, we must update the usage and capacity of the generations + // again. In the distant past, the free set did not know anything about generations, so we had + // a layer built above it to represent how much young/old memory was available. This layer is + // redundant and adds complexity. We would like to one day remove it. Until then, we must keep it + // synchronized with the free set's view of things. + static void balance_generations_after_rebuilding_free_set(); + + // Logs the number of live bytes marked in the old generation. This is _not_ the same + // value used as the baseline for the old generation _after_ the full gc is complete. + // The value reported in the logs does not include objects and regions that may be + // promoted during the full gc. + static void log_live_in_old(ShenandoahHeap* heap); + + // This is used to tally the number, usage and space wasted by humongous objects for each generation. + static void account_for_region(ShenandoahHeapRegion* r, size_t ®ion_count, size_t ®ion_usage, size_t &humongous_waste); + + // Regions which are scheduled for in-place promotion during evacuation temporarily + // have their top set to their end to prevent new objects from being allocated in them + // before they are promoted. If the full GC encounters such a region, it means the + // in-place promotion did not happen, and we must restore the original value of top. + static void restore_top_before_promote(ShenandoahHeap* heap); + + // Pinned regions are not compacted, so they may still hold unmarked objects with + // references to reclaimed memory. Remembered set scanning will crash if it attempts + // to iterate the oops in these objects. This method fills in dead objects for pinned, + // old regions. + static void maybe_coalesce_and_fill_region(ShenandoahHeapRegion* r); +}; + +class ShenandoahPrepareForGenerationalCompactionObjectClosure : public ObjectClosure { +private: + PreservedMarks* const _preserved_marks; + ShenandoahGenerationalHeap* const _heap; + uint _tenuring_threshold; + + // _empty_regions is a thread-local list of heap regions that have been completely emptied by this worker thread's + // compaction efforts. The worker thread that drives these efforts adds compacted regions to this list if the + // region has not been compacted onto itself. + GrowableArray& _empty_regions; + int _empty_regions_pos; + ShenandoahHeapRegion* _old_to_region; + ShenandoahHeapRegion* _young_to_region; + ShenandoahHeapRegion* _from_region; + ShenandoahAffiliation _from_affiliation; + HeapWord* _old_compact_point; + HeapWord* _young_compact_point; + uint _worker_id; + +public: + ShenandoahPrepareForGenerationalCompactionObjectClosure(PreservedMarks* preserved_marks, + GrowableArray& empty_regions, + ShenandoahHeapRegion* from_region, uint worker_id); + + void set_from_region(ShenandoahHeapRegion* from_region); + void finish(); + void finish_old_region(); + void finish_young_region(); + bool is_compact_same_region(); + int empty_regions_pos() const { return _empty_regions_pos; } + + void do_object(oop p) override; +}; + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALFULLGC_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp new file mode 100644 index 00000000000..5b8afc52b93 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -0,0 +1,1140 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahAgeCensus.hpp" +#include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" +#include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" +#include "gc/shenandoah/shenandoahInitLogger.hpp" +#include "gc/shenandoah/shenandoahMemoryPool.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPhaseTimings.hpp" +#include "gc/shenandoah/shenandoahRegulatorThread.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahWorkerPolicy.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "logging/log.hpp" +#include "utilities/events.hpp" + + +class ShenandoahGenerationalInitLogger : public ShenandoahInitLogger { +public: + static void print() { + ShenandoahGenerationalInitLogger logger; + logger.print_all(); + } + + void print_heap() override { + ShenandoahInitLogger::print_heap(); + + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + + ShenandoahYoungGeneration* young = heap->young_generation(); + log_info(gc, init)("Young Generation Soft Size: " EXACTFMT, EXACTFMTARGS(young->soft_max_capacity())); + log_info(gc, init)("Young Generation Max: " EXACTFMT, EXACTFMTARGS(young->max_capacity())); + + ShenandoahOldGeneration* old = heap->old_generation(); + log_info(gc, init)("Old Generation Soft Size: " EXACTFMT, EXACTFMTARGS(old->soft_max_capacity())); + log_info(gc, init)("Old Generation Max: " EXACTFMT, EXACTFMTARGS(old->max_capacity())); + } + +protected: + void print_gc_specific() override { + ShenandoahInitLogger::print_gc_specific(); + + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + log_info(gc, init)("Young Heuristics: %s", heap->young_generation()->heuristics()->name()); + log_info(gc, init)("Old Heuristics: %s", heap->old_generation()->heuristics()->name()); + } +}; + +size_t ShenandoahGenerationalHeap::calculate_min_plab() { + return align_up(PLAB::min_size(), CardTable::card_size_in_words()); +} + +size_t ShenandoahGenerationalHeap::calculate_max_plab() { + size_t MaxTLABSizeWords = ShenandoahHeapRegion::max_tlab_size_words(); + return align_down(MaxTLABSizeWords, CardTable::card_size_in_words()); +} + +// Returns size in bytes +size_t ShenandoahGenerationalHeap::unsafe_max_tlab_alloc(Thread *thread) const { + return MIN2(ShenandoahHeapRegion::max_tlab_size_bytes(), young_generation()->available()); +} + +ShenandoahGenerationalHeap::ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy) : + ShenandoahHeap(policy), + _age_census(nullptr), + _evac_tracker(new ShenandoahEvacuationTracker()), + _min_plab_size(calculate_min_plab()), + _max_plab_size(calculate_max_plab()), + _regulator_thread(nullptr), + _young_gen_memory_pool(nullptr), + _old_gen_memory_pool(nullptr) { + assert(is_aligned(_min_plab_size, CardTable::card_size_in_words()), "min_plab_size must be aligned"); + assert(is_aligned(_max_plab_size, CardTable::card_size_in_words()), "max_plab_size must be aligned"); +} + +void ShenandoahGenerationalHeap::post_initialize() { + ShenandoahHeap::post_initialize(); + _age_census = new ShenandoahAgeCensus(); +} + +void ShenandoahGenerationalHeap::print_init_logger() const { + ShenandoahGenerationalInitLogger logger; + logger.print_all(); +} + +void ShenandoahGenerationalHeap::print_tracing_info() const { + ShenandoahHeap::print_tracing_info(); + + LogTarget(Info, gc, stats) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.cr(); + ls.cr(); + evac_tracker()->print_global_on(&ls); + } +} + +void ShenandoahGenerationalHeap::initialize_heuristics() { + // Initialize global generation and heuristics even in generational mode. + ShenandoahHeap::initialize_heuristics(); + + // Max capacity is the maximum _allowed_ capacity. That is, the maximum allowed capacity + // for old would be total heap - minimum capacity of young. This means the sum of the maximum + // allowed for old and young could exceed the total heap size. It remains the case that the + // _actual_ capacity of young + old = total. + _generation_sizer.heap_size_changed(max_capacity()); + size_t initial_capacity_young = _generation_sizer.max_young_size(); + size_t max_capacity_young = _generation_sizer.max_young_size(); + size_t initial_capacity_old = max_capacity() - max_capacity_young; + size_t max_capacity_old = max_capacity() - initial_capacity_young; + + _young_generation = new ShenandoahYoungGeneration(max_workers(), max_capacity_young, initial_capacity_young); + _old_generation = new ShenandoahOldGeneration(max_workers(), max_capacity_old, initial_capacity_old); + _young_generation->initialize_heuristics(mode()); + _old_generation->initialize_heuristics(mode()); +} + +void ShenandoahGenerationalHeap::initialize_serviceability() { + assert(mode()->is_generational(), "Only for the generational mode"); + _young_gen_memory_pool = new ShenandoahYoungGenMemoryPool(this); + _old_gen_memory_pool = new ShenandoahOldGenMemoryPool(this); + cycle_memory_manager()->add_pool(_young_gen_memory_pool); + cycle_memory_manager()->add_pool(_old_gen_memory_pool); + stw_memory_manager()->add_pool(_young_gen_memory_pool); + stw_memory_manager()->add_pool(_old_gen_memory_pool); +} + +GrowableArray ShenandoahGenerationalHeap::memory_pools() { + assert(mode()->is_generational(), "Only for the generational mode"); + GrowableArray memory_pools(2); + memory_pools.append(_young_gen_memory_pool); + memory_pools.append(_old_gen_memory_pool); + return memory_pools; +} + +void ShenandoahGenerationalHeap::initialize_controller() { + auto control_thread = new ShenandoahGenerationalControlThread(); + _control_thread = control_thread; + _regulator_thread = new ShenandoahRegulatorThread(control_thread); +} + +void ShenandoahGenerationalHeap::gc_threads_do(ThreadClosure* tcl) const { + if (!shenandoah_policy()->is_at_shutdown()) { + ShenandoahHeap::gc_threads_do(tcl); + tcl->do_thread(regulator_thread()); + } +} + +void ShenandoahGenerationalHeap::stop() { + regulator_thread()->stop(); + ShenandoahHeap::stop(); +} + +void ShenandoahGenerationalHeap::evacuate_collection_set(bool concurrent) { + ShenandoahRegionIterator regions; + ShenandoahGenerationalEvacuationTask task(this, ®ions, concurrent, false /* only promote regions */); + workers()->run_task(&task); +} + +void ShenandoahGenerationalHeap::promote_regions_in_place(bool concurrent) { + ShenandoahRegionIterator regions; + ShenandoahGenerationalEvacuationTask task(this, ®ions, concurrent, true /* only promote regions */); + workers()->run_task(&task); +} + +oop ShenandoahGenerationalHeap::evacuate_object(oop p, Thread* thread) { + assert(thread == Thread::current(), "Expected thread parameter to be current thread."); + if (ShenandoahThreadLocalData::is_oom_during_evac(thread)) { + // This thread went through the OOM during evac protocol and it is safe to return + // the forward pointer. It must not attempt to evacuate anymore. + return ShenandoahBarrierSet::resolve_forwarded(p); + } + + assert(ShenandoahThreadLocalData::is_evac_allowed(thread), "must be enclosed in oom-evac scope"); + + ShenandoahHeapRegion* r = heap_region_containing(p); + assert(!r->is_humongous(), "never evacuate humongous objects"); + + ShenandoahAffiliation target_gen = r->affiliation(); + // gc_generation() can change asynchronously and should not be used here. + assert(active_generation() != nullptr, "Error"); + if (active_generation()->is_young() && target_gen == YOUNG_GENERATION) { + markWord mark = p->mark(); + if (mark.is_marked()) { + // Already forwarded. + return ShenandoahBarrierSet::resolve_forwarded(p); + } + + if (mark.has_displaced_mark_helper()) { + // We don't want to deal with MT here just to ensure we read the right mark word. + // Skip the potential promotion attempt for this one. + } else if (r->age() + mark.age() >= age_census()->tenuring_threshold()) { + oop result = try_evacuate_object(p, thread, r, OLD_GENERATION); + if (result != nullptr) { + return result; + } + // If we failed to promote this aged object, we'll fall through to code below and evacuate to young-gen. + } + } + return try_evacuate_object(p, thread, r, target_gen); +} + +// try_evacuate_object registers the object and dirties the associated remembered set information when evacuating +// to OLD_GENERATION. +oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapRegion* from_region, + ShenandoahAffiliation target_gen) { + bool alloc_from_lab = true; + bool has_plab = false; + HeapWord* copy = nullptr; + size_t size = ShenandoahForwarding::size(p); + bool is_promotion = (target_gen == OLD_GENERATION) && from_region->is_young(); + +#ifdef ASSERT + if (ShenandoahOOMDuringEvacALot && + (os::random() & 1) == 0) { // Simulate OOM every ~2nd slow-path call + copy = nullptr; + } else { +#endif + if (UseTLAB) { + switch (target_gen) { + case YOUNG_GENERATION: { + copy = allocate_from_gclab(thread, size); + if ((copy == nullptr) && (size < ShenandoahThreadLocalData::gclab_size(thread))) { + // GCLAB allocation failed because we are bumping up against the limit on young evacuation reserve. Try resetting + // the desired GCLAB size and retry GCLAB allocation to avoid cascading of shared memory allocations. + ShenandoahThreadLocalData::set_gclab_size(thread, PLAB::min_size()); + copy = allocate_from_gclab(thread, size); + // If we still get nullptr, we'll try a shared allocation below. + } + break; + } + case OLD_GENERATION: { + PLAB* plab = ShenandoahThreadLocalData::plab(thread); + if (plab != nullptr) { + has_plab = true; + copy = allocate_from_plab(thread, size, is_promotion); + if ((copy == nullptr) && (size < ShenandoahThreadLocalData::plab_size(thread)) && + ShenandoahThreadLocalData::plab_retries_enabled(thread)) { + // PLAB allocation failed because we are bumping up against the limit on old evacuation reserve or because + // the requested object does not fit within the current plab but the plab still has an "abundance" of memory, + // where abundance is defined as >= ShenGenHeap::plab_min_size(). In the former case, we try shrinking the + // desired PLAB size to the minimum and retry PLAB allocation to avoid cascading of shared memory allocations. + if (plab->words_remaining() < plab_min_size()) { + ShenandoahThreadLocalData::set_plab_size(thread, plab_min_size()); + copy = allocate_from_plab(thread, size, is_promotion); + // If we still get nullptr, we'll try a shared allocation below. + if (copy == nullptr) { + // If retry fails, don't continue to retry until we have success (probably in next GC pass) + ShenandoahThreadLocalData::disable_plab_retries(thread); + } + } + // else, copy still equals nullptr. this causes shared allocation below, preserving this plab for future needs. + } + } + break; + } + default: { + ShouldNotReachHere(); + break; + } + } + } + + if (copy == nullptr) { + // If we failed to allocate in LAB, we'll try a shared allocation. + if (!is_promotion || !has_plab || (size > PLAB::min_size())) { + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(size, target_gen, is_promotion); + copy = allocate_memory(req); + alloc_from_lab = false; + } + // else, we leave copy equal to nullptr, signaling a promotion failure below if appropriate. + // We choose not to promote objects smaller than PLAB::min_size() by way of shared allocations, as this is too + // costly. Instead, we'll simply "evacuate" to young-gen memory (using a GCLAB) and will promote in a future + // evacuation pass. This condition is denoted by: is_promotion && has_plab && (size <= PLAB::min_size()) + } +#ifdef ASSERT + } +#endif + + if (copy == nullptr) { + if (target_gen == OLD_GENERATION) { + if (from_region->is_young()) { + // Signal that promotion failed. Will evacuate this old object somewhere in young gen. + old_generation()->handle_failed_promotion(thread, size); + return nullptr; + } else { + // Remember that evacuation to old gen failed. We'll want to trigger a full gc to recover from this + // after the evacuation threads have finished. + old_generation()->handle_failed_evacuation(); + } + } + + control_thread()->handle_alloc_failure_evac(size); + + oom_evac_handler()->handle_out_of_memory_during_evacuation(); + + return ShenandoahBarrierSet::resolve_forwarded(p); + } + + // Copy the object: + NOT_PRODUCT(evac_tracker()->begin_evacuation(thread, size * HeapWordSize)); + Copy::aligned_disjoint_words(cast_from_oop(p), copy, size); + oop copy_val = cast_to_oop(copy); + + // Update the age of the evacuated object + if (target_gen == YOUNG_GENERATION && is_aging_cycle()) { + ShenandoahHeap::increase_object_age(copy_val, from_region->age() + 1); + } + + // Try to install the new forwarding pointer. + oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val); + if (result == copy_val) { + // Successfully evacuated. Our copy is now the public one! + + // This is necessary for virtual thread support. This uses the mark word without + // considering that it may now be a forwarding pointer (and could therefore crash). + // Secondarily, we do not want to spend cycles relativizing stack chunks for oops + // that lost the evacuation race (and will therefore not become visible). It is + // safe to do this on the public copy (this is also done during concurrent mark). + ContinuationGCSupport::relativize_stack_chunk(copy_val); + + // Record that the evacuation succeeded + NOT_PRODUCT(evac_tracker()->end_evacuation(thread, size * HeapWordSize)); + + if (target_gen == OLD_GENERATION) { + old_generation()->handle_evacuation(copy, size, from_region->is_young()); + } else { + // When copying to the old generation above, we don't care + // about recording object age in the census stats. + assert(target_gen == YOUNG_GENERATION, "Error"); + // We record this census only when simulating pre-adaptive tenuring behavior, or + // when we have been asked to record the census at evacuation rather than at mark + if (ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring) { + evac_tracker()->record_age(thread, size * HeapWordSize, ShenandoahHeap::get_object_age(copy_val)); + } + } + shenandoah_assert_correct(nullptr, copy_val); + return copy_val; + } else { + // Failed to evacuate. We need to deal with the object that is left behind. Since this + // new allocation is certainly after TAMS, it will be considered live in the next cycle. + // But if it happens to contain references to evacuated regions, those references would + // not get updated for this stale copy during this cycle, and we will crash while scanning + // it the next cycle. + if (alloc_from_lab) { + // For LAB allocations, it is enough to rollback the allocation ptr. Either the next + // object will overwrite this stale copy, or the filler object on LAB retirement will + // do this. + switch (target_gen) { + case YOUNG_GENERATION: { + ShenandoahThreadLocalData::gclab(thread)->undo_allocation(copy, size); + break; + } + case OLD_GENERATION: { + ShenandoahThreadLocalData::plab(thread)->undo_allocation(copy, size); + if (is_promotion) { + ShenandoahThreadLocalData::subtract_from_plab_promoted(thread, size * HeapWordSize); + } + break; + } + default: { + ShouldNotReachHere(); + break; + } + } + } else { + // For non-LAB allocations, we have no way to retract the allocation, and + // have to explicitly overwrite the copy with the filler object. With that overwrite, + // we have to keep the fwdptr initialized and pointing to our (stale) copy. + assert(size >= ShenandoahHeap::min_fill_size(), "previously allocated object known to be larger than min_size"); + fill_with_object(copy, size); + shenandoah_assert_correct(nullptr, copy_val); + // For non-LAB allocations, the object has already been registered + } + shenandoah_assert_correct(nullptr, result); + return result; + } +} + +inline HeapWord* ShenandoahGenerationalHeap::allocate_from_plab(Thread* thread, size_t size, bool is_promotion) { + assert(UseTLAB, "TLABs should be enabled"); + + PLAB* plab = ShenandoahThreadLocalData::plab(thread); + HeapWord* obj; + + if (plab == nullptr) { + assert(!thread->is_Java_thread() && !thread->is_Worker_thread(), "Performance: thread should have PLAB: %s", thread->name()); + // No PLABs in this thread, fallback to shared allocation + return nullptr; + } else if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) { + return nullptr; + } + // if plab->word_size() <= 0, thread's plab not yet initialized for this pass, so allow_plab_promotions() is not trustworthy + obj = plab->allocate(size); + if ((obj == nullptr) && (plab->words_remaining() < plab_min_size())) { + // allocate_from_plab_slow will establish allow_plab_promotions(thread) for future invocations + obj = allocate_from_plab_slow(thread, size, is_promotion); + } + // if plab->words_remaining() >= ShenGenHeap::heap()->plab_min_size(), just return nullptr so we can use a shared allocation + if (obj == nullptr) { + return nullptr; + } + + if (is_promotion) { + ShenandoahThreadLocalData::add_to_plab_promoted(thread, size * HeapWordSize); + } + return obj; +} + +// Establish a new PLAB and allocate size HeapWords within it. +HeapWord* ShenandoahGenerationalHeap::allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion) { + // New object should fit the PLAB size + + assert(mode()->is_generational(), "PLABs only relevant to generational GC"); + const size_t plab_min_size = this->plab_min_size(); + // PLABs are aligned to card boundaries to avoid synchronization with concurrent + // allocations in other PLABs. + const size_t min_size = (size > plab_min_size)? align_up(size, CardTable::card_size_in_words()): plab_min_size; + + // Figure out size of new PLAB, using value determined at last refill. + size_t cur_size = ShenandoahThreadLocalData::plab_size(thread); + if (cur_size == 0) { + cur_size = plab_min_size; + } + + // Expand aggressively, doubling at each refill in this epoch, ceiling at plab_max_size() + size_t future_size = MIN2(cur_size * 2, plab_max_size()); + // Doubling, starting at a card-multiple, should give us a card-multiple. (Ceiling and floor + // are card multiples.) + assert(is_aligned(future_size, CardTable::card_size_in_words()), "Card multiple by construction, future_size: " SIZE_FORMAT + ", card_size: " SIZE_FORMAT ", cur_size: " SIZE_FORMAT ", max: " SIZE_FORMAT, + future_size, (size_t) CardTable::card_size_in_words(), cur_size, plab_max_size()); + + // Record new heuristic value even if we take any shortcut. This captures + // the case when moderately-sized objects always take a shortcut. At some point, + // heuristics should catch up with them. Note that the requested cur_size may + // not be honored, but we remember that this is the preferred size. + log_debug(gc, free)("Set new PLAB size: " SIZE_FORMAT, future_size); + ShenandoahThreadLocalData::set_plab_size(thread, future_size); + if (cur_size < size) { + // The PLAB to be allocated is still not large enough to hold the object. Fall back to shared allocation. + // This avoids retiring perfectly good PLABs in order to represent a single large object allocation. + log_debug(gc, free)("Current PLAB size (" SIZE_FORMAT ") is too small for " SIZE_FORMAT, cur_size, size); + return nullptr; + } + + // Retire current PLAB, and allocate a new one. + PLAB* plab = ShenandoahThreadLocalData::plab(thread); + if (plab->words_remaining() < plab_min_size) { + // Retire current PLAB. This takes care of any PLAB book-keeping. + // retire_plab() registers the remnant filler object with the remembered set scanner without a lock. + // Since PLABs are card-aligned, concurrent registrations in other PLABs don't interfere. + retire_plab(plab, thread); + + size_t actual_size = 0; + HeapWord* plab_buf = allocate_new_plab(min_size, cur_size, &actual_size); + if (plab_buf == nullptr) { + if (min_size == plab_min_size) { + // Disable PLAB promotions for this thread because we cannot even allocate a minimal PLAB. This allows us + // to fail faster on subsequent promotion attempts. + ShenandoahThreadLocalData::disable_plab_promotions(thread); + } + return nullptr; + } else { + ShenandoahThreadLocalData::enable_plab_retries(thread); + } + // Since the allocated PLAB may have been down-sized for alignment, plab->allocate(size) below may still fail. + if (ZeroTLAB) { + // ... and clear it. + Copy::zero_to_words(plab_buf, actual_size); + } else { + // ...and zap just allocated object. +#ifdef ASSERT + // Skip mangling the space corresponding to the object header to + // ensure that the returned space is not considered parsable by + // any concurrent GC thread. + size_t hdr_size = oopDesc::header_size(); + Copy::fill_to_words(plab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal); +#endif // ASSERT + } + assert(is_aligned(actual_size, CardTable::card_size_in_words()), "Align by design"); + plab->set_buf(plab_buf, actual_size); + if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) { + return nullptr; + } + return plab->allocate(size); + } else { + // If there's still at least min_size() words available within the current plab, don't retire it. Let's nibble + // away on this plab as long as we can. Meanwhile, return nullptr to force this particular allocation request + // to be satisfied with a shared allocation. By packing more promotions into the previously allocated PLAB, we + // reduce the likelihood of evacuation failures, and we reduce the need for downsizing our PLABs. + return nullptr; + } +} + +HeapWord* ShenandoahGenerationalHeap::allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size) { + // Align requested sizes to card-sized multiples. Align down so that we don't violate max size of TLAB. + assert(is_aligned(min_size, CardTable::card_size_in_words()), "Align by design"); + assert(word_size >= min_size, "Requested PLAB is too small"); + + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(min_size, word_size); + // Note that allocate_memory() sets a thread-local flag to prohibit further promotions by this thread + // if we are at risk of infringing on the old-gen evacuation budget. + HeapWord* res = allocate_memory(req); + if (res != nullptr) { + *actual_size = req.actual_size(); + } else { + *actual_size = 0; + } + assert(is_aligned(res, CardTable::card_size_in_words()), "Align by design"); + return res; +} + +void ShenandoahGenerationalHeap::retire_plab(PLAB* plab, Thread* thread) { + // We don't enforce limits on plab evacuations. We let it consume all available old-gen memory in order to reduce + // probability of an evacuation failure. We do enforce limits on promotion, to make sure that excessive promotion + // does not result in an old-gen evacuation failure. Note that a failed promotion is relatively harmless. Any + // object that fails to promote in the current cycle will be eligible for promotion in a subsequent cycle. + + // When the plab was instantiated, its entirety was treated as if the entire buffer was going to be dedicated to + // promotions. Now that we are retiring the buffer, we adjust for the reality that the plab is not entirely promotions. + // 1. Some of the plab may have been dedicated to evacuations. + // 2. Some of the plab may have been abandoned due to waste (at the end of the plab). + size_t not_promoted = + ShenandoahThreadLocalData::get_plab_actual_size(thread) - ShenandoahThreadLocalData::get_plab_promoted(thread); + ShenandoahThreadLocalData::reset_plab_promoted(thread); + ShenandoahThreadLocalData::set_plab_actual_size(thread, 0); + if (not_promoted > 0) { + old_generation()->unexpend_promoted(not_promoted); + } + const size_t original_waste = plab->waste(); + HeapWord* const top = plab->top(); + + // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. + // It adds the size of this unused memory, in words, to plab->waste(). + plab->retire(); + if (top != nullptr && plab->waste() > original_waste && is_in_old(top)) { + // If retiring the plab created a filler object, then we need to register it with our card scanner so it can + // safely walk the region backing the plab. + log_debug(gc)("retire_plab() is registering remnant of size " SIZE_FORMAT " at " PTR_FORMAT, + plab->waste() - original_waste, p2i(top)); + // No lock is necessary because the PLAB memory is aligned on card boundaries. + old_generation()->card_scan()->register_object_without_lock(top); + } +} + +void ShenandoahGenerationalHeap::retire_plab(PLAB* plab) { + Thread* thread = Thread::current(); + retire_plab(plab, thread); +} + +ShenandoahGenerationalHeap::TransferResult ShenandoahGenerationalHeap::balance_generations() { + shenandoah_assert_heaplocked_or_safepoint(); + + ShenandoahOldGeneration* old_gen = old_generation(); + const ssize_t old_region_balance = old_gen->get_region_balance(); + old_gen->set_region_balance(0); + + if (old_region_balance > 0) { + const auto old_region_surplus = checked_cast(old_region_balance); + const bool success = generation_sizer()->transfer_to_young(old_region_surplus); + return TransferResult { + success, old_region_surplus, "young" + }; + } + + if (old_region_balance < 0) { + const auto old_region_deficit = checked_cast(-old_region_balance); + const bool success = generation_sizer()->transfer_to_old(old_region_deficit); + if (!success) { + old_gen->handle_failed_transfer(); + } + return TransferResult { + success, old_region_deficit, "old" + }; + } + + return TransferResult {true, 0, "none"}; +} + +// Make sure old-generation is large enough, but no larger than is necessary, to hold mixed evacuations +// and promotions, if we anticipate either. Any deficit is provided by the young generation, subject to +// xfer_limit, and any surplus is transferred to the young generation. +// xfer_limit is the maximum we're able to transfer from young to old. +void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t old_xfer_limit, size_t old_cset_regions) { + + // We can limit the old reserve to the size of anticipated promotions: + // max_old_reserve is an upper bound on memory evacuated from old and promoted to old, + // clamped by the old generation space available. + // + // Here's the algebra. + // Let SOEP = ShenandoahOldEvacRatioPercent, + // OE = old evac, + // YE = young evac, and + // TE = total evac = OE + YE + // By definition: + // SOEP/100 = OE/TE + // = OE/(OE+YE) + // => SOEP/(100-SOEP) = OE/((OE+YE)-OE) // componendo-dividendo: If a/b = c/d, then a/(b-a) = c/(d-c) + // = OE/YE + // => OE = YE*SOEP/(100-SOEP) + + // We have to be careful in the event that SOEP is set to 100 by the user. + assert(ShenandoahOldEvacRatioPercent <= 100, "Error"); + const size_t old_available = old_generation()->available(); + // The free set will reserve this amount of memory to hold young evacuations + const size_t young_reserve = (young_generation()->max_capacity() * ShenandoahEvacReserve) / 100; + + // In the case that ShenandoahOldEvacRatioPercent equals 100, max_old_reserve is limited only by xfer_limit. + + const double bound_on_old_reserve = old_available + old_xfer_limit + young_reserve; + const double max_old_reserve = (ShenandoahOldEvacRatioPercent == 100)? + bound_on_old_reserve: MIN2(double(young_reserve * ShenandoahOldEvacRatioPercent) / double(100 - ShenandoahOldEvacRatioPercent), + bound_on_old_reserve); + + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + + // Decide how much old space we should reserve for a mixed collection + double reserve_for_mixed = 0; + if (old_generation()->has_unprocessed_collection_candidates()) { + // We want this much memory to be unfragmented in order to reliably evacuate old. This is conservative because we + // may not evacuate the entirety of unprocessed candidates in a single mixed evacuation. + const double max_evac_need = (double(old_generation()->unprocessed_collection_candidates_live_memory()) * ShenandoahOldEvacWaste); + assert(old_available >= old_generation()->free_unaffiliated_regions() * region_size_bytes, + "Unaffiliated available must be less than total available"); + const double old_fragmented_available = double(old_available - old_generation()->free_unaffiliated_regions() * region_size_bytes); + reserve_for_mixed = max_evac_need + old_fragmented_available; + if (reserve_for_mixed > max_old_reserve) { + reserve_for_mixed = max_old_reserve; + } + } + + // Decide how much space we should reserve for promotions from young + size_t reserve_for_promo = 0; + const size_t promo_load = old_generation()->get_promotion_potential(); + const bool doing_promotions = promo_load > 0; + if (doing_promotions) { + // We're promoting and have a bound on the maximum amount that can be promoted + assert(max_old_reserve >= reserve_for_mixed, "Sanity"); + const size_t available_for_promotions = max_old_reserve - reserve_for_mixed; + reserve_for_promo = MIN2((size_t)(promo_load * ShenandoahPromoEvacWaste), available_for_promotions); + } + + // This is the total old we want to ideally reserve + const size_t old_reserve = reserve_for_mixed + reserve_for_promo; + assert(old_reserve <= max_old_reserve, "cannot reserve more than max for old evacuations"); + + // We now check if the old generation is running a surplus or a deficit. + const size_t max_old_available = old_generation()->available() + old_cset_regions * region_size_bytes; + if (max_old_available >= old_reserve) { + // We are running a surplus, so the old region surplus can go to young + const size_t old_surplus = (max_old_available - old_reserve) / region_size_bytes; + const size_t unaffiliated_old_regions = old_generation()->free_unaffiliated_regions() + old_cset_regions; + const size_t old_region_surplus = MIN2(old_surplus, unaffiliated_old_regions); + old_generation()->set_region_balance(checked_cast(old_region_surplus)); + } else { + // We are running a deficit which we'd like to fill from young. + // Ignore that this will directly impact young_generation()->max_capacity(), + // indirectly impacting young_reserve and old_reserve. These computations are conservative. + // Note that deficit is rounded up by one region. + const size_t old_need = (old_reserve - max_old_available + region_size_bytes - 1) / region_size_bytes; + const size_t max_old_region_xfer = old_xfer_limit / region_size_bytes; + + // Round down the regions we can transfer from young to old. If we're running short + // on young-gen memory, we restrict the xfer. Old-gen collection activities will be + // curtailed if the budget is restricted. + const size_t old_region_deficit = MIN2(old_need, max_old_region_xfer); + old_generation()->set_region_balance(0 - checked_cast(old_region_deficit)); + } +} + +void ShenandoahGenerationalHeap::reset_generation_reserves() { + young_generation()->set_evacuation_reserve(0); + old_generation()->set_evacuation_reserve(0); + old_generation()->set_promoted_reserve(0); +} + +void ShenandoahGenerationalHeap::TransferResult::print_on(const char* when, outputStream* ss) const { + auto heap = ShenandoahGenerationalHeap::heap(); + ShenandoahYoungGeneration* const young_gen = heap->young_generation(); + ShenandoahOldGeneration* const old_gen = heap->old_generation(); + const size_t young_available = young_gen->available(); + const size_t old_available = old_gen->available(); + ss->print_cr("After %s, %s " SIZE_FORMAT " regions to %s to prepare for next gc, old available: " + PROPERFMT ", young_available: " PROPERFMT, + when, + success? "successfully transferred": "failed to transfer", region_count, region_destination, + PROPERFMTARGS(old_available), PROPERFMTARGS(young_available)); +} + +void ShenandoahGenerationalHeap::coalesce_and_fill_old_regions(bool concurrent) { + class ShenandoahGlobalCoalesceAndFill : public WorkerTask { + private: + ShenandoahPhaseTimings::Phase _phase; + ShenandoahRegionIterator _regions; + public: + explicit ShenandoahGlobalCoalesceAndFill(ShenandoahPhaseTimings::Phase phase) : + WorkerTask("Shenandoah Global Coalesce"), + _phase(phase) {} + + void work(uint worker_id) override { + ShenandoahWorkerTimingsTracker timer(_phase, + ShenandoahPhaseTimings::ScanClusters, + worker_id, true); + ShenandoahHeapRegion* region; + while ((region = _regions.next()) != nullptr) { + // old region is not in the collection set and was not immediately trashed + if (region->is_old() && region->is_active() && !region->is_humongous()) { + // Reset the coalesce and fill boundary because this is a global collect + // and cannot be preempted by young collects. We want to be sure the entire + // region is coalesced here and does not resume from a previously interrupted + // or completed coalescing. + region->begin_preemptible_coalesce_and_fill(); + region->oop_coalesce_and_fill(false); + } + } + } + }; + + ShenandoahPhaseTimings::Phase phase = concurrent ? + ShenandoahPhaseTimings::conc_coalesce_and_fill : + ShenandoahPhaseTimings::degen_gc_coalesce_and_fill; + + // This is not cancellable + ShenandoahGlobalCoalesceAndFill coalesce(phase); + workers()->run_task(&coalesce); + old_generation()->set_parsable(true); +} + +template +class ShenandoahGenerationalUpdateHeapRefsTask : public WorkerTask { +private: + ShenandoahGenerationalHeap* _heap; + ShenandoahRegionIterator* _regions; + ShenandoahRegionChunkIterator* _work_chunks; + +public: + explicit ShenandoahGenerationalUpdateHeapRefsTask(ShenandoahRegionIterator* regions, + ShenandoahRegionChunkIterator* work_chunks) : + WorkerTask("Shenandoah Update References"), + _heap(ShenandoahGenerationalHeap::heap()), + _regions(regions), + _work_chunks(work_chunks) + { + bool old_bitmap_stable = _heap->old_generation()->is_mark_complete(); + log_debug(gc, remset)("Update refs, scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable)); + } + + void work(uint worker_id) { + if (CONCURRENT) { + ShenandoahConcurrentWorkerSession worker_session(worker_id); + ShenandoahSuspendibleThreadSetJoiner stsj; + do_work(worker_id); + } else { + ShenandoahParallelWorkerSession worker_session(worker_id); + do_work(worker_id); + } + } + +private: + template + void do_work(uint worker_id) { + T cl; + + if (CONCURRENT && (worker_id == 0)) { + // We ask the first worker to replenish the Mutator free set by moving regions previously reserved to hold the + // results of evacuation. These reserves are no longer necessary because evacuation has completed. + size_t cset_regions = _heap->collection_set()->count(); + + // Now that evacuation is done, we can reassign any regions that had been reserved to hold the results of evacuation + // to the mutator free set. At the end of GC, we will have cset_regions newly evacuated fully empty regions from + // which we will be able to replenish the Collector free set and the OldCollector free set in preparation for the + // next GC cycle. + _heap->free_set()->move_regions_from_collector_to_mutator(cset_regions); + } + // If !CONCURRENT, there's no value in expanding Mutator free set + + ShenandoahHeapRegion* r = _regions->next(); + // We update references for global, old, and young collections. + ShenandoahGeneration* const gc_generation = _heap->gc_generation(); + shenandoah_assert_generations_reconciled(); + assert(gc_generation->is_mark_complete(), "Expected complete marking"); + ShenandoahMarkingContext* const ctx = _heap->marking_context(); + bool is_mixed = _heap->collection_set()->has_old_regions(); + while (r != nullptr) { + HeapWord* update_watermark = r->get_update_watermark(); + assert(update_watermark >= r->bottom(), "sanity"); + + log_debug(gc)("Update refs worker " UINT32_FORMAT ", looking at region " SIZE_FORMAT, worker_id, r->index()); + bool region_progress = false; + if (r->is_active() && !r->is_cset()) { + if (r->is_young()) { + _heap->marked_object_oop_iterate(r, &cl, update_watermark); + region_progress = true; + } else if (r->is_old()) { + if (gc_generation->is_global()) { + + _heap->marked_object_oop_iterate(r, &cl, update_watermark); + region_progress = true; + } + // Otherwise, this is an old region in a young or mixed cycle. Process it during a second phase, below. + // Don't bother to report pacing progress in this case. + } else { + // Because updating of references runs concurrently, it is possible that a FREE inactive region transitions + // to a non-free active region while this loop is executing. Whenever this happens, the changing of a region's + // active status may propagate at a different speed than the changing of the region's affiliation. + + // When we reach this control point, it is because a race has allowed a region's is_active() status to be seen + // by this thread before the region's affiliation() is seen by this thread. + + // It's ok for this race to occur because the newly transformed region does not have any references to be + // updated. + + assert(r->get_update_watermark() == r->bottom(), + "%s Region " SIZE_FORMAT " is_active but not recognized as YOUNG or OLD so must be newly transitioned from FREE", + r->affiliation_name(), r->index()); + } + } + + if (region_progress && ShenandoahPacing) { + _heap->pacer()->report_updaterefs(pointer_delta(update_watermark, r->bottom())); + } + + if (_heap->check_cancelled_gc_and_yield(CONCURRENT)) { + return; + } + + r = _regions->next(); + } + + if (!gc_generation->is_global()) { + // Since this is generational and not GLOBAL, we have to process the remembered set. There's no remembered + // set processing if not in generational mode or if GLOBAL mode. + + // After this thread has exhausted its traditional update-refs work, it continues with updating refs within + // remembered set. The remembered set workload is better balanced between threads, so threads that are "behind" + // can catch up with other threads during this phase, allowing all threads to work more effectively in parallel. + update_references_in_remembered_set(worker_id, cl, ctx, is_mixed); + } + } + + template + void update_references_in_remembered_set(uint worker_id, T &cl, const ShenandoahMarkingContext* ctx, bool is_mixed) { + + struct ShenandoahRegionChunk assignment; + ShenandoahScanRemembered* scanner = _heap->old_generation()->card_scan(); + + while (!_heap->check_cancelled_gc_and_yield(CONCURRENT) && _work_chunks->next(&assignment)) { + // Keep grabbing next work chunk to process until finished, or asked to yield + ShenandoahHeapRegion* r = assignment._r; + if (r->is_active() && !r->is_cset() && r->is_old()) { + HeapWord* start_of_range = r->bottom() + assignment._chunk_offset; + HeapWord* end_of_range = r->get_update_watermark(); + if (end_of_range > start_of_range + assignment._chunk_size) { + end_of_range = start_of_range + assignment._chunk_size; + } + + if (start_of_range >= end_of_range) { + continue; + } + + // Old region in a young cycle or mixed cycle. + if (is_mixed) { + if (r->is_humongous()) { + // Need to examine both dirty and clean cards during mixed evac. + r->oop_iterate_humongous_slice_all(&cl,start_of_range, assignment._chunk_size); + } else { + // Since this is mixed evacuation, old regions that are candidates for collection have not been coalesced + // and filled. This will use mark bits to find objects that need to be updated. + update_references_in_old_region(cl, ctx, scanner, r, start_of_range, end_of_range); + } + } else { + // This is a young evacuation + size_t cluster_size = CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster; + size_t clusters = assignment._chunk_size / cluster_size; + assert(clusters * cluster_size == assignment._chunk_size, "Chunk assignment must align on cluster boundaries"); + scanner->process_region_slice(r, assignment._chunk_offset, clusters, end_of_range, &cl, true, worker_id); + } + + if (ShenandoahPacing) { + _heap->pacer()->report_updaterefs(pointer_delta(end_of_range, start_of_range)); + } + } + } + } + + template + void update_references_in_old_region(T &cl, const ShenandoahMarkingContext* ctx, ShenandoahScanRemembered* scanner, + const ShenandoahHeapRegion* r, HeapWord* start_of_range, + HeapWord* end_of_range) const { + // In case last object in my range spans boundary of my chunk, I may need to scan all the way to top() + ShenandoahObjectToOopBoundedClosure objs(&cl, start_of_range, r->top()); + + // Any object that begins in a previous range is part of a different scanning assignment. Any object that + // starts after end_of_range is also not my responsibility. (Either allocated during evacuation, so does + // not hold pointers to from-space, or is beyond the range of my assigned work chunk.) + + // Find the first object that begins in my range, if there is one. Note that `p` will be set to `end_of_range` + // when no live object is found in the range. + HeapWord* tams = ctx->top_at_mark_start(r); + HeapWord* p = get_first_object_start_word(ctx, scanner, tams, start_of_range, end_of_range); + + while (p < end_of_range) { + // p is known to point to the beginning of marked object obj + oop obj = cast_to_oop(p); + objs.do_object(obj); + HeapWord* prev_p = p; + p += obj->size(); + if (p < tams) { + p = ctx->get_next_marked_addr(p, tams); + // If there are no more marked objects before tams, this returns tams. Note that tams is + // either >= end_of_range, or tams is the start of an object that is marked. + } + assert(p != prev_p, "Lack of forward progress"); + } + } + + HeapWord* get_first_object_start_word(const ShenandoahMarkingContext* ctx, ShenandoahScanRemembered* scanner, HeapWord* tams, + HeapWord* start_of_range, HeapWord* end_of_range) const { + HeapWord* p = start_of_range; + + if (p >= tams) { + // We cannot use ctx->is_marked(obj) to test whether an object begins at this address. Instead, + // we need to use the remembered set crossing map to advance p to the first object that starts + // within the enclosing card. + size_t card_index = scanner->card_index_for_addr(start_of_range); + while (true) { + HeapWord* first_object = scanner->first_object_in_card(card_index); + if (first_object != nullptr) { + p = first_object; + break; + } else if (scanner->addr_for_card_index(card_index + 1) < end_of_range) { + card_index++; + } else { + // Signal that no object was found in range + p = end_of_range; + break; + } + } + } else if (!ctx->is_marked(cast_to_oop(p))) { + p = ctx->get_next_marked_addr(p, tams); + // If there are no more marked objects before tams, this returns tams. + // Note that tams is either >= end_of_range, or tams is the start of an object that is marked. + } + return p; + } +}; + +void ShenandoahGenerationalHeap::update_heap_references(bool concurrent) { + assert(!is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); + const uint nworkers = workers()->active_workers(); + ShenandoahRegionChunkIterator work_list(nworkers); + if (concurrent) { + ShenandoahGenerationalUpdateHeapRefsTask task(&_update_refs_iterator, &work_list); + workers()->run_task(&task); + } else { + ShenandoahGenerationalUpdateHeapRefsTask task(&_update_refs_iterator, &work_list); + workers()->run_task(&task); + } + + if (ShenandoahEnableCardStats) { + // Only do this if we are collecting card stats + ShenandoahScanRemembered* card_scan = old_generation()->card_scan(); + assert(card_scan != nullptr, "Card table must exist when card stats are enabled"); + card_scan->log_card_stats(nworkers, CARD_STAT_UPDATE_REFS); + } +} + +struct ShenandoahCompositeRegionClosure { + template + class Closure : public ShenandoahHeapRegionClosure { + private: + C1 &_c1; + C2 &_c2; + + public: + Closure(C1 &c1, C2 &c2) : ShenandoahHeapRegionClosure(), _c1(c1), _c2(c2) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + _c1.heap_region_do(r); + _c2.heap_region_do(r); + } + + bool is_thread_safe() override { + return _c1.is_thread_safe() && _c2.is_thread_safe(); + } + }; + + template + static Closure of(C1 &c1, C2 &c2) { + return Closure(c1, c2); + } +}; + +class ShenandoahUpdateRegionAges : public ShenandoahHeapRegionClosure { +private: + ShenandoahMarkingContext* _ctx; + +public: + explicit ShenandoahUpdateRegionAges(ShenandoahMarkingContext* ctx) : _ctx(ctx) { } + + void heap_region_do(ShenandoahHeapRegion* r) override { + // Maintenance of region age must follow evacuation in order to account for + // evacuation allocations within survivor regions. We consult region age during + // the subsequent evacuation to determine whether certain objects need to + // be promoted. + if (r->is_young() && r->is_active()) { + HeapWord *tams = _ctx->top_at_mark_start(r); + HeapWord *top = r->top(); + + // Allocations move the watermark when top moves. However, compacting + // objects will sometimes lower top beneath the watermark, after which, + // attempts to read the watermark will assert out (watermark should not be + // higher than top). + if (top > tams) { + // There have been allocations in this region since the start of the cycle. + // Any objects new to this region must not assimilate elevated age. + r->reset_age(); + } else if (ShenandoahGenerationalHeap::heap()->is_aging_cycle()) { + r->increment_age(); + } + } + } + + bool is_thread_safe() override { + return true; + } +}; + +void ShenandoahGenerationalHeap::final_update_refs_update_region_states() { + ShenandoahSynchronizePinnedRegionStates pins; + ShenandoahUpdateRegionAges ages(active_generation()->complete_marking_context()); + auto cl = ShenandoahCompositeRegionClosure::of(pins, ages); + parallel_heap_region_iterate(&cl); +} + +void ShenandoahGenerationalHeap::complete_degenerated_cycle() { + shenandoah_assert_heaplocked_or_safepoint(); + if (is_concurrent_old_mark_in_progress()) { + // This is still necessary for degenerated cycles because the degeneration point may occur + // after final mark of the young generation. See ShenandoahConcurrentGC::op_final_updaterefs for + // a more detailed explanation. + old_generation()->transfer_pointers_from_satb(); + } + + // We defer generation resizing actions until after cset regions have been recycled. + TransferResult result = balance_generations(); + LogTarget(Info, gc, ergo) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + result.print_on("Degenerated GC", &ls); + } + + // In case degeneration interrupted concurrent evacuation or update references, we need to clean up + // transient state. Otherwise, these actions have no effect. + reset_generation_reserves(); + + if (!old_generation()->is_parsable()) { + ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_coalesce_and_fill); + coalesce_and_fill_old_regions(false); + } +} + +void ShenandoahGenerationalHeap::complete_concurrent_cycle() { + if (!old_generation()->is_parsable()) { + // Class unloading may render the card offsets unusable, so we must rebuild them before + // the next remembered set scan. We _could_ let the control thread do this sometime after + // the global cycle has completed and before the next young collection, but under memory + // pressure the control thread may not have the time (that is, because it's running back + // to back GCs). In that scenario, we would have to make the old regions parsable before + // we could start a young collection. This could delay the start of the young cycle and + // throw off the heuristics. + entry_global_coalesce_and_fill(); + } + + TransferResult result; + { + ShenandoahHeapLocker locker(lock()); + + result = balance_generations(); + reset_generation_reserves(); + } + + LogTarget(Info, gc, ergo) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + result.print_on("Concurrent GC", &ls); + } +} + +void ShenandoahGenerationalHeap::entry_global_coalesce_and_fill() { + const char* msg = "Coalescing and filling old regions"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_coalesce_and_fill); + + TraceCollectorStats tcs(monitoring_support()->concurrent_collection_counters()); + EventMark em("%s", msg); + ShenandoahWorkerScope scope(workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_marking(), + "concurrent coalesce and fill"); + + coalesce_and_fill_old_regions(true); +} + +void ShenandoahGenerationalHeap::update_region_ages(ShenandoahMarkingContext* ctx) { + ShenandoahUpdateRegionAges cl(ctx); + parallel_heap_region_iterate(&cl); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp new file mode 100644 index 00000000000..cef5dfd7070 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -0,0 +1,169 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP + +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "memory/universe.hpp" +#include "utilities/checkedCast.hpp" + +class PLAB; +class ShenandoahRegulatorThread; +class ShenandoahGenerationalControlThread; +class ShenandoahAgeCensus; + +class ShenandoahGenerationalHeap : public ShenandoahHeap { +public: + explicit ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy); + void post_initialize() override; + void initialize_heuristics() override; + + static ShenandoahGenerationalHeap* heap() { + shenandoah_assert_generational(); + CollectedHeap* heap = Universe::heap(); + return cast(heap); + } + + static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) { + shenandoah_assert_generational(); + return checked_cast(heap); + } + + void print_init_logger() const override; + void print_tracing_info() const override; + + size_t unsafe_max_tlab_alloc(Thread *thread) const override; + +private: + // ---------- Evacuations and Promotions + // + // True when regions and objects should be aged during the current cycle + ShenandoahSharedFlag _is_aging_cycle; + // Age census used for adapting tenuring threshold + ShenandoahAgeCensus* _age_census; + // Used primarily to look for failed evacuation attempts. + ShenandoahEvacuationTracker* _evac_tracker; + +public: + void set_aging_cycle(bool cond) { + _is_aging_cycle.set_cond(cond); + } + + inline bool is_aging_cycle() const { + return _is_aging_cycle.is_set(); + } + + // Return the age census object for young gen + ShenandoahAgeCensus* age_census() const { + return _age_census; + } + + ShenandoahEvacuationTracker* evac_tracker() const { + return _evac_tracker; + } + + // Ages regions that haven't been used for allocations in the current cycle. + // Resets ages for regions that have been used for allocations. + void update_region_ages(ShenandoahMarkingContext* ctx); + + oop evacuate_object(oop p, Thread* thread) override; + oop try_evacuate_object(oop p, Thread* thread, ShenandoahHeapRegion* from_region, ShenandoahAffiliation target_gen); + void evacuate_collection_set(bool concurrent) override; + void promote_regions_in_place(bool concurrent); + + size_t plab_min_size() const { return _min_plab_size; } + size_t plab_max_size() const { return _max_plab_size; } + + void retire_plab(PLAB* plab); + void retire_plab(PLAB* plab, Thread* thread); + + // ---------- Update References + // + void update_heap_references(bool concurrent) override; + void final_update_refs_update_region_states() override; + +private: + HeapWord* allocate_from_plab(Thread* thread, size_t size, bool is_promotion); + HeapWord* allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion); + HeapWord* allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size); + + const size_t _min_plab_size; + const size_t _max_plab_size; + + static size_t calculate_min_plab(); + static size_t calculate_max_plab(); + +public: + // ---------- Serviceability + // + void initialize_serviceability() override; + GrowableArray memory_pools() override; + + ShenandoahRegulatorThread* regulator_thread() const { return _regulator_thread; } + + void gc_threads_do(ThreadClosure* tcl) const override; + + void stop() override; + + // Used for logging the result of a region transfer outside the heap lock + struct TransferResult { + bool success; + size_t region_count; + const char* region_destination; + + void print_on(const char* when, outputStream* ss) const; + }; + + const ShenandoahGenerationSizer* generation_sizer() const { return &_generation_sizer; } + + // Zeros out the evacuation and promotion reserves + void reset_generation_reserves(); + + // Computes the optimal size for the old generation, represented as a surplus or deficit of old regions + void compute_old_generation_balance(size_t old_xfer_limit, size_t old_cset_regions); + + // Transfers surplus old regions to young, or takes regions from young to satisfy old region deficit + TransferResult balance_generations(); + + // Balances generations, coalesces and fills old regions if necessary + void complete_degenerated_cycle(); + void complete_concurrent_cycle(); +private: + void initialize_controller() override; + void entry_global_coalesce_and_fill(); + + // Makes old regions parsable. This will also rebuild card offsets, which is necessary if classes were unloaded + void coalesce_and_fill_old_regions(bool concurrent); + + ShenandoahRegulatorThread* _regulator_thread; + + MemoryPool* _young_gen_memory_pool; + MemoryPool* _old_gen_memory_pool; + + ShenandoahGenerationSizer _generation_sizer; +}; + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp new file mode 100644 index 00000000000..9b13c7c95af --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp @@ -0,0 +1,146 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahAgeCensus.hpp" +#include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGlobalGeneration.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahVerifier.hpp" + + +const char* ShenandoahGlobalGeneration::name() const { + return type() == NON_GEN ? "" : "Global"; +} + +size_t ShenandoahGlobalGeneration::max_capacity() const { + return ShenandoahHeap::heap()->max_capacity(); +} + +size_t ShenandoahGlobalGeneration::used_regions() const { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + assert(heap->mode()->is_generational(), "Region usage accounting is only for generational mode"); + return heap->old_generation()->used_regions() + heap->young_generation()->used_regions(); +} + +size_t ShenandoahGlobalGeneration::used_regions_size() const { + return ShenandoahHeap::heap()->capacity(); +} + +size_t ShenandoahGlobalGeneration::soft_max_capacity() const { + return ShenandoahHeap::heap()->soft_max_capacity(); +} + +size_t ShenandoahGlobalGeneration::available() const { + return ShenandoahHeap::heap()->free_set()->available(); +} + +size_t ShenandoahGlobalGeneration::soft_available() const { + size_t available = this->available(); + + // Make sure the code below treats available without the soft tail. + assert(max_capacity() >= soft_max_capacity(), "Max capacity must be greater than soft max capacity."); + size_t soft_tail = max_capacity() - soft_max_capacity(); + return (available > soft_tail) ? (available - soft_tail) : 0; +} + +void ShenandoahGlobalGeneration::set_concurrent_mark_in_progress(bool in_progress) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (in_progress && heap->mode()->is_generational()) { + // Global collection has preempted an old generation mark. This is fine + // because the global generation includes the old generation, but we + // want the global collect to start from a clean slate and we don't want + // any stale state in the old generation. + assert(!heap->is_concurrent_old_mark_in_progress(), "Old cycle should not be running."); + } + + heap->set_concurrent_young_mark_in_progress(in_progress); +} + +bool ShenandoahGlobalGeneration::contains(ShenandoahAffiliation affiliation) const { + return true; +} + +bool ShenandoahGlobalGeneration::contains(ShenandoahHeapRegion* region) const { + return true; +} + +void ShenandoahGlobalGeneration::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) { + ShenandoahHeap::heap()->parallel_heap_region_iterate(cl); +} + +void ShenandoahGlobalGeneration::heap_region_iterate(ShenandoahHeapRegionClosure* cl) { + ShenandoahHeap::heap()->heap_region_iterate(cl); +} + +bool ShenandoahGlobalGeneration::is_concurrent_mark_in_progress() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + return heap->is_concurrent_mark_in_progress(); +} + +ShenandoahHeuristics* ShenandoahGlobalGeneration::initialize_heuristics(ShenandoahMode* gc_mode) { + if (gc_mode->is_generational()) { + _heuristics = new ShenandoahGlobalHeuristics(this); + } else { + _heuristics = gc_mode->initialize_heuristics(this); + } + + _heuristics->set_guaranteed_gc_interval(ShenandoahGuaranteedGCInterval); + confirm_heuristics_mode(); + return _heuristics; +} + +void ShenandoahGlobalGeneration::set_mark_complete() { + ShenandoahGeneration::set_mark_complete(); + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + heap->young_generation()->set_mark_complete(); + heap->old_generation()->set_mark_complete(); + } +} + +void ShenandoahGlobalGeneration::set_mark_incomplete() { + ShenandoahGeneration::set_mark_incomplete(); + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + heap->young_generation()->set_mark_incomplete(); + heap->old_generation()->set_mark_incomplete(); + } +} + +void ShenandoahGlobalGeneration::prepare_gc() { + ShenandoahGeneration::prepare_gc(); + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + assert(type() == GLOBAL, "Unexpected generation type"); + // Clear any stale/partial local census data before the start of a + // new marking cycle + ShenandoahGenerationalHeap::heap()->age_census()->reset_local(); + } else { + assert(type() == NON_GEN, "Unexpected generation type"); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp new file mode 100644 index 00000000000..d51a77fdf8f --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP + +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" + +// A "generation" that represents the whole heap. +class ShenandoahGlobalGeneration : public ShenandoahGeneration { +public: + ShenandoahGlobalGeneration(bool generational, uint max_queues, size_t max_capacity, size_t soft_max_capacity) + : ShenandoahGeneration(generational ? GLOBAL : NON_GEN, max_queues, max_capacity, soft_max_capacity) { } + +public: + const char* name() const override; + + size_t max_capacity() const override; + size_t soft_max_capacity() const override; + size_t used_regions() const override; + size_t used_regions_size() const override; + size_t available() const override; + size_t soft_available() const override; + + void set_concurrent_mark_in_progress(bool in_progress) override; + + bool contains(ShenandoahAffiliation affiliation) const override; + bool contains(ShenandoahHeapRegion* region) const override; + + bool contains(oop obj) const override { + return ShenandoahHeap::heap()->is_in_reserved(obj); + } + + void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + + void heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + + bool is_concurrent_mark_in_progress() override; + + void set_mark_complete() override; + + void set_mark_incomplete() override; + + ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override; + + virtual void prepare_gc() override; +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 0f7139691a3..6ef66926b72 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -37,6 +38,9 @@ #include "gc/shared/plab.hpp" #include "gc/shared/tlab_globals.hpp" +#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" @@ -45,20 +49,25 @@ #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahControlThread.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahGlobalGeneration.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahInitLogger.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahMemoryPool.hpp" -#include "gc/shenandoah/shenandoahMetrics.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" @@ -66,8 +75,12 @@ #include "gc/shenandoah/shenandoahVMOperations.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/mode/shenandoahSATBMode.hpp" +#include "utilities/globalDefinitions.hpp" + #if INCLUDE_JFR #include "gc/shenandoah/shenandoahJfrSupport.hpp" #endif @@ -161,9 +174,6 @@ jint ShenandoahHeap::initialize() { "Regions should cover entire heap exactly: " SIZE_FORMAT " != " SIZE_FORMAT "/" SIZE_FORMAT, _num_regions, max_byte_size, reg_size_bytes); - // Now we know the number of regions, initialize the heuristics. - initialize_heuristics(); - size_t num_committed_regions = init_byte_size / reg_size_bytes; num_committed_regions = MIN2(num_committed_regions, _num_regions); assert(num_committed_regions <= _num_regions, "sanity"); @@ -217,6 +227,28 @@ jint ShenandoahHeap::initialize() { "Cannot commit heap memory"); } + BarrierSet::set_barrier_set(new ShenandoahBarrierSet(this, _heap_region)); + + // Now we know the number of regions and heap sizes, initialize the heuristics. + initialize_heuristics(); + + assert(_heap_region.byte_size() == heap_rs.size(), "Need to know reserved size for card table"); + + // + // Worker threads must be initialized after the barrier is configured + // + _workers = new ShenandoahWorkerThreads("Shenandoah GC Threads", _max_workers); + if (_workers == nullptr) { + vm_exit_during_initialization("Failed necessary allocation."); + } else { + _workers->initialize_workers(); + } + + if (ParallelGCThreads > 1) { + _safepoint_workers = new ShenandoahWorkerThreads("Safepoint Cleanup Thread", ParallelGCThreads); + _safepoint_workers->initialize_workers(); + } + // // Reserve and commit memory for bitmap(s) // @@ -257,14 +289,14 @@ jint ShenandoahHeap::initialize() { _bitmap_region_special = bitmap.special(); size_t bitmap_init_commit = _bitmap_bytes_per_slice * - align_up(num_committed_regions, _bitmap_regions_per_slice) / _bitmap_regions_per_slice; + align_up(num_committed_regions, _bitmap_regions_per_slice) / _bitmap_regions_per_slice; bitmap_init_commit = MIN2(_bitmap_size, bitmap_init_commit); if (!_bitmap_region_special) { os::commit_memory_or_exit((char *) _bitmap_region.start(), bitmap_init_commit, bitmap_page_size, false, "Cannot commit bitmap memory"); } - _marking_context = new ShenandoahMarkingContext(_heap_region, _bitmap_region, _num_regions, _max_workers); + _marking_context = new ShenandoahMarkingContext(_heap_region, _bitmap_region, _num_regions); if (ShenandoahVerify) { ReservedSpace verify_bitmap(_bitmap_size, bitmap_page_size); @@ -348,6 +380,7 @@ jint ShenandoahHeap::initialize() { } _regions = NEW_C_HEAP_ARRAY(ShenandoahHeapRegion*, _num_regions, mtGC); + _affiliations = NEW_C_HEAP_ARRAY(uint8_t, _num_regions, mtGC); _free_set = new ShenandoahFreeSet(this, _num_regions); { @@ -364,12 +397,18 @@ jint ShenandoahHeap::initialize() { _marking_context->initialize_top_at_mark_start(r); _regions[i] = r; assert(!collection_set()->is_in(i), "New region should not be in collection set"); + + _affiliations[i] = ShenandoahAffiliation::FREE; } // Initialize to complete _marking_context->mark_complete(); + size_t young_cset_regions, old_cset_regions; - _free_set->rebuild(); + // We are initializing free set. We ignore cset region tallies. + size_t first_old, last_old, num_old; + _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + _free_set->finish_rebuild(young_cset_regions, old_cset_regions, num_old); } if (AlwaysPreTouch) { @@ -418,21 +457,31 @@ jint ShenandoahHeap::initialize() { _pacer->setup_for_idle(); } - _control_thread = new ShenandoahControlThread(); + initialize_controller(); - ShenandoahInitLogger::print(); + print_init_logger(); FullGCForwarding::initialize(_heap_region); return JNI_OK; } +void ShenandoahHeap::initialize_controller() { + _control_thread = new ShenandoahControlThread(); +} + +void ShenandoahHeap::print_init_logger() const { + ShenandoahInitLogger::print(); +} + void ShenandoahHeap::initialize_mode() { if (ShenandoahGCMode != nullptr) { if (strcmp(ShenandoahGCMode, "satb") == 0) { _gc_mode = new ShenandoahSATBMode(); } else if (strcmp(ShenandoahGCMode, "passive") == 0) { _gc_mode = new ShenandoahPassiveMode(); + } else if (strcmp(ShenandoahGCMode, "generational") == 0) { + _gc_mode = new ShenandoahGenerationalMode(); } else { vm_exit_during_initialization("Unknown -XX:ShenandoahGCMode option"); } @@ -453,19 +502,8 @@ void ShenandoahHeap::initialize_mode() { } void ShenandoahHeap::initialize_heuristics() { - assert(_gc_mode != nullptr, "Must be initialized"); - _heuristics = _gc_mode->initialize_heuristics(); - - if (_heuristics->is_diagnostic() && !UnlockDiagnosticVMOptions) { - vm_exit_during_initialization( - err_msg("Heuristics \"%s\" is diagnostic, and must be enabled via -XX:+UnlockDiagnosticVMOptions.", - _heuristics->name())); - } - if (_heuristics->is_experimental() && !UnlockExperimentalVMOptions) { - vm_exit_during_initialization( - err_msg("Heuristics \"%s\" is experimental, and must be enabled via -XX:+UnlockExperimentalVMOptions.", - _heuristics->name())); - } + _global_generation = new ShenandoahGlobalGeneration(mode()->is_generational(), max_workers(), max_capacity(), max_capacity()); + _global_generation->initialize_heuristics(mode()); } #ifdef _MSC_VER @@ -475,34 +513,38 @@ void ShenandoahHeap::initialize_heuristics() { ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : CollectedHeap(), + _gc_generation(nullptr), + _active_generation(nullptr), _initial_size(0), - _used(0), _committed(0), - _bytes_allocated_since_gc_start(0), - _max_workers(MAX2(ConcGCThreads, ParallelGCThreads)), + _max_workers(MAX3(ConcGCThreads, ParallelGCThreads, 1U)), _workers(nullptr), _safepoint_workers(nullptr), _heap_region_special(false), _num_regions(0), _regions(nullptr), - _update_refs_iterator(this), + _affiliations(nullptr), _gc_state_changed(false), _gc_no_progress_count(0), + _cancel_requested_time(0), + _update_refs_iterator(this), + _global_generation(nullptr), _control_thread(nullptr), + _young_generation(nullptr), + _old_generation(nullptr), _shenandoah_policy(policy), _gc_mode(nullptr), - _heuristics(nullptr), _free_set(nullptr), _pacer(nullptr), _verifier(nullptr), _phase_timings(nullptr), + _mmu_tracker(), _monitoring_support(nullptr), _memory_pool(nullptr), _stw_memory_manager("Shenandoah Pauses"), _cycle_memory_manager("Shenandoah Cycles"), _gc_timer(new ConcurrentGCTimer()), _log_min_obj_alignment_in_bytes(LogMinObjAlignmentInBytes), - _ref_processor(new ShenandoahReferenceProcessor(MAX2(_max_workers, 1U))), _marking_context(nullptr), _bitmap_size(0), _bitmap_regions_per_slice(0), @@ -512,58 +554,14 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _liveness_cache(nullptr), _collection_set(nullptr) { - // Initialize GC mode early, so we can adjust barrier support + // Initialize GC mode early, many subsequent initialization procedures depend on it initialize_mode(); - BarrierSet::set_barrier_set(new ShenandoahBarrierSet(this)); - - _max_workers = MAX2(_max_workers, 1U); - _workers = new ShenandoahWorkerThreads("Shenandoah GC Threads", _max_workers); - if (_workers == nullptr) { - vm_exit_during_initialization("Failed necessary allocation."); - } else { - _workers->initialize_workers(); - } - - if (ParallelGCThreads > 1) { - _safepoint_workers = new ShenandoahWorkerThreads("Safepoint Cleanup Thread", - ParallelGCThreads); - _safepoint_workers->initialize_workers(); - } } #ifdef _MSC_VER #pragma warning( pop ) #endif -class ShenandoahResetBitmapTask : public WorkerTask { -private: - ShenandoahRegionIterator _regions; - -public: - ShenandoahResetBitmapTask() : - WorkerTask("Shenandoah Reset Bitmap") {} - - void work(uint worker_id) { - ShenandoahHeapRegion* region = _regions.next(); - ShenandoahHeap* heap = ShenandoahHeap::heap(); - ShenandoahMarkingContext* const ctx = heap->marking_context(); - while (region != nullptr) { - if (heap->is_bitmap_slice_committed(region)) { - ctx->clear_bitmap(region); - } - region = _regions.next(); - } - } -}; - -void ShenandoahHeap::reset_mark_bitmap() { - assert_gc_workers(_workers->active_workers()); - mark_incomplete_marking_context(); - - ShenandoahResetBitmapTask task; - _workers->run_task(&task); -} - void ShenandoahHeap::print_on(outputStream* st) const { st->print_cr("Shenandoah Heap"); st->print_cr(" " SIZE_FORMAT "%s max, " SIZE_FORMAT "%s soft max, " SIZE_FORMAT "%s committed, " SIZE_FORMAT "%s used", @@ -578,7 +576,12 @@ void ShenandoahHeap::print_on(outputStream* st) const { st->print("Status: "); if (has_forwarded_objects()) st->print("has forwarded objects, "); - if (is_concurrent_mark_in_progress()) st->print("marking, "); + if (!mode()->is_generational()) { + if (is_concurrent_mark_in_progress()) st->print("marking,"); + } else { + if (is_concurrent_old_mark_in_progress()) st->print("old marking, "); + if (is_concurrent_young_mark_in_progress()) st->print("young marking, "); + } if (is_evacuation_in_progress()) st->print("evacuating, "); if (is_update_refs_in_progress()) st->print("updating refs, "); if (is_degenerated_gc_in_progress()) st->print("degenerated gc, "); @@ -629,6 +632,8 @@ class ShenandoahInitWorkerGCLABClosure : public ThreadClosure { void ShenandoahHeap::post_initialize() { CollectedHeap::post_initialize(); + _mmu_tracker.initialize(); + MutexLocker ml(Threads_lock); ShenandoahInitWorkerGCLABClosure init_gclabs; @@ -642,23 +647,21 @@ void ShenandoahHeap::post_initialize() { _safepoint_workers->set_initialize_gclab(); } - _heuristics->initialize(); - JFR_ONLY(ShenandoahJFRSupport::register_jfr_type_serializers();) } +ShenandoahHeuristics* ShenandoahHeap::heuristics() { + return _global_generation->heuristics(); +} + size_t ShenandoahHeap::used() const { - return Atomic::load(&_used); + return global_generation()->used(); } size_t ShenandoahHeap::committed() const { return Atomic::load(&_committed); } -size_t ShenandoahHeap::available() const { - return free_set()->available(); -} - void ShenandoahHeap::increase_committed(size_t bytes) { shenandoah_assert_heaplocked_or_safepoint(); _committed += bytes; @@ -669,33 +672,84 @@ void ShenandoahHeap::decrease_committed(size_t bytes) { _committed -= bytes; } -void ShenandoahHeap::increase_used(size_t bytes) { - Atomic::add(&_used, bytes, memory_order_relaxed); +// For tracking usage based on allocations, it should be the case that: +// * The sum of regions::used == heap::used +// * The sum of a generation's regions::used == generation::used +// * The sum of a generation's humongous regions::free == generation::humongous_waste +// These invariants are checked by the verifier on GC safepoints. +// +// Additional notes: +// * When a mutator's allocation request causes a region to be retired, the +// free memory left in that region is considered waste. It does not contribute +// to the usage, but it _does_ contribute to allocation rate. +// * The bottom of a PLAB must be aligned on card size. In some cases this will +// require padding in front of the PLAB (a filler object). Because this padding +// is included in the region's used memory we include the padding in the usage +// accounting as waste. +// * Mutator allocations are used to compute an allocation rate. They are also +// sent to the Pacer for those purposes. +// * There are three sources of waste: +// 1. The padding used to align a PLAB on card size +// 2. Region's free is less than minimum TLAB size and is retired +// 3. The unused portion of memory in the last region of a humongous object +void ShenandoahHeap::increase_used(const ShenandoahAllocRequest& req) { + size_t actual_bytes = req.actual_size() * HeapWordSize; + size_t wasted_bytes = req.waste() * HeapWordSize; + ShenandoahGeneration* generation = generation_for(req.affiliation()); + + if (req.is_gc_alloc()) { + assert(wasted_bytes == 0 || req.type() == ShenandoahAllocRequest::_alloc_plab, "Only PLABs have waste"); + increase_used(generation, actual_bytes + wasted_bytes); + } else { + assert(req.is_mutator_alloc(), "Expected mutator alloc here"); + // padding and actual size both count towards allocation counter + generation->increase_allocated(actual_bytes + wasted_bytes); + + // only actual size counts toward usage for mutator allocations + increase_used(generation, actual_bytes); + + // notify pacer of both actual size and waste + notify_mutator_alloc_words(req.actual_size(), req.waste()); + + if (wasted_bytes > 0 && ShenandoahHeapRegion::requires_humongous(req.actual_size())) { + increase_humongous_waste(generation,wasted_bytes); + } + } } -void ShenandoahHeap::set_used(size_t bytes) { - Atomic::store(&_used, bytes); +void ShenandoahHeap::increase_humongous_waste(ShenandoahGeneration* generation, size_t bytes) { + generation->increase_humongous_waste(bytes); + if (!generation->is_global()) { + global_generation()->increase_humongous_waste(bytes); + } } -void ShenandoahHeap::decrease_used(size_t bytes) { - assert(used() >= bytes, "never decrease heap size by more than we've left"); - Atomic::sub(&_used, bytes, memory_order_relaxed); +void ShenandoahHeap::decrease_humongous_waste(ShenandoahGeneration* generation, size_t bytes) { + generation->decrease_humongous_waste(bytes); + if (!generation->is_global()) { + global_generation()->decrease_humongous_waste(bytes); + } } -void ShenandoahHeap::increase_allocated(size_t bytes) { - Atomic::add(&_bytes_allocated_since_gc_start, bytes, memory_order_relaxed); +void ShenandoahHeap::increase_used(ShenandoahGeneration* generation, size_t bytes) { + generation->increase_used(bytes); + if (!generation->is_global()) { + global_generation()->increase_used(bytes); + } } -void ShenandoahHeap::notify_mutator_alloc_words(size_t words, bool waste) { - size_t bytes = words * HeapWordSize; - if (!waste) { - increase_used(bytes); +void ShenandoahHeap::decrease_used(ShenandoahGeneration* generation, size_t bytes) { + generation->decrease_used(bytes); + if (!generation->is_global()) { + global_generation()->decrease_used(bytes); } - increase_allocated(bytes); +} + +void ShenandoahHeap::notify_mutator_alloc_words(size_t words, size_t waste) { if (ShenandoahPacing) { control_thread()->pacing_notify_alloc(words); - if (waste) { - pacer()->claim_for_alloc(words); + if (waste > 0) { + pacer()->claim_for_alloc(waste); } } } @@ -825,8 +879,6 @@ void ShenandoahHeap::notify_heap_changed() { // Update monitoring counters when we took a new region. This amortizes the // update costs on slow path. monitoring_support()->notify_heap_changed(); - - // This is called from allocation path, and thus should be fast. _heap_changed.try_set(); } @@ -844,17 +896,20 @@ HeapWord* ShenandoahHeap::allocate_from_gclab_slow(Thread* thread, size_t size) // Figure out size of new GCLAB, looking back at heuristics. Expand aggressively. size_t new_size = ShenandoahThreadLocalData::gclab_size(thread) * 2; + new_size = MIN2(new_size, PLAB::max_size()); new_size = MAX2(new_size, PLAB::min_size()); // Record new heuristic value even if we take any shortcut. This captures // the case when moderately-sized objects always take a shortcut. At some point, // heuristics should catch up with them. + log_debug(gc, free)("Set new GCLAB size: " SIZE_FORMAT, new_size); ShenandoahThreadLocalData::set_gclab_size(thread, new_size); if (new_size < size) { // New size still does not fit the object. Fall back to shared allocation. // This avoids retiring perfectly good GCLABs, when we encounter a large object. + log_debug(gc, free)("New gclab size (" SIZE_FORMAT ") is too small for " SIZE_FORMAT, new_size, size); return nullptr; } @@ -884,6 +939,7 @@ HeapWord* ShenandoahHeap::allocate_from_gclab_slow(Thread* thread, size_t size) return gclab->allocate(size); } +// Called from stubs in JIT code or interpreter HeapWord* ShenandoahHeap::allocate_new_tlab(size_t min_size, size_t requested_size, size_t* actual_size) { @@ -932,8 +988,10 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { // is testing that the GC overhead limit has not been exceeded. // This will notify the collector to start a cycle, but will raise // an OOME to the mutator if the last Full GCs have not made progress. + // gc_no_progress_count is incremented following each degen or full GC that fails to achieve is_good_progress(). if (result == nullptr && !req.is_lab_alloc() && get_gc_no_progress_count() > ShenandoahNoProgressThreshold) { control_thread()->handle_alloc_failure(req, false); + req.set_actual_size(0); return nullptr; } @@ -948,8 +1006,6 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { // Stop retrying and return nullptr to cause OOMError exception if our allocation failed even after: // a) We experienced a GC that had good progress, or // b) We experienced at least one Full GC (whether or not it had good progress) - // - // TODO: Consider GLOBAL GC rather than Full GC to remediate OOM condition: https://bugs.openjdk.org/browse/JDK-8335910 size_t original_count = shenandoah_policy()->full_gc_count(); while ((result == nullptr) && (original_count == shenandoah_policy()->full_gc_count())) { @@ -960,7 +1016,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { // If our allocation request has been satisifed after it initially failed, we count this as good gc progress notify_gc_progress(); } - if (log_is_enabled(Debug, gc, alloc)) { + if (log_develop_is_enabled(Debug, gc, alloc)) { ResourceMark rm; log_debug(gc, alloc)("Thread: %s, Result: " PTR_FORMAT ", Request: %s, Size: " SIZE_FORMAT ", Original: " SIZE_FORMAT ", Latest: " SIZE_FORMAT, @@ -979,6 +1035,14 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { notify_heap_changed(); } + if (result == nullptr) { + req.set_actual_size(0); + } + + // This is called regardless of the outcome of the allocation to account + // for any waste created by retiring regions with this request. + increase_used(req); + if (result != nullptr) { size_t requested = req.size(); size_t actual = req.actual_size(); @@ -988,16 +1052,12 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) { ShenandoahAllocRequest::alloc_type_to_string(req.type()), requested, actual); if (req.is_mutator_alloc()) { - notify_mutator_alloc_words(actual, false); - // If we requested more than we were granted, give the rest back to pacer. // This only matters if we are in the same pacing epoch: do not try to unpace // over the budget for the other phase. if (ShenandoahPacing && (pacer_epoch > 0) && (requested > actual)) { pacer()->unpace_for_alloc(pacer_epoch, requested - actual); } - } else { - increase_used(actual*HeapWordSize); } } @@ -1010,7 +1070,43 @@ HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req // we are already running at safepoint or from stack watermark machinery, and we cannot // block again. ShenandoahHeapLocker locker(lock(), req.is_mutator_alloc()); - return _free_set->allocate(req, in_new_region); + + // Make sure the old generation has room for either evacuations or promotions before trying to allocate. + if (req.is_old() && !old_generation()->can_allocate(req)) { + return nullptr; + } + + // If TLAB request size is greater than available, allocate() will attempt to downsize request to fit within available + // memory. + HeapWord* result = _free_set->allocate(req, in_new_region); + + // Record the plab configuration for this result and register the object. + if (result != nullptr && req.is_old()) { + old_generation()->configure_plab_for_current_thread(req); + if (req.type() == ShenandoahAllocRequest::_alloc_shared_gc) { + // Register the newly allocated object while we're holding the global lock since there's no synchronization + // built in to the implementation of register_object(). There are potential races when multiple independent + // threads are allocating objects, some of which might span the same card region. For example, consider + // a card table's memory region within which three objects are being allocated by three different threads: + // + // objects being "concurrently" allocated: + // [-----a------][-----b-----][--------------c------------------] + // [---- card table memory range --------------] + // + // Before any objects are allocated, this card's memory range holds no objects. Note that allocation of object a + // wants to set the starts-object, first-start, and last-start attributes of the preceding card region. + // Allocation of object b wants to set the starts-object, first-start, and last-start attributes of this card region. + // Allocation of object c also wants to set the starts-object, first-start, and last-start attributes of this + // card region. + // + // The thread allocating b and the thread allocating c can "race" in various ways, resulting in confusion, such as + // last-start representing object b while first-start represents object c. This is why we need to require all + // register_object() invocations to be "mutually exclusive" with respect to each card's memory range. + old_generation()->card_scan()->register_object(result); + } + } + + return result; } HeapWord* ShenandoahHeap::mem_allocate(size_t size, @@ -1025,8 +1121,8 @@ MetaWord* ShenandoahHeap::satisfy_failed_metadata_allocation(ClassLoaderData* lo MetaWord* result; // Inform metaspace OOM to GC heuristics if class unloading is possible. - if (heuristics()->can_unload_classes()) { - ShenandoahHeuristics* h = heuristics(); + ShenandoahHeuristics* h = global_generation()->heuristics(); + if (h->can_unload_classes()) { h->record_metaspace_oom(); } @@ -1124,20 +1220,29 @@ void ShenandoahHeap::evacuate_collection_set(bool concurrent) { } oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { - if (ShenandoahThreadLocalData::is_oom_during_evac(Thread::current())) { - // This thread went through the OOM during evac protocol and it is safe to return - // the forward pointer. It must not attempt to evacuate any more. + assert(thread == Thread::current(), "Expected thread parameter to be current thread."); + if (ShenandoahThreadLocalData::is_oom_during_evac(thread)) { + // This thread went through the OOM during evac protocol. It is safe to return + // the forward pointer. It must not attempt to evacuate any other objects. return ShenandoahBarrierSet::resolve_forwarded(p); } assert(ShenandoahThreadLocalData::is_evac_allowed(thread), "must be enclosed in oom-evac scope"); - size_t size = ShenandoahForwarding::size(p); + ShenandoahHeapRegion* r = heap_region_containing(p); + assert(!r->is_humongous(), "never evacuate humongous objects"); - assert(!heap_region_containing(p)->is_humongous(), "never evacuate humongous objects"); + ShenandoahAffiliation target_gen = r->affiliation(); + return try_evacuate_object(p, thread, r, target_gen); +} - bool alloc_from_gclab = true; +oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapRegion* from_region, + ShenandoahAffiliation target_gen) { + assert(target_gen == YOUNG_GENERATION, "Only expect evacuations to young in this mode"); + assert(from_region->is_young(), "Only expect evacuations from young in this mode"); + bool alloc_from_lab = true; HeapWord* copy = nullptr; + size_t size = ShenandoahForwarding::size(p); #ifdef ASSERT if (ShenandoahOOMDuringEvacALot && @@ -1149,9 +1254,10 @@ oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { copy = allocate_from_gclab(thread, size); } if (copy == nullptr) { - ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(size); + // If we failed to allocate in LAB, we'll try a shared allocation. + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(size, target_gen); copy = allocate_memory(req); - alloc_from_gclab = false; + alloc_from_lab = false; } #ifdef ASSERT } @@ -1182,17 +1288,19 @@ oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { // But if it happens to contain references to evacuated regions, those references would // not get updated for this stale copy during this cycle, and we will crash while scanning // it the next cycle. - // - // For GCLAB allocations, it is enough to rollback the allocation ptr. Either the next - // object will overwrite this stale copy, or the filler object on LAB retirement will - // do this. For non-GCLAB allocations, we have no way to retract the allocation, and - // have to explicitly overwrite the copy with the filler object. With that overwrite, - // we have to keep the fwdptr initialized and pointing to our (stale) copy. - if (alloc_from_gclab) { + if (alloc_from_lab) { + // For LAB allocations, it is enough to rollback the allocation ptr. Either the next + // object will overwrite this stale copy, or the filler object on LAB retirement will + // do this. ShenandoahThreadLocalData::gclab(thread)->undo_allocation(copy, size); } else { + // For non-LAB allocations, we have no way to retract the allocation, and + // have to explicitly overwrite the copy with the filler object. With that overwrite, + // we have to keep the fwdptr initialized and pointing to our (stale) copy. + assert(size >= ShenandoahHeap::min_fill_size(), "previously allocated object known to be larger than min_size"); fill_with_object(copy, size); shenandoah_assert_correct(nullptr, copy_val); + // For non-LAB allocations, the object has already been registered } shenandoah_assert_correct(nullptr, result); return result; @@ -1226,7 +1334,7 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const { } } -void ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) { +size_t ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) { assert(start->is_humongous_start(), "reclaim regions starting with the first one"); oop humongous_obj = cast_to_oop(start->bottom()); @@ -1246,6 +1354,7 @@ void ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) { region->make_trash_immediate(); } + return required_regions; } class ShenandoahCheckCleanGCLABClosure : public ThreadClosure { @@ -1255,6 +1364,12 @@ class ShenandoahCheckCleanGCLABClosure : public ThreadClosure { PLAB* gclab = ShenandoahThreadLocalData::gclab(thread); assert(gclab != nullptr, "GCLAB should be initialized for %s", thread->name()); assert(gclab->words_remaining() == 0, "GCLAB should not need retirement"); + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + PLAB* plab = ShenandoahThreadLocalData::plab(thread); + assert(plab != nullptr, "PLAB should be initialized for %s", thread->name()); + assert(plab->words_remaining() == 0, "PLAB should not need retirement"); + } } }; @@ -1270,6 +1385,19 @@ class ShenandoahRetireGCLABClosure : public ThreadClosure { if (_resize && ShenandoahThreadLocalData::gclab_size(thread) > 0) { ShenandoahThreadLocalData::set_gclab_size(thread, 0); } + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + PLAB* plab = ShenandoahThreadLocalData::plab(thread); + assert(plab != nullptr, "PLAB should be initialized for %s", thread->name()); + + // There are two reasons to retire all plabs between old-gen evacuation passes. + // 1. We need to make the plab memory parsable by remembered-set scanning. + // 2. We need to establish a trustworthy UpdateWaterMark value within each old-gen heap region + ShenandoahGenerationalHeap::heap()->retire_plab(plab, thread); + if (_resize && ShenandoahThreadLocalData::plab_size(thread) > 0) { + ShenandoahThreadLocalData::set_plab_size(thread, 0); + } + } } }; @@ -1403,6 +1531,46 @@ void ShenandoahHeap::print_tracing_info() const { } } +void ShenandoahHeap::set_gc_generation(ShenandoahGeneration* generation) { + shenandoah_assert_control_or_vm_thread_at_safepoint(); + _gc_generation = generation; +} + +// Active generation may only be set by the VM thread at a safepoint. +void ShenandoahHeap::set_active_generation() { + assert(Thread::current()->is_VM_thread(), "Only the VM Thread"); + assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint!"); + assert(_gc_generation != nullptr, "Will set _active_generation to nullptr"); + _active_generation = _gc_generation; +} + +void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation) { + shenandoah_policy()->record_collection_cause(cause); + + assert(gc_cause() == GCCause::_no_gc, "Over-writing cause"); + assert(_gc_generation == nullptr, "Over-writing _gc_generation"); + + set_gc_cause(cause); + set_gc_generation(generation); + + generation->heuristics()->record_cycle_start(); +} + +void ShenandoahHeap::on_cycle_end(ShenandoahGeneration* generation) { + assert(gc_cause() != GCCause::_no_gc, "cause wasn't set"); + assert(_gc_generation != nullptr, "_gc_generation wasn't set"); + + generation->heuristics()->record_cycle_end(); + if (mode()->is_generational() && generation->is_global()) { + // If we just completed a GLOBAL GC, claim credit for completion of young-gen and old-gen GC as well + young_generation()->heuristics()->record_cycle_end(); + old_generation()->heuristics()->record_cycle_end(); + } + + set_gc_generation(nullptr); + set_gc_cause(GCCause::_no_gc); +} + void ShenandoahHeap::verify(VerifyOption vo) { if (ShenandoahSafepoint::is_at_shenandoah_safepoint()) { if (ShenandoahVerify) { @@ -1752,107 +1920,11 @@ void ShenandoahHeap::recycle_trash() { free_set()->recycle_trash(); } -class ShenandoahResetUpdateRegionStateClosure : public ShenandoahHeapRegionClosure { -private: - ShenandoahMarkingContext* const _ctx; -public: - ShenandoahResetUpdateRegionStateClosure() : _ctx(ShenandoahHeap::heap()->marking_context()) {} - - void heap_region_do(ShenandoahHeapRegion* r) { - if (r->is_active()) { - // Reset live data and set TAMS optimistically. We would recheck these under the pause - // anyway to capture any updates that happened since now. - r->clear_live_data(); - _ctx->capture_top_at_mark_start(r); - } - } - - bool is_thread_safe() { return true; } -}; - -void ShenandoahHeap::prepare_gc() { - reset_mark_bitmap(); - - ShenandoahResetUpdateRegionStateClosure cl; - parallel_heap_region_iterate(&cl); -} - -class ShenandoahFinalMarkUpdateRegionStateClosure : public ShenandoahHeapRegionClosure { -private: - ShenandoahMarkingContext* const _ctx; - ShenandoahHeapLock* const _lock; - -public: - ShenandoahFinalMarkUpdateRegionStateClosure() : - _ctx(ShenandoahHeap::heap()->complete_marking_context()), _lock(ShenandoahHeap::heap()->lock()) {} - - void heap_region_do(ShenandoahHeapRegion* r) { - if (r->is_active()) { - // All allocations past TAMS are implicitly live, adjust the region data. - // Bitmaps/TAMS are swapped at this point, so we need to poll complete bitmap. - HeapWord *tams = _ctx->top_at_mark_start(r); - HeapWord *top = r->top(); - if (top > tams) { - r->increase_live_data_alloc_words(pointer_delta(top, tams)); - } - - // We are about to select the collection set, make sure it knows about - // current pinning status. Also, this allows trashing more regions that - // now have their pinning status dropped. - if (r->is_pinned()) { - if (r->pin_count() == 0) { - ShenandoahHeapLocker locker(_lock); - r->make_unpinned(); - } - } else { - if (r->pin_count() > 0) { - ShenandoahHeapLocker locker(_lock); - r->make_pinned(); - } - } - - // Remember limit for updating refs. It's guaranteed that we get no - // from-space-refs written from here on. - r->set_update_watermark_at_safepoint(r->top()); - } else { - assert(!r->has_live(), "Region " SIZE_FORMAT " should have no live data", r->index()); - assert(_ctx->top_at_mark_start(r) == r->top(), - "Region " SIZE_FORMAT " should have correct TAMS", r->index()); - } - } - - bool is_thread_safe() { return true; } -}; - -void ShenandoahHeap::prepare_regions_and_collection_set(bool concurrent) { - assert(!is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); - { - ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_update_region_states : - ShenandoahPhaseTimings::degen_gc_final_update_region_states); - ShenandoahFinalMarkUpdateRegionStateClosure cl; - parallel_heap_region_iterate(&cl); - - assert_pinned_region_status(); - } - - { - ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::choose_cset : - ShenandoahPhaseTimings::degen_gc_choose_cset); - ShenandoahHeapLocker locker(lock()); - _collection_set->clear(); - heuristics()->choose_collection_set(_collection_set); - } - - { - ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : - ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); - ShenandoahHeapLocker locker(lock()); - _free_set->rebuild(); - } -} - void ShenandoahHeap::do_class_unloading() { _unloader.unload(); + if (mode()->is_generational()) { + old_generation()->set_parsable(false); + } } void ShenandoahHeap::stw_weak_refs(bool full_gc) { @@ -1861,7 +1933,8 @@ void ShenandoahHeap::stw_weak_refs(bool full_gc) { : ShenandoahPhaseTimings::degen_gc_weakrefs; ShenandoahTimingsTracker t(phase); ShenandoahGCWorkerPhase worker_phase(phase); - ref_processor()->process_references(phase, workers(), false /* concurrent */); + shenandoah_assert_generations_reconciled(); + gc_generation()->ref_processor()->process_references(phase, workers(), false /* concurrent */); } void ShenandoahHeap::prepare_update_heap_references(bool concurrent) { @@ -1897,10 +1970,58 @@ void ShenandoahHeap::set_gc_state(uint mask, bool value) { _gc_state_changed = true; } -void ShenandoahHeap::set_concurrent_mark_in_progress(bool in_progress) { - assert(!has_forwarded_objects(), "Not expected before/after mark phase"); - set_gc_state(MARKING, in_progress); - ShenandoahBarrierSet::satb_mark_queue_set().set_active_all_threads(in_progress, !in_progress); +void ShenandoahHeap::set_concurrent_young_mark_in_progress(bool in_progress) { + uint mask; + assert(!has_forwarded_objects(), "Young marking is not concurrent with evacuation"); + if (!in_progress && is_concurrent_old_mark_in_progress()) { + assert(mode()->is_generational(), "Only generational GC has old marking"); + assert(_gc_state.is_set(MARKING), "concurrent_old_marking_in_progress implies MARKING"); + // If old-marking is in progress when we turn off YOUNG_MARKING, leave MARKING (and OLD_MARKING) on + mask = YOUNG_MARKING; + } else { + mask = MARKING | YOUNG_MARKING; + } + set_gc_state(mask, in_progress); + manage_satb_barrier(in_progress); +} + +void ShenandoahHeap::set_concurrent_old_mark_in_progress(bool in_progress) { +#ifdef ASSERT + // has_forwarded_objects() iff UPDATEREFS or EVACUATION + bool has_forwarded = has_forwarded_objects(); + bool updating_or_evacuating = _gc_state.is_set(UPDATEREFS | EVACUATION); + bool evacuating = _gc_state.is_set(EVACUATION); + assert ((has_forwarded == updating_or_evacuating) || (evacuating && !has_forwarded && collection_set()->is_empty()), + "Updating or evacuating iff has forwarded objects, or if evacuation phase is promoting in place without forwarding"); +#endif + if (!in_progress && is_concurrent_young_mark_in_progress()) { + // If young-marking is in progress when we turn off OLD_MARKING, leave MARKING (and YOUNG_MARKING) on + assert(_gc_state.is_set(MARKING), "concurrent_young_marking_in_progress implies MARKING"); + set_gc_state(OLD_MARKING, in_progress); + } else { + set_gc_state(MARKING | OLD_MARKING, in_progress); + } + manage_satb_barrier(in_progress); +} + +bool ShenandoahHeap::is_prepare_for_old_mark_in_progress() const { + return old_generation()->is_preparing_for_mark(); +} + +void ShenandoahHeap::manage_satb_barrier(bool active) { + if (is_concurrent_mark_in_progress()) { + // Ignore request to deactivate barrier while concurrent mark is in progress. + // Do not attempt to re-activate the barrier if it is already active. + if (active && !ShenandoahBarrierSet::satb_mark_queue_set().is_active()) { + ShenandoahBarrierSet::satb_mark_queue_set().set_active_all_threads(active, !active); + } + } else { + // No concurrent marking is in progress so honor request to deactivate, + // but only if the barrier is already active. + if (!active && ShenandoahBarrierSet::satb_mark_queue_set().is_active()) { + ShenandoahBarrierSet::satb_mark_queue_set().set_active_all_threads(active, !active); + } + } } void ShenandoahHeap::set_evacuation_in_progress(bool in_progress) { @@ -1933,11 +2054,23 @@ bool ShenandoahHeap::try_cancel_gc() { return prev == CANCELLABLE; } +void ShenandoahHeap::cancel_concurrent_mark() { + if (mode()->is_generational()) { + young_generation()->cancel_marking(); + old_generation()->cancel_marking(); + } + + global_generation()->cancel_marking(); + + ShenandoahBarrierSet::satb_mark_queue_set().abandon_partial_marking(); +} + void ShenandoahHeap::cancel_gc(GCCause::Cause cause) { if (try_cancel_gc()) { FormatBuffer<> msg("Cancelling GC: %s", GCCause::to_string(cause)); log_info(gc)("%s", msg.buffer()); Events::log(Thread::current(), "%s", msg.buffer()); + _cancel_requested_time = os::elapsedTime(); } } @@ -2061,12 +2194,13 @@ address ShenandoahHeap::in_cset_fast_test_addr() { return (address) heap->collection_set()->biased_map_address(); } -size_t ShenandoahHeap::bytes_allocated_since_gc_start() const { - return Atomic::load(&_bytes_allocated_since_gc_start); -} - void ShenandoahHeap::reset_bytes_allocated_since_gc_start() { - Atomic::store(&_bytes_allocated_since_gc_start, (size_t)0); + if (mode()->is_generational()) { + young_generation()->reset_bytes_allocated_since_gc_start(); + old_generation()->reset_bytes_allocated_since_gc_start(); + } + + global_generation()->reset_bytes_allocated_since_gc_start(); } void ShenandoahHeap::set_degenerated_gc_in_progress(bool in_progress) { @@ -2130,8 +2264,11 @@ void ShenandoahHeap::sync_pinned_region_status() { void ShenandoahHeap::assert_pinned_region_status() { for (size_t i = 0; i < num_regions(); i++) { ShenandoahHeapRegion* r = get_region(i); - assert((r->is_pinned() && r->pin_count() > 0) || (!r->is_pinned() && r->pin_count() == 0), - "Region " SIZE_FORMAT " pinning status is inconsistent", i); + shenandoah_assert_generations_reconciled(); + if (gc_generation()->contains(r)) { + assert((r->is_pinned() && r->pin_count() > 0) || (!r->is_pinned() && r->pin_count() == 0), + "Region " SIZE_FORMAT " pinning status is inconsistent", i); + } } } #endif @@ -2186,7 +2323,7 @@ class ShenandoahUpdateHeapRefsTask : public WorkerTask { ShenandoahHeap* _heap; ShenandoahRegionIterator* _regions; public: - ShenandoahUpdateHeapRefsTask(ShenandoahRegionIterator* regions) : + explicit ShenandoahUpdateHeapRefsTask(ShenandoahRegionIterator* regions) : WorkerTask("Shenandoah Update References"), _heap(ShenandoahHeap::heap()), _regions(regions) { @@ -2206,27 +2343,28 @@ class ShenandoahUpdateHeapRefsTask : public WorkerTask { private: template void do_work(uint worker_id) { - T cl; if (CONCURRENT && (worker_id == 0)) { // We ask the first worker to replenish the Mutator free set by moving regions previously reserved to hold the // results of evacuation. These reserves are no longer necessary because evacuation has completed. size_t cset_regions = _heap->collection_set()->count(); - // We cannot transfer any more regions than will be reclaimed when the existing collection set is recycled because - // we need the reclaimed collection set regions to replenish the collector reserves + + // Now that evacuation is done, we can reassign any regions that had been reserved to hold the results of evacuation + // to the mutator free set. At the end of GC, we will have cset_regions newly evacuated fully empty regions from + // which we will be able to replenish the Collector free set and the OldCollector free set in preparation for the + // next GC cycle. _heap->free_set()->move_regions_from_collector_to_mutator(cset_regions); } // If !CONCURRENT, there's no value in expanding Mutator free set - + T cl; ShenandoahHeapRegion* r = _regions->next(); - ShenandoahMarkingContext* const ctx = _heap->complete_marking_context(); while (r != nullptr) { HeapWord* update_watermark = r->get_update_watermark(); assert (update_watermark >= r->bottom(), "sanity"); if (r->is_active() && !r->is_cset()) { _heap->marked_object_oop_iterate(r, &cl, update_watermark); - } - if (ShenandoahPacing) { - _heap->pacer()->report_updaterefs(pointer_delta(update_watermark, r->bottom())); + if (ShenandoahPacing) { + _heap->pacer()->report_updaterefs(pointer_delta(update_watermark, r->bottom())); + } } if (_heap->check_cancelled_gc_and_yield(CONCURRENT)) { return; @@ -2248,36 +2386,6 @@ void ShenandoahHeap::update_heap_references(bool concurrent) { } } - -class ShenandoahFinalUpdateRefsUpdateRegionStateClosure : public ShenandoahHeapRegionClosure { -private: - ShenandoahHeapLock* const _lock; - -public: - ShenandoahFinalUpdateRefsUpdateRegionStateClosure() : _lock(ShenandoahHeap::heap()->lock()) {} - - void heap_region_do(ShenandoahHeapRegion* r) { - // Drop unnecessary "pinned" state from regions that does not have CP marks - // anymore, as this would allow trashing them. - - if (r->is_active()) { - if (r->is_pinned()) { - if (r->pin_count() == 0) { - ShenandoahHeapLocker locker(_lock); - r->make_unpinned(); - } - } else { - if (r->pin_count() > 0) { - ShenandoahHeapLocker locker(_lock); - r->make_pinned(); - } - } - } - } - - bool is_thread_safe() { return true; } -}; - void ShenandoahHeap::update_heap_region_states(bool concurrent) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); assert(!is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); @@ -2286,8 +2394,8 @@ void ShenandoahHeap::update_heap_region_states(bool concurrent) { ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_update_refs_update_region_states : ShenandoahPhaseTimings::degen_gc_final_update_refs_update_region_states); - ShenandoahFinalUpdateRefsUpdateRegionStateClosure cl; - parallel_heap_region_iterate(&cl); + + final_update_refs_update_region_states(); assert_pinned_region_status(); } @@ -2300,13 +2408,54 @@ void ShenandoahHeap::update_heap_region_states(bool concurrent) { } } +void ShenandoahHeap::final_update_refs_update_region_states() { + ShenandoahSynchronizePinnedRegionStates cl; + parallel_heap_region_iterate(&cl); +} + void ShenandoahHeap::rebuild_free_set(bool concurrent) { - { - ShenandoahGCPhase phase(concurrent ? - ShenandoahPhaseTimings::final_update_refs_rebuild_freeset : - ShenandoahPhaseTimings::degen_gc_final_update_refs_rebuild_freeset); - ShenandoahHeapLocker locker(lock()); - _free_set->rebuild(); + ShenandoahGCPhase phase(concurrent ? + ShenandoahPhaseTimings::final_update_refs_rebuild_freeset : + ShenandoahPhaseTimings::degen_gc_final_update_refs_rebuild_freeset); + ShenandoahHeapLocker locker(lock()); + size_t young_cset_regions, old_cset_regions; + size_t first_old_region, last_old_region, old_region_count; + _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count); + // If there are no old regions, first_old_region will be greater than last_old_region + assert((first_old_region > last_old_region) || + ((last_old_region + 1 - first_old_region >= old_region_count) && + get_region(first_old_region)->is_old() && get_region(last_old_region)->is_old()), + "sanity: old_region_count: " SIZE_FORMAT ", first_old_region: " SIZE_FORMAT ", last_old_region: " SIZE_FORMAT, + old_region_count, first_old_region, last_old_region); + + if (mode()->is_generational()) { +#ifdef ASSERT + if (ShenandoahVerify) { + verifier()->verify_before_rebuilding_free_set(); + } +#endif + + // The computation of bytes_of_allocation_runway_before_gc_trigger is quite conservative so consider all of this + // available for transfer to old. Note that transfer of humongous regions does not impact available. + ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); + size_t allocation_runway = gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions); + gen_heap->compute_old_generation_balance(allocation_runway, old_cset_regions); + + // Total old_available may have been expanded to hold anticipated promotions. We trigger if the fragmented available + // memory represents more than 16 regions worth of data. Note that fragmentation may increase when we promote regular + // regions in place when many of these regular regions have an abundant amount of available memory within them. Fragmentation + // will decrease as promote-by-copy consumes the available memory within these partially consumed regions. + // + // We consider old-gen to have excessive fragmentation if more than 12.5% of old-gen is free memory that resides + // within partially consumed regions of memory. + } + // Rebuild free set based on adjusted generation sizes. + _free_set->finish_rebuild(young_cset_regions, old_cset_regions, old_region_count); + + if (mode()->is_generational()) { + ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); + ShenandoahOldGeneration* old_gen = gen_heap->old_generation(); + old_gen->heuristics()->evaluate_triggers(first_old_region, last_old_region, old_region_count, num_regions()); } } @@ -2429,7 +2578,7 @@ GrowableArray ShenandoahHeap::memory_pools() { } MemoryUsage ShenandoahHeap::memory_usage() { - return _memory_pool->get_memory_usage(); + return MemoryUsage(_initial_size, used(), committed(), max_capacity()); } ShenandoahRegionIterator::ShenandoahRegionIterator() : @@ -2571,3 +2720,26 @@ void ShenandoahHeap::complete_loaded_archive_space(MemRegion archive_space) { p2i(end), p2i(end_reg->top())); #endif } + +ShenandoahGeneration* ShenandoahHeap::generation_for(ShenandoahAffiliation affiliation) const { + if (!mode()->is_generational()) { + return global_generation(); + } else if (affiliation == YOUNG_GENERATION) { + return young_generation(); + } else if (affiliation == OLD_GENERATION) { + return old_generation(); + } + + ShouldNotReachHere(); + return nullptr; +} + +void ShenandoahHeap::log_heap_status(const char* msg) const { + if (mode()->is_generational()) { + young_generation()->log_status(msg); + old_generation()->log_status(msg); + } else { + global_generation()->log_status(msg); + } +} + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 7e616f925d0..a9a793f9e60 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -29,11 +30,16 @@ #include "gc/shared/markBitMap.hpp" #include "gc/shared/softRefPolicy.hpp" #include "gc/shared/collectedHeap.hpp" -#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" -#include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahController.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahEvacOOMHandler.hpp" +#include "gc/shenandoah/shenandoahEvacTracker.hpp" +#include "gc/shenandoah/shenandoahGenerationType.hpp" +#include "gc/shenandoah/shenandoahGenerationSizer.hpp" +#include "gc/shenandoah/shenandoahMmuTracker.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "gc/shenandoah/shenandoahUnload.hpp" @@ -45,9 +51,11 @@ class ConcurrentGCTimer; class ObjectIterateScanRootClosure; class ShenandoahCollectorPolicy; -class ShenandoahControlThread; class ShenandoahGCSession; class ShenandoahGCStateResetter; +class ShenandoahGeneration; +class ShenandoahYoungGeneration; +class ShenandoahOldGeneration; class ShenandoahHeuristics; class ShenandoahMarkingContext; class ShenandoahMode; @@ -117,7 +125,7 @@ typedef Stack ShenandoahScanObjectStack; // to encode forwarding data. See BrooksPointer for details on forwarding data encoding. // See ShenandoahControlThread for GC cycle structure. // -class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { +class ShenandoahHeap : public CollectedHeap { friend class ShenandoahAsserts; friend class VMStructs; friend class ShenandoahGCSession; @@ -127,6 +135,7 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // Supported GC friend class ShenandoahConcurrentGC; + friend class ShenandoahOldGC; friend class ShenandoahDegenGC; friend class ShenandoahFullGC; friend class ShenandoahUnload; @@ -136,11 +145,46 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { private: ShenandoahHeapLock _lock; + // Indicates the generation whose collection is in + // progress. Mutator threads aren't allowed to read + // this field. + ShenandoahGeneration* _gc_generation; + + // This is set and cleared by only the VMThread + // at each STW pause (safepoint) to the value seen in + // _gc_generation. This allows the value to be always consistently + // seen by all mutators as well as all GC worker threads. + // In that sense, it's a stable snapshot of _gc_generation that is + // updated at each STW pause associated with a ShenandoahVMOp. + ShenandoahGeneration* _active_generation; + public: ShenandoahHeapLock* lock() { return &_lock; } + ShenandoahGeneration* gc_generation() const { + // We don't want this field read by a mutator thread + assert(!Thread::current()->is_Java_thread(), "Not allowed"); + // value of _gc_generation field, see above + return _gc_generation; + } + + ShenandoahGeneration* active_generation() const { + // value of _active_generation field, see above + return _active_generation; + } + + // Set the _gc_generation field + void set_gc_generation(ShenandoahGeneration* generation); + + // Copy the value in the _gc_generation field into + // the _active_generation field: can only be called at + // a safepoint by the VMThread. + void set_active_generation(); + + ShenandoahHeuristics* heuristics(); + // ---------- Initialization, termination, identification, printing routines // public: @@ -153,8 +197,8 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { jint initialize() override; void post_initialize() override; void initialize_mode(); - void initialize_heuristics(); - + virtual void initialize_heuristics(); + virtual void print_init_logger() const; void initialize_serviceability() override; void print_on(outputStream* st) const override; @@ -175,35 +219,34 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // ---------- Heap counters and metrics // private: - size_t _initial_size; - size_t _minimum_size; + size_t _initial_size; + size_t _minimum_size; + volatile size_t _soft_max_size; shenandoah_padding(0); - volatile size_t _used; volatile size_t _committed; - volatile size_t _bytes_allocated_since_gc_start; shenandoah_padding(1); + void increase_used(const ShenandoahAllocRequest& req); + public: - void increase_used(size_t bytes); - void decrease_used(size_t bytes); - void set_used(size_t bytes); + void increase_used(ShenandoahGeneration* generation, size_t bytes); + void decrease_used(ShenandoahGeneration* generation, size_t bytes); + void increase_humongous_waste(ShenandoahGeneration* generation, size_t bytes); + void decrease_humongous_waste(ShenandoahGeneration* generation, size_t bytes); void increase_committed(size_t bytes); void decrease_committed(size_t bytes); - void increase_allocated(size_t bytes); - size_t bytes_allocated_since_gc_start() const override; void reset_bytes_allocated_since_gc_start(); size_t min_capacity() const; size_t max_capacity() const override; - size_t soft_max_capacity() const override; + size_t soft_max_capacity() const; size_t initial_capacity() const; size_t capacity() const override; size_t used() const override; size_t committed() const; - size_t available() const override; void set_soft_max_capacity(size_t v); @@ -223,6 +266,8 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { ShenandoahWorkerThreads* _workers; ShenandoahWorkerThreads* _safepoint_workers; + virtual void initialize_controller(); + public: uint max_workers(); void assert_gc_workers(uint nworker) NOT_DEBUG_RETURN; @@ -239,7 +284,7 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { bool _heap_region_special; size_t _num_regions; ShenandoahHeapRegion** _regions; - ShenandoahRegionIterator _update_refs_iterator; + uint8_t* _affiliations; // Holds array of enum ShenandoahAffiliation, including FREE status in non-generational mode public: @@ -256,6 +301,8 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { void heap_region_iterate(ShenandoahHeapRegionClosure* blk) const; void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* blk) const; + inline ShenandoahMmuTracker* mmu_tracker() { return &_mmu_tracker; }; + // ---------- GC state machinery // // GC state describes the important parts of collector state, that may be @@ -271,6 +318,7 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { HAS_FORWARDED_BITPOS = 0, // Heap is under marking: needs SATB barriers. + // For generational mode, it means either young or old marking, or both. MARKING_BITPOS = 1, // Heap is under evacuation: needs LRB barriers. (Set together with HAS_FORWARDED) @@ -281,6 +329,12 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // Heap is under weak-reference/roots processing: needs weak-LRB barriers. WEAK_ROOTS_BITPOS = 4, + + // Young regions are under marking, need SATB barriers. + YOUNG_MARKING_BITPOS = 5, + + // Old regions are under marking, need SATB barriers. + OLD_MARKING_BITPOS = 6 }; enum GCState { @@ -290,13 +344,13 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { EVACUATION = 1 << EVACUATION_BITPOS, UPDATEREFS = 1 << UPDATEREFS_BITPOS, WEAK_ROOTS = 1 << WEAK_ROOTS_BITPOS, + YOUNG_MARKING = 1 << YOUNG_MARKING_BITPOS, + OLD_MARKING = 1 << OLD_MARKING_BITPOS }; private: bool _gc_state_changed; ShenandoahSharedBitmap _gc_state; - - // tracks if new regions have been allocated or retired since last check ShenandoahSharedFlag _heap_changed; ShenandoahSharedFlag _degenerated_gc_in_progress; ShenandoahSharedFlag _full_gc_in_progress; @@ -325,7 +379,8 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { return _heap_changed.try_unset(); } - void set_concurrent_mark_in_progress(bool in_progress); + void set_concurrent_young_mark_in_progress(bool in_progress); + void set_concurrent_old_mark_in_progress(bool in_progress); void set_evacuation_in_progress(bool in_progress); void set_update_refs_in_progress(bool in_progress); void set_degenerated_gc_in_progress(bool in_progress); @@ -337,7 +392,10 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { inline bool is_stable() const; inline bool is_idle() const; + inline bool is_concurrent_mark_in_progress() const; + inline bool is_concurrent_young_mark_in_progress() const; + inline bool is_concurrent_old_mark_in_progress() const; inline bool is_update_refs_in_progress() const; inline bool is_evacuation_in_progress() const; inline bool is_degenerated_gc_in_progress() const; @@ -348,8 +406,11 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { inline bool is_stw_gc_in_progress() const; inline bool is_concurrent_strong_root_in_progress() const; inline bool is_concurrent_weak_root_in_progress() const; + bool is_prepare_for_old_mark_in_progress() const; private: + void manage_satb_barrier(bool active); + enum CancelState { // Normal state. GC has not been cancelled and is open for cancellation. // Worker threads can suspend for safepoint. @@ -360,16 +421,22 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { CANCELLED }; + double _cancel_requested_time; ShenandoahSharedEnumFlag _cancelled_gc; + + // Returns true if cancel request was successfully communicated. + // Returns false if some other thread already communicated cancel + // request. A true return value does not mean GC has been + // cancelled, only that the process of cancelling GC has begun. bool try_cancel_gc(); public: - inline bool cancelled_gc() const; inline bool check_cancelled_gc_and_yield(bool sts_active = true); - inline void clear_cancelled_gc(); + inline void clear_cancelled_gc(bool clear_oom_handler = true); + void cancel_concurrent_mark(); void cancel_gc(GCCause::Cause cause); public: @@ -381,13 +448,16 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // Returns true if the soft maximum heap has been changed using management APIs. bool check_soft_max_changed(); +protected: + // This is shared between shConcurrentGC and shDegenerateGC so that degenerated + // GC can resume update refs from where the concurrent GC was cancelled. It is + // also used in shGenerationalHeap, which uses a different closure for update refs. + ShenandoahRegionIterator _update_refs_iterator; + private: // GC support - // Reset bitmap, prepare regions for new GC cycle - void prepare_gc(); - void prepare_regions_and_collection_set(bool concurrent); // Evacuation - void evacuate_collection_set(bool concurrent); + virtual void evacuate_collection_set(bool concurrent); // Concurrent root processing void prepare_concurrent_roots(); void finish_concurrent_roots(); @@ -395,14 +465,15 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { void do_class_unloading(); // Reference updating void prepare_update_heap_references(bool concurrent); - void update_heap_references(bool concurrent); + virtual void update_heap_references(bool concurrent); // Final update region states void update_heap_region_states(bool concurrent); - void rebuild_free_set(bool concurrent); + virtual void final_update_refs_update_region_states(); void rendezvous_threads(const char* name); void recycle_trash(); public: + void rebuild_free_set(bool concurrent); void notify_gc_progress(); void notify_gc_no_progress(); size_t get_gc_no_progress_count() const; @@ -410,27 +481,52 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // // Mark support private: - ShenandoahControlThread* _control_thread; + ShenandoahGeneration* _global_generation; + +protected: + ShenandoahController* _control_thread; + + ShenandoahYoungGeneration* _young_generation; + ShenandoahOldGeneration* _old_generation; + +private: ShenandoahCollectorPolicy* _shenandoah_policy; ShenandoahMode* _gc_mode; - ShenandoahHeuristics* _heuristics; ShenandoahFreeSet* _free_set; ShenandoahPacer* _pacer; ShenandoahVerifier* _verifier; - ShenandoahPhaseTimings* _phase_timings; - - ShenandoahControlThread* control_thread() { return _control_thread; } + ShenandoahPhaseTimings* _phase_timings; + ShenandoahMmuTracker _mmu_tracker; public: + ShenandoahController* control_thread() { return _control_thread; } + + ShenandoahGeneration* global_generation() const { return _global_generation; } + ShenandoahYoungGeneration* young_generation() const { + assert(mode()->is_generational(), "Young generation requires generational mode"); + return _young_generation; + } + + ShenandoahOldGeneration* old_generation() const { + assert(mode()->is_generational(), "Old generation requires generational mode"); + return _old_generation; + } + + ShenandoahGeneration* generation_for(ShenandoahAffiliation affiliation) const; + ShenandoahCollectorPolicy* shenandoah_policy() const { return _shenandoah_policy; } ShenandoahMode* mode() const { return _gc_mode; } - ShenandoahHeuristics* heuristics() const { return _heuristics; } ShenandoahFreeSet* free_set() const { return _free_set; } ShenandoahPacer* pacer() const { return _pacer; } ShenandoahPhaseTimings* phase_timings() const { return _phase_timings; } + ShenandoahEvacOOMHandler* oom_evac_handler() { return &_oom_evac_handler; } + + void on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation); + void on_cycle_end(ShenandoahGeneration* generation); + ShenandoahVerifier* verifier(); // ---------- VM subsystem bindings @@ -444,7 +540,7 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // For exporting to SA int _log_min_obj_alignment_in_bytes; public: - ShenandoahMonitoringSupport* monitoring_support() { return _monitoring_support; } + ShenandoahMonitoringSupport* monitoring_support() const { return _monitoring_support; } GCMemoryManager* cycle_memory_manager() { return &_cycle_memory_manager; } GCMemoryManager* stw_memory_manager() { return &_stw_memory_manager; } @@ -454,14 +550,6 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { GCTracer* tracer(); ConcurrentGCTimer* gc_timer() const; -// ---------- Reference processing -// -private: - ShenandoahReferenceProcessor* const _ref_processor; - -public: - ShenandoahReferenceProcessor* ref_processor() { return _ref_processor; } - // ---------- Class Unloading // private: @@ -480,6 +568,9 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { void stw_process_weak_roots(bool full_gc); void stw_weak_refs(bool full_gc); + inline void assert_lock_for_affiliation(ShenandoahAffiliation orig_affiliation, + ShenandoahAffiliation new_affiliation); + // Heap iteration support void scan_roots_for_iteration(ShenandoahScanObjectStack* oop_stack, ObjectIterateScanRootClosure* oops); bool prepare_aux_bitmap_for_iteration(); @@ -497,6 +588,21 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // Use is_in_reserved to check if object is within heap bounds. bool is_in(const void* p) const override; + // Returns true if the given oop belongs to a generation that is actively being collected. + inline bool is_in_active_generation(oop obj) const; + inline bool is_in_young(const void* p) const; + inline bool is_in_old(const void* p) const; + + // Returns true iff the young generation is being collected and the given pointer + // is in the old generation. This is used to prevent the young collection from treating + // such an object as unreachable. + inline bool is_in_old_during_young_collection(oop obj) const; + + inline ShenandoahAffiliation region_affiliation(const ShenandoahHeapRegion* r); + inline void set_affiliation(ShenandoahHeapRegion* r, ShenandoahAffiliation new_affiliation); + + inline ShenandoahAffiliation region_affiliation(size_t index); + bool requires_barriers(stackChunkOop obj) const override; MemRegion reserved_region() const { return _reserved; } @@ -543,15 +649,17 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // ---------- CDS archive support - bool can_load_archived_objects() const override { return true; } + bool can_load_archived_objects() const override { return !ShenandoahCardBarrier; } HeapWord* allocate_loaded_archive_space(size_t size) override; void complete_loaded_archive_space(MemRegion archive_space) override; // ---------- Allocation support // +protected: + inline HeapWord* allocate_from_gclab(Thread* thread, size_t size); + private: HeapWord* allocate_memory_under_lock(ShenandoahAllocRequest& request, bool& in_new_region); - inline HeapWord* allocate_from_gclab(Thread* thread, size_t size); HeapWord* allocate_from_gclab_slow(Thread* thread, size_t size); HeapWord* allocate_new_gclab(size_t min_size, size_t word_size, size_t* actual_size); @@ -562,7 +670,7 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { size_t size, Metaspace::MetadataType mdtype) override; - void notify_mutator_alloc_words(size_t words, bool waste); + void notify_mutator_alloc_words(size_t words, size_t waste); HeapWord* allocate_new_tlab(size_t min_size, size_t requested_size, size_t* actual_size) override; size_t tlab_capacity(Thread *thr) const override; @@ -600,8 +708,6 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { public: inline ShenandoahMarkingContext* complete_marking_context() const; inline ShenandoahMarkingContext* marking_context() const; - inline void mark_complete_marking_context(); - inline void mark_incomplete_marking_context(); template inline void marked_object_iterate(ShenandoahHeapRegion* region, T* cl); @@ -612,8 +718,6 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { template inline void marked_object_oop_iterate(ShenandoahHeapRegion* region, T* cl, HeapWord* limit); - void reset_mark_bitmap(); - // SATB barriers hooks inline bool requires_marking(const void* entry) const; @@ -634,6 +738,8 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { ShenandoahCollectionSet* _collection_set; ShenandoahEvacOOMHandler _oom_evac_handler; + oop try_evacuate_object(oop src, Thread* thread, ShenandoahHeapRegion* from_region, ShenandoahAffiliation target_gen); + public: static address in_cset_fast_test_addr(); @@ -645,9 +751,9 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { // Checks if location is in the collection set. Can be interior pointer, not the oop itself. inline bool in_collection_set_loc(void* loc) const; - // Evacuates object src. Returns the evacuated object, either evacuated + // Evacuates or promotes object src. Returns the evacuated object, either evacuated // by this thread, or by some other thread. - oop evacuate_object(oop src, Thread* thread); + virtual oop evacuate_object(oop src, Thread* thread); // Call before/after evacuation. inline void enter_evacuation(Thread* t); @@ -674,7 +780,16 @@ class ShenandoahHeap : public CollectedHeap, public ShenandoahSpaceInfo { static inline void atomic_clear_oop(narrowOop* addr, oop compare); static inline void atomic_clear_oop(narrowOop* addr, narrowOop compare); - void trash_humongous_region_at(ShenandoahHeapRegion *r); + size_t trash_humongous_region_at(ShenandoahHeapRegion *r); + + static inline void increase_object_age(oop obj, uint additional_age); + + // Return the object's age, or a sentinel value when the age can't + // necessarily be determined because of concurrent locking by the + // mutator + static inline uint get_object_age(oop obj); + + void log_heap_status(const char *msg) const; private: void trash_cset_regions(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 6131d079590..b2c8e0405e0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -40,14 +41,16 @@ #include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" -#include "gc/shenandoah/shenandoahControlThread.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/prefetch.inline.hpp" +#include "runtime/objectMonitor.inline.hpp" #include "utilities/copy.hpp" #include "utilities/globalDefinitions.hpp" @@ -264,9 +267,16 @@ inline bool ShenandoahHeap::check_cancelled_gc_and_yield(bool sts_active) { return cancelled_gc(); } -inline void ShenandoahHeap::clear_cancelled_gc() { +inline void ShenandoahHeap::clear_cancelled_gc(bool clear_oom_handler) { _cancelled_gc.set(CANCELLABLE); - _oom_evac_handler.clear(); + if (_cancel_requested_time > 0) { + log_debug(gc)("GC cancellation took %.3fs", (os::elapsedTime() - _cancel_requested_time)); + _cancel_requested_time = 0; + } + + if (clear_oom_handler) { + _oom_evac_handler.clear(); + } } inline HeapWord* ShenandoahHeap::allocate_from_gclab(Thread* thread, size_t size) { @@ -283,10 +293,143 @@ inline HeapWord* ShenandoahHeap::allocate_from_gclab(Thread* thread, size_t size if (obj != nullptr) { return obj; } - // Otherwise... return allocate_from_gclab_slow(thread, size); } +void ShenandoahHeap::increase_object_age(oop obj, uint additional_age) { + // This operates on new copy of an object. This means that the object's mark-word + // is thread-local and therefore safe to access. However, when the mark is + // displaced (i.e. stack-locked or monitor-locked), then it must be considered + // a shared memory location. It can be accessed by other threads. + // In particular, a competing evacuating thread can succeed to install its copy + // as the forwardee and continue to unlock the object, at which point 'our' + // write to the foreign stack-location would potentially over-write random + // information on that stack. Writing to a monitor is less problematic, + // but still not safe: while the ObjectMonitor would not randomly disappear, + // the other thread would also write to the same displaced header location, + // possibly leading to increase the age twice. + // For all these reasons, we take the conservative approach and not attempt + // to increase the age when the header is displaced. + markWord w = obj->mark(); + // The mark-word has been copied from the original object. It can not be + // inflating, because inflation can not be interrupted by a safepoint, + // and after a safepoint, a Java thread would first have to successfully + // evacuate the object before it could inflate the monitor. + assert(!w.is_being_inflated() || LockingMode == LM_LIGHTWEIGHT, "must not inflate monitor before evacuation of object succeeds"); + // It is possible that we have copied the object after another thread has + // already successfully completed evacuation. While harmless (we would never + // publish our copy), don't even attempt to modify the age when that + // happens. + if (!w.has_displaced_mark_helper() && !w.is_marked()) { + w = w.set_age(MIN2(markWord::max_age, w.age() + additional_age)); + obj->set_mark(w); + } +} + +// Return the object's age, or a sentinel value when the age can't +// necessarily be determined because of concurrent locking by the +// mutator +uint ShenandoahHeap::get_object_age(oop obj) { + markWord w = obj->mark(); + assert(!w.is_marked(), "must not be forwarded"); + if (w.has_monitor()) { + w = w.monitor()->header(); + } else if (w.is_being_inflated() || w.has_displaced_mark_helper()) { + // Informs caller that we aren't able to determine the age + return markWord::max_age + 1; // sentinel + } + assert(w.age() <= markWord::max_age, "Impossible!"); + return w.age(); +} + +inline bool ShenandoahHeap::is_in_active_generation(oop obj) const { + if (!mode()->is_generational()) { + // everything is the same single generation + assert(is_in_reserved(obj), "Otherwise shouldn't return true below"); + return true; + } + + ShenandoahGeneration* const gen = active_generation(); + + if (gen == nullptr) { + // no collection is happening: only expect this to be called + // when concurrent processing is active, but that could change + return false; + } + + assert(is_in_reserved(obj), "only check if is in active generation for objects (" PTR_FORMAT ") in heap", p2i(obj)); + assert(gen->is_old() || gen->is_young() || gen->is_global(), + "Active generation must be old, young, or global"); + + size_t index = heap_region_containing(obj)->index(); + + // No flickering! + assert(gen == active_generation(), "Race?"); + + switch (_affiliations[index]) { + case ShenandoahAffiliation::FREE: + // Free regions are in old, young, and global collections + return true; + case ShenandoahAffiliation::YOUNG_GENERATION: + // Young regions are in young and global collections, not in old collections + return !gen->is_old(); + case ShenandoahAffiliation::OLD_GENERATION: + // Old regions are in old and global collections, not in young collections + return !gen->is_young(); + default: + assert(false, "Bad affiliation (%d) for region " SIZE_FORMAT, _affiliations[index], index); + return false; + } +} + +inline bool ShenandoahHeap::is_in_young(const void* p) const { + return is_in_reserved(p) && (_affiliations[heap_region_index_containing(p)] == ShenandoahAffiliation::YOUNG_GENERATION); +} + +inline bool ShenandoahHeap::is_in_old(const void* p) const { + return is_in_reserved(p) && (_affiliations[heap_region_index_containing(p)] == ShenandoahAffiliation::OLD_GENERATION); +} + +inline bool ShenandoahHeap::is_in_old_during_young_collection(oop obj) const { + return active_generation()->is_young() && is_in_old(obj); +} + +inline ShenandoahAffiliation ShenandoahHeap::region_affiliation(const ShenandoahHeapRegion *r) { + return (ShenandoahAffiliation) _affiliations[r->index()]; +} + +inline void ShenandoahHeap::assert_lock_for_affiliation(ShenandoahAffiliation orig_affiliation, + ShenandoahAffiliation new_affiliation) { + // A lock is required when changing from FREE to NON-FREE. Though it may be possible to elide the lock when + // transitioning from in-use to FREE, the current implementation uses a lock for this transition. A lock is + // not required to change from YOUNG to OLD (i.e. when promoting humongous region). + // + // new_affiliation is: FREE YOUNG OLD + // orig_affiliation is: FREE X L L + // YOUNG L X + // OLD L X X + // X means state transition won't happen (so don't care) + // L means lock should be held + // Blank means no lock required because affiliation visibility will not be required until subsequent safepoint + // + // Note: during full GC, all transitions between states are possible. During Full GC, we should be in a safepoint. + + if ((orig_affiliation == ShenandoahAffiliation::FREE) || (new_affiliation == ShenandoahAffiliation::FREE)) { + shenandoah_assert_heaplocked_or_safepoint(); + } +} + +inline void ShenandoahHeap::set_affiliation(ShenandoahHeapRegion* r, ShenandoahAffiliation new_affiliation) { +#ifdef ASSERT + assert_lock_for_affiliation(region_affiliation(r), new_affiliation); +#endif + _affiliations[r->index()] = (uint8_t) new_affiliation; +} + +inline ShenandoahAffiliation ShenandoahHeap::region_affiliation(size_t index) { + return (ShenandoahAffiliation) _affiliations[index]; +} + inline bool ShenandoahHeap::requires_marking(const void* entry) const { oop obj = cast_to_oop(entry); return !_marking_context->is_marked_strong(obj); @@ -314,6 +457,14 @@ inline bool ShenandoahHeap::is_concurrent_mark_in_progress() const { return _gc_state.is_set(MARKING); } +inline bool ShenandoahHeap::is_concurrent_young_mark_in_progress() const { + return _gc_state.is_set(YOUNG_MARKING); +} + +inline bool ShenandoahHeap::is_concurrent_old_mark_in_progress() const { + return _gc_state.is_set(OLD_MARKING); +} + inline bool ShenandoahHeap::is_evacuation_in_progress() const { return _gc_state.is_set(EVACUATION); } @@ -355,8 +506,7 @@ template inline void ShenandoahHeap::marked_object_iterate(ShenandoahHeapRegion* region, T* cl, HeapWord* limit) { assert(! region->is_humongous_continuation(), "no humongous continuation regions here"); - ShenandoahMarkingContext* const ctx = complete_marking_context(); - assert(ctx->is_complete(), "sanity"); + ShenandoahMarkingContext* const ctx = marking_context(); HeapWord* tams = ctx->top_at_mark_start(region); @@ -487,14 +637,6 @@ inline ShenandoahHeapRegion* ShenandoahHeap::get_region(size_t region_idx) const } } -inline void ShenandoahHeap::mark_complete_marking_context() { - _marking_context->mark_complete(); -} - -inline void ShenandoahHeap::mark_incomplete_marking_context() { - _marking_context->mark_incomplete(); -} - inline ShenandoahMarkingContext* ShenandoahHeap::complete_marking_context() const { assert (_marking_context->is_complete()," sanity"); return _marking_context; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index 92602871ccd..9767caf8156 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved. + * Copyright (c) 2013, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -24,12 +25,19 @@ */ #include "precompiled.hpp" +#include "gc/shared/cardTable.hpp" #include "gc/shared/space.hpp" #include "gc/shared/tlab_globals.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" #include "memory/iterator.inline.hpp" @@ -60,13 +68,20 @@ ShenandoahHeapRegion::ShenandoahHeapRegion(HeapWord* start, size_t index, bool c _end(start + RegionSizeWords), _new_top(nullptr), _empty_time(os::elapsedTime()), + _top_before_promoted(nullptr), _state(committed ? _empty_committed : _empty_uncommitted), _top(start), _tlab_allocs(0), _gclab_allocs(0), + _plab_allocs(0), _live_data(0), _critical_pins(0), - _update_watermark(start) { + _update_watermark(start), + _age(0) +#ifdef SHENANDOAH_CENSUS_NOISE + , _youth(0) +#endif // SHENANDOAH_CENSUS_NOISE + { assert(Universe::on_page_boundary(_bottom) && Universe::on_page_boundary(_end), "invalid space boundaries"); @@ -82,13 +97,14 @@ void ShenandoahHeapRegion::report_illegal_transition(const char *method) { fatal("%s", ss.freeze()); } -void ShenandoahHeapRegion::make_regular_allocation() { +void ShenandoahHeapRegion::make_regular_allocation(ShenandoahAffiliation affiliation) { shenandoah_assert_heaplocked(); - + reset_age(); switch (_state) { case _empty_uncommitted: do_commit(); case _empty_committed: + assert(this->affiliation() == affiliation, "Region affiliation should already be established"); set_state(_regular); case _regular: case _pinned: @@ -98,13 +114,38 @@ void ShenandoahHeapRegion::make_regular_allocation() { } } +// Change affiliation to YOUNG_GENERATION if _state is not _pinned_cset, _regular, or _pinned. This implements +// behavior previously performed as a side effect of make_regular_bypass(). This is used by Full GC in non-generational +// modes to transition regions from FREE. Note that all non-free regions in single-generational modes are young. +void ShenandoahHeapRegion::make_affiliated_maybe() { + shenandoah_assert_heaplocked(); + assert(!ShenandoahHeap::heap()->mode()->is_generational(), "Only call if non-generational"); + switch (_state) { + case _empty_uncommitted: + case _empty_committed: + case _cset: + case _humongous_start: + case _humongous_cont: + if (affiliation() != YOUNG_GENERATION) { + set_affiliation(YOUNG_GENERATION); + } + return; + case _pinned_cset: + case _regular: + case _pinned: + return; + default: + assert(false, "Unexpected _state in make_affiliated_maybe"); + } +} + void ShenandoahHeapRegion::make_regular_bypass() { shenandoah_assert_heaplocked(); assert (!Universe::is_fully_initialized() || ShenandoahHeap::heap()->is_full_gc_in_progress() || ShenandoahHeap::heap()->is_degenerated_gc_in_progress(), "Only for STW GC or when Universe is initializing (CDS)"); - + reset_age(); switch (_state) { case _empty_uncommitted: do_commit(); @@ -112,6 +153,14 @@ void ShenandoahHeapRegion::make_regular_bypass() { case _cset: case _humongous_start: case _humongous_cont: + if (_state == _humongous_start || _state == _humongous_cont) { + // CDS allocates chunks of the heap to fill with regular objects. The allocator + // will dutifully track any waste in the unused portion of the last region. Once + // CDS has finished initializing the objects, it will convert these regions to + // regular regions. The 'waste' in the last region is no longer wasted at this point, + // so we must stop treating it as such. + decrement_humongous_waste(); + } set_state(_regular); return; case _pinned_cset: @@ -127,6 +176,7 @@ void ShenandoahHeapRegion::make_regular_bypass() { void ShenandoahHeapRegion::make_humongous_start() { shenandoah_assert_heaplocked(); + reset_age(); switch (_state) { case _empty_uncommitted: do_commit(); @@ -138,10 +188,12 @@ void ShenandoahHeapRegion::make_humongous_start() { } } -void ShenandoahHeapRegion::make_humongous_start_bypass() { +void ShenandoahHeapRegion::make_humongous_start_bypass(ShenandoahAffiliation affiliation) { shenandoah_assert_heaplocked(); assert (ShenandoahHeap::heap()->is_full_gc_in_progress(), "only for full GC"); - + // Don't bother to account for affiliated regions during Full GC. We recompute totals at end. + set_affiliation(affiliation); + reset_age(); switch (_state) { case _empty_committed: case _regular: @@ -156,6 +208,7 @@ void ShenandoahHeapRegion::make_humongous_start_bypass() { void ShenandoahHeapRegion::make_humongous_cont() { shenandoah_assert_heaplocked(); + reset_age(); switch (_state) { case _empty_uncommitted: do_commit(); @@ -167,10 +220,12 @@ void ShenandoahHeapRegion::make_humongous_cont() { } } -void ShenandoahHeapRegion::make_humongous_cont_bypass() { +void ShenandoahHeapRegion::make_humongous_cont_bypass(ShenandoahAffiliation affiliation) { shenandoah_assert_heaplocked(); assert (ShenandoahHeap::heap()->is_full_gc_in_progress(), "only for full GC"); - + set_affiliation(affiliation); + // Don't bother to account for affiliated regions during Full GC. We recompute totals at end. + reset_age(); switch (_state) { case _empty_committed: case _regular: @@ -211,6 +266,7 @@ void ShenandoahHeapRegion::make_unpinned() { switch (_state) { case _pinned: + assert(is_affiliated(), "Pinned region should be affiliated"); set_state(_regular); return; case _regular: @@ -229,6 +285,7 @@ void ShenandoahHeapRegion::make_unpinned() { void ShenandoahHeapRegion::make_cset() { shenandoah_assert_heaplocked(); + // Leave age untouched. We need to consult the age when we are deciding whether to promote evacuated objects. switch (_state) { case _regular: set_state(_cset); @@ -241,12 +298,17 @@ void ShenandoahHeapRegion::make_cset() { void ShenandoahHeapRegion::make_trash() { shenandoah_assert_heaplocked(); + reset_age(); switch (_state) { - case _cset: - // Reclaiming cset regions case _humongous_start: case _humongous_cont: - // Reclaiming humongous regions + { + // Reclaiming humongous regions and reclaim humongous waste. When this region is eventually recycled, we'll reclaim + // its used memory. At recycle time, we no longer recognize this as a humongous region. + decrement_humongous_waste(); + } + case _cset: + // Reclaiming cset regions case _regular: // Immediate region reclaim set_state(_trash); @@ -261,11 +323,15 @@ void ShenandoahHeapRegion::make_trash_immediate() { // On this path, we know there are no marked objects in the region, // tell marking context about it to bypass bitmap resets. - ShenandoahHeap::heap()->complete_marking_context()->reset_top_bitmap(this); + assert(ShenandoahHeap::heap()->gc_generation()->is_mark_complete(), "Marking should be complete here."); + shenandoah_assert_generations_reconciled(); + ShenandoahHeap::heap()->marking_context()->reset_top_bitmap(this); } void ShenandoahHeapRegion::make_empty() { shenandoah_assert_heaplocked(); + reset_age(); + CENSUS_NOISE(clear_youth();) switch (_state) { case _trash: set_state(_empty_committed); @@ -305,10 +371,11 @@ void ShenandoahHeapRegion::make_committed_bypass() { void ShenandoahHeapRegion::reset_alloc_metadata() { _tlab_allocs = 0; _gclab_allocs = 0; + _plab_allocs = 0; } size_t ShenandoahHeapRegion::get_shared_allocs() const { - return used() - (_tlab_allocs + _gclab_allocs) * HeapWordSize; + return used() - (_tlab_allocs + _gclab_allocs + _plab_allocs) * HeapWordSize; } size_t ShenandoahHeapRegion::get_tlab_allocs() const { @@ -319,6 +386,10 @@ size_t ShenandoahHeapRegion::get_gclab_allocs() const { return _gclab_allocs * HeapWordSize; } +size_t ShenandoahHeapRegion::get_plab_allocs() const { + return _plab_allocs * HeapWordSize; +} + void ShenandoahHeapRegion::set_live_data(size_t s) { assert(Thread::current()->is_VM_thread(), "by VM thread"); _live_data = (s >> LogHeapWordSize); @@ -363,6 +434,8 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const { ShouldNotReachHere(); } + st->print("|%s", shenandoah_affiliation_code(affiliation())); + #define SHR_PTR_FORMAT "%12" PRIxPTR st->print("|BTE " SHR_PTR_FORMAT ", " SHR_PTR_FORMAT ", " SHR_PTR_FORMAT, @@ -374,6 +447,9 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const { st->print("|U " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(used()), proper_unit_for_byte_size(used())); st->print("|T " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_tlab_allocs()), proper_unit_for_byte_size(get_tlab_allocs())); st->print("|G " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_gclab_allocs()), proper_unit_for_byte_size(get_gclab_allocs())); + if (ShenandoahHeap::heap()->mode()->is_generational()) { + st->print("|P " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_plab_allocs()), proper_unit_for_byte_size(get_plab_allocs())); + } st->print("|S " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_shared_allocs()), proper_unit_for_byte_size(get_shared_allocs())); st->print("|L " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_live_data_bytes()), proper_unit_for_byte_size(get_live_data_bytes())); st->print("|CP " SIZE_FORMAT_W(3), pin_count()); @@ -382,33 +458,100 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const { #undef SHR_PTR_FORMAT } -void ShenandoahHeapRegion::oop_iterate(OopIterateClosure* blk) { - if (!is_active()) return; - if (is_humongous()) { - oop_iterate_humongous(blk); - } else { - oop_iterate_objects(blk); +// oop_iterate without closure, return true if completed without cancellation +bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable) { + + assert(!is_humongous(), "No need to fill or coalesce humongous regions"); + if (!is_active()) { + end_preemptible_coalesce_and_fill(); + return true; } -} -void ShenandoahHeapRegion::oop_iterate_objects(OopIterateClosure* blk) { - assert(! is_humongous(), "no humongous region here"); - HeapWord* obj_addr = bottom(); - HeapWord* t = top(); - // Could call objects iterate, but this is easier. + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + ShenandoahMarkingContext* marking_context = heap->marking_context(); + + // Expect marking to be completed before these threads invoke this service. + assert(heap->gc_generation()->is_mark_complete(), "sanity"); + shenandoah_assert_generations_reconciled(); + + // All objects above TAMS are considered live even though their mark bits will not be set. Note that young- + // gen evacuations that interrupt a long-running old-gen concurrent mark may promote objects into old-gen + // while the old-gen concurrent marking is ongoing. These newly promoted objects will reside above TAMS + // and will be treated as live during the current old-gen marking pass, even though they will not be + // explicitly marked. + HeapWord* t = marking_context->top_at_mark_start(this); + + // Resume coalesce and fill from this address + HeapWord* obj_addr = resume_coalesce_and_fill(); + while (obj_addr < t) { oop obj = cast_to_oop(obj_addr); - obj_addr += obj->oop_iterate_size(blk); + if (marking_context->is_marked(obj)) { + assert(obj->klass() != nullptr, "klass should not be nullptr"); + obj_addr += obj->size(); + } else { + // Object is not marked. Coalesce and fill dead object with dead neighbors. + HeapWord* next_marked_obj = marking_context->get_next_marked_addr(obj_addr, t); + assert(next_marked_obj <= t, "next marked object cannot exceed top"); + size_t fill_size = next_marked_obj - obj_addr; + assert(fill_size >= ShenandoahHeap::min_fill_size(), "previously allocated object known to be larger than min_size"); + ShenandoahHeap::fill_with_object(obj_addr, fill_size); + heap->old_generation()->card_scan()->coalesce_objects(obj_addr, fill_size); + obj_addr = next_marked_obj; + } + if (cancellable && heap->cancelled_gc()) { + suspend_coalesce_and_fill(obj_addr); + return false; + } } + // Mark that this region has been coalesced and filled + end_preemptible_coalesce_and_fill(); + return true; } -void ShenandoahHeapRegion::oop_iterate_humongous(OopIterateClosure* blk) { +size_t get_card_count(size_t words) { + assert(words % CardTable::card_size_in_words() == 0, "Humongous iteration must span whole number of cards"); + assert(CardTable::card_size_in_words() * (words / CardTable::card_size_in_words()) == words, + "slice must be integral number of cards"); + return words / CardTable::card_size_in_words(); +} + +void ShenandoahHeapRegion::oop_iterate_humongous_slice_dirty(OopIterateClosure* blk, + HeapWord* start, size_t words, bool write_table) const { assert(is_humongous(), "only humongous region here"); - // Find head. + ShenandoahHeapRegion* r = humongous_start_region(); - assert(r->is_humongous_start(), "need humongous head here"); oop obj = cast_to_oop(r->bottom()); - obj->oop_iterate(blk, MemRegion(bottom(), top())); + size_t num_cards = get_card_count(words); + + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + ShenandoahScanRemembered* scanner = heap->old_generation()->card_scan(); + size_t card_index = scanner->card_index_for_addr(start); + if (write_table) { + while (num_cards-- > 0) { + if (scanner->is_write_card_dirty(card_index++)) { + obj->oop_iterate(blk, MemRegion(start, start + CardTable::card_size_in_words())); + } + start += CardTable::card_size_in_words(); + } + } else { + while (num_cards-- > 0) { + if (scanner->is_card_dirty(card_index++)) { + obj->oop_iterate(blk, MemRegion(start, start + CardTable::card_size_in_words())); + } + start += CardTable::card_size_in_words(); + } + } +} + +void ShenandoahHeapRegion::oop_iterate_humongous_slice_all(OopIterateClosure* cl, HeapWord* start, size_t words) const { + assert(is_humongous(), "only humongous region here"); + + ShenandoahHeapRegion* r = humongous_start_region(); + oop obj = cast_to_oop(r->bottom()); + + // Scan all data, regardless of whether cards are dirty + obj->oop_iterate(cl, MemRegion(start, start + words)); } ShenandoahHeapRegion* ShenandoahHeapRegion::humongous_start_region() const { @@ -427,16 +570,24 @@ ShenandoahHeapRegion* ShenandoahHeapRegion::humongous_start_region() const { } void ShenandoahHeapRegion::recycle() { + shenandoah_assert_heaplocked(); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahGeneration* generation = heap->generation_for(affiliation()); + + heap->decrease_used(generation, used()); + generation->decrement_affiliated_region_count(); + set_top(bottom()); clear_live_data(); - reset_alloc_metadata(); - ShenandoahHeap::heap()->marking_context()->reset_top_at_mark_start(this); + heap->marking_context()->reset_top_at_mark_start(this); + set_update_watermark(bottom()); make_empty(); + set_affiliation(FREE); if (ZapUnusedHeapArea) { SpaceMangler::mangle_region(MemRegion(bottom(), end())); } @@ -480,6 +631,11 @@ size_t ShenandoahHeapRegion::setup_sizes(size_t max_heap_size) { FLAG_SET_DEFAULT(ShenandoahMinRegionSize, MIN_REGION_SIZE); } + // Generational Shenandoah needs this alignment for card tables. + if (strcmp(ShenandoahGCMode, "generational") == 0) { + max_heap_size = align_up(max_heap_size , CardTable::ct_max_alignment_constraint()); + } + size_t region_size; if (FLAG_IS_DEFAULT(ShenandoahRegionSize)) { if (ShenandoahMinRegionSize > max_heap_size / MIN_NUM_REGIONS) { @@ -658,3 +814,65 @@ void ShenandoahHeapRegion::record_unpin() { size_t ShenandoahHeapRegion::pin_count() const { return Atomic::load(&_critical_pins); } + +void ShenandoahHeapRegion::set_affiliation(ShenandoahAffiliation new_affiliation) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + ShenandoahAffiliation region_affiliation = heap->region_affiliation(this); + { + ShenandoahMarkingContext* const ctx = heap->complete_marking_context(); + log_debug(gc)("Setting affiliation of Region " SIZE_FORMAT " from %s to %s, top: " PTR_FORMAT ", TAMS: " PTR_FORMAT + ", watermark: " PTR_FORMAT ", top_bitmap: " PTR_FORMAT, + index(), shenandoah_affiliation_name(region_affiliation), shenandoah_affiliation_name(new_affiliation), + p2i(top()), p2i(ctx->top_at_mark_start(this)), p2i(_update_watermark), p2i(ctx->top_bitmap(this))); + } + +#ifdef ASSERT + { + // During full gc, heap->complete_marking_context() is not valid, may equal nullptr. + ShenandoahMarkingContext* const ctx = heap->complete_marking_context(); + size_t idx = this->index(); + HeapWord* top_bitmap = ctx->top_bitmap(this); + + assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, _end), + "Region " SIZE_FORMAT ", bitmap should be clear between top_bitmap: " PTR_FORMAT " and end: " PTR_FORMAT, idx, + p2i(top_bitmap), p2i(_end)); + } +#endif + + if (region_affiliation == new_affiliation) { + return; + } + + if (!heap->mode()->is_generational()) { + log_trace(gc)("Changing affiliation of region %zu from %s to %s", + index(), affiliation_name(), shenandoah_affiliation_name(new_affiliation)); + heap->set_affiliation(this, new_affiliation); + return; + } + + switch (new_affiliation) { + case FREE: + assert(!has_live(), "Free region should not have live data"); + break; + case YOUNG_GENERATION: + reset_age(); + break; + case OLD_GENERATION: + break; + default: + ShouldNotReachHere(); + return; + } + heap->set_affiliation(this, new_affiliation); +} + +void ShenandoahHeapRegion::decrement_humongous_waste() const { + assert(is_humongous(), "Should only use this for humongous regions"); + size_t waste_bytes = free(); + if (waste_bytes > 0) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahGeneration* generation = heap->generation_for(affiliation()); + heap->decrease_humongous_waste(generation, waste_bytes); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index c34c4c232e4..1c0f1182f07 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -27,6 +28,8 @@ #include "gc/shared/gc_globals.hpp" #include "gc/shared/spaceDecorator.hpp" +#include "gc/shenandoah/shenandoahAffiliation.hpp" +#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" @@ -122,6 +125,7 @@ class ShenandoahHeapRegion { _REGION_STATES_NUM // last }; +public: static const char* region_state_to_string(RegionState s) { switch (s) { case _empty_uncommitted: return "Empty Uncommitted"; @@ -140,6 +144,7 @@ class ShenandoahHeapRegion { } } +private: // This method protects from accidental changes in enum order: int region_state_to_ordinal(RegionState s) const { switch (s) { @@ -167,12 +172,13 @@ class ShenandoahHeapRegion { } // Allowed transitions from the outside code: - void make_regular_allocation(); + void make_regular_allocation(ShenandoahAffiliation affiliation); + void make_affiliated_maybe(); void make_regular_bypass(); void make_humongous_start(); void make_humongous_cont(); - void make_humongous_start_bypass(); - void make_humongous_cont_bypass(); + void make_humongous_start_bypass(ShenandoahAffiliation affiliation); + void make_humongous_cont_bypass(ShenandoahAffiliation affiliation); void make_pinned(); void make_unpinned(); void make_cset(); @@ -197,6 +203,11 @@ class ShenandoahHeapRegion { bool is_committed() const { return !is_empty_uncommitted(); } bool is_cset() const { return _state == _cset || _state == _pinned_cset; } bool is_pinned() const { return _state == _pinned || _state == _pinned_cset || _state == _pinned_humongous_start; } + bool is_regular_pinned() const { return _state == _pinned; } + + inline bool is_young() const; + inline bool is_old() const; + inline bool is_affiliated() const; // Macro-properties: bool is_alloc_allowed() const { return is_empty() || is_regular() || _state == _pinned; } @@ -229,20 +240,27 @@ class ShenandoahHeapRegion { HeapWord* _new_top; double _empty_time; + HeapWord* _top_before_promoted; + // Seldom updated fields RegionState _state; + HeapWord* _coalesce_and_fill_boundary; // for old regions not selected as collection set candidates. // Frequently updated fields HeapWord* _top; size_t _tlab_allocs; size_t _gclab_allocs; + size_t _plab_allocs; volatile size_t _live_data; volatile size_t _critical_pins; HeapWord* volatile _update_watermark; + uint _age; + CENSUS_NOISE(uint _youth;) // tracks epochs of retrograde ageing (rejuvenation) + public: ShenandoahHeapRegion(HeapWord* start, size_t index, bool committed); @@ -327,8 +345,19 @@ class ShenandoahHeapRegion { return _index; } - // Allocation (return null if full) - inline HeapWord* allocate(size_t word_size, ShenandoahAllocRequest::Type type); + inline void save_top_before_promote(); + inline HeapWord* get_top_before_promote() const { return _top_before_promoted; } + inline void restore_top_before_promote(); + inline size_t garbage_before_padded_for_promote() const; + + // If next available memory is not aligned on address that is multiple of alignment, fill the empty space + // so that returned object is aligned on an address that is a multiple of alignment_in_bytes. Requested + // size is in words. It is assumed that this->is_old(). A pad object is allocated, filled, and registered + // if necessary to assure the new allocation is properly aligned. Return nullptr if memory is not available. + inline HeapWord* allocate_aligned(size_t word_size, ShenandoahAllocRequest &req, size_t alignment_in_bytes); + + // Allocation (return nullptr if full) + inline HeapWord* allocate(size_t word_size, const ShenandoahAllocRequest& req); inline void clear_live_data(); void set_live_data(size_t s); @@ -349,7 +378,36 @@ class ShenandoahHeapRegion { void recycle(); - void oop_iterate(OopIterateClosure* cl); + inline void begin_preemptible_coalesce_and_fill() { + _coalesce_and_fill_boundary = _bottom; + } + + inline void end_preemptible_coalesce_and_fill() { + _coalesce_and_fill_boundary = _end; + } + + inline void suspend_coalesce_and_fill(HeapWord* next_focus) { + _coalesce_and_fill_boundary = next_focus; + } + + inline HeapWord* resume_coalesce_and_fill() { + return _coalesce_and_fill_boundary; + } + + // Coalesce contiguous spans of garbage objects by filling header and registering start locations with remembered set. + // This is used by old-gen GC following concurrent marking to make old-gen HeapRegions parsable. Old regions must be + // parsable because the mark bitmap is not reliable during the concurrent old mark. + // Return true iff region is completely coalesced and filled. Returns false if cancelled before task is complete. + bool oop_coalesce_and_fill(bool cancellable); + + // Invoke closure on every reference contained within the humongous object that spans this humongous + // region if the reference is contained within a DIRTY card and the reference is no more than words following + // start within the humongous object. + void oop_iterate_humongous_slice_dirty(OopIterateClosure* cl, HeapWord* start, size_t words, bool write_table) const; + + // Invoke closure on every reference contained within the humongous object starting from start and + // ending at start + words. + void oop_iterate_humongous_slice_all(OopIterateClosure* cl, HeapWord* start, size_t words) const; HeapWord* block_start(const void* p) const; size_t block_size(const HeapWord* p) const; @@ -369,25 +427,54 @@ class ShenandoahHeapRegion { size_t capacity() const { return byte_size(bottom(), end()); } size_t used() const { return byte_size(bottom(), top()); } + size_t used_before_promote() const { return byte_size(bottom(), get_top_before_promote()); } size_t free() const { return byte_size(top(), end()); } + // Does this region contain this address? + bool contains(HeapWord* p) const { + return (bottom() <= p) && (p < top()); + } + inline void adjust_alloc_metadata(ShenandoahAllocRequest::Type type, size_t); void reset_alloc_metadata(); size_t get_shared_allocs() const; size_t get_tlab_allocs() const; size_t get_gclab_allocs() const; + size_t get_plab_allocs() const; inline HeapWord* get_update_watermark() const; inline void set_update_watermark(HeapWord* w); inline void set_update_watermark_at_safepoint(HeapWord* w); + inline ShenandoahAffiliation affiliation() const; + inline const char* affiliation_name() const; + + void set_affiliation(ShenandoahAffiliation new_affiliation); + + // Region ageing and rejuvenation + uint age() const { return _age; } + CENSUS_NOISE(uint youth() const { return _youth; }) + + void increment_age() { + const uint max_age = markWord::max_age; + assert(_age <= max_age, "Error"); + if (_age++ >= max_age) { + _age = max_age; // clamp + } + } + + void reset_age() { + CENSUS_NOISE(_youth += _age;) + _age = 0; + } + + CENSUS_NOISE(void clear_youth() { _youth = 0; }) + private: + void decrement_humongous_waste() const; void do_commit(); void do_uncommit(); - void oop_iterate_objects(OopIterateClosure* cl); - void oop_iterate_humongous(OopIterateClosure* cl); - inline void internal_increase_live_data(size_t s); void set_state(RegionState to); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp index 0435333fe1e..382d9ba942c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,20 +26,74 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_INLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_INLINE_HPP +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" - #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" #include "runtime/atomic.hpp" -HeapWord* ShenandoahHeapRegion::allocate(size_t size, ShenandoahAllocRequest::Type type) { +HeapWord* ShenandoahHeapRegion::allocate_aligned(size_t size, ShenandoahAllocRequest &req, size_t alignment_in_bytes) { + shenandoah_assert_heaplocked_or_safepoint(); + assert(req.is_lab_alloc(), "allocate_aligned() only applies to LAB allocations"); + assert(is_object_aligned(size), "alloc size breaks alignment: " SIZE_FORMAT, size); + assert(is_old(), "aligned allocations are only taken from OLD regions to support PLABs"); + assert(is_aligned(alignment_in_bytes, HeapWordSize), "Expect heap word alignment"); + + HeapWord* orig_top = top(); + size_t alignment_in_words = alignment_in_bytes / HeapWordSize; + + // unalignment_words is the amount by which current top() exceeds the desired alignment point. We subtract this amount + // from alignment_in_words to determine padding required to next alignment point. + + HeapWord* aligned_obj = (HeapWord*) align_up(orig_top, alignment_in_bytes); + size_t pad_words = aligned_obj - orig_top; + if ((pad_words > 0) && (pad_words < ShenandoahHeap::min_fill_size())) { + pad_words += alignment_in_words; + aligned_obj += alignment_in_words; + } + + if (pointer_delta(end(), aligned_obj) < size) { + // Shrink size to fit within available space and align it + size = pointer_delta(end(), aligned_obj); + size = align_down(size, alignment_in_words); + } + + // Both originally requested size and adjusted size must be properly aligned + assert (is_aligned(size, alignment_in_words), "Size must be multiple of alignment constraint"); + if (size >= req.min_size()) { + // Even if req.min_size() may not be a multiple of card size, we know that size is. + if (pad_words > 0) { + assert(pad_words >= ShenandoahHeap::min_fill_size(), "pad_words expanded above to meet size constraint"); + ShenandoahHeap::fill_with_object(orig_top, pad_words); + ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->register_object(orig_top); + } + + make_regular_allocation(req.affiliation()); + adjust_alloc_metadata(req.type(), size); + + HeapWord* new_top = aligned_obj + size; + assert(new_top <= end(), "PLAB cannot span end of heap region"); + set_top(new_top); + // We do not req.set_actual_size() here. The caller sets it. + req.set_waste(pad_words); + assert(is_object_aligned(new_top), "new top breaks alignment: " PTR_FORMAT, p2i(new_top)); + assert(is_aligned(aligned_obj, alignment_in_bytes), "obj is not aligned: " PTR_FORMAT, p2i(aligned_obj)); + return aligned_obj; + } else { + // The aligned size that fits in this region is smaller than min_size, so don't align top and don't allocate. Return failure. + return nullptr; + } +} + +HeapWord* ShenandoahHeapRegion::allocate(size_t size, const ShenandoahAllocRequest& req) { shenandoah_assert_heaplocked_or_safepoint(); assert(is_object_aligned(size), "alloc size breaks alignment: " SIZE_FORMAT, size); HeapWord* obj = top(); if (pointer_delta(end(), obj) >= size) { - make_regular_allocation(); - adjust_alloc_metadata(type, size); + make_regular_allocation(req.affiliation()); + adjust_alloc_metadata(req.type(), size); HeapWord* new_top = obj + size; set_top(new_top); @@ -64,6 +119,9 @@ inline void ShenandoahHeapRegion::adjust_alloc_metadata(ShenandoahAllocRequest:: case ShenandoahAllocRequest::_alloc_gclab: _gclab_allocs += size; break; + case ShenandoahAllocRequest::_alloc_plab: + _plab_allocs += size; + break; default: ShouldNotReachHere(); } @@ -82,12 +140,6 @@ inline void ShenandoahHeapRegion::increase_live_data_gc_words(size_t s) { inline void ShenandoahHeapRegion::internal_increase_live_data(size_t s) { size_t new_live_data = Atomic::add(&_live_data, s, memory_order_relaxed); -#ifdef ASSERT - size_t live_bytes = new_live_data * HeapWordSize; - size_t used_bytes = used(); - assert(live_bytes <= used_bytes, - "can't have more live data than used: " SIZE_FORMAT ", " SIZE_FORMAT, live_bytes, used_bytes); -#endif } inline void ShenandoahHeapRegion::clear_live_data() { @@ -115,6 +167,17 @@ inline size_t ShenandoahHeapRegion::garbage() const { return result; } +inline size_t ShenandoahHeapRegion::garbage_before_padded_for_promote() const { + assert(get_top_before_promote() != nullptr, "top before promote should not equal null"); + size_t used_before_promote = byte_size(bottom(), get_top_before_promote()); + assert(used_before_promote >= get_live_data_bytes(), + "Live Data must be a subset of used before promotion live: " SIZE_FORMAT " used: " SIZE_FORMAT, + get_live_data_bytes(), used_before_promote); + size_t result = used_before_promote - get_live_data_bytes(); + return result; + +} + inline HeapWord* ShenandoahHeapRegion::get_update_watermark() const { HeapWord* watermark = Atomic::load_acquire(&_update_watermark); assert(bottom() <= watermark && watermark <= top(), "within bounds"); @@ -133,4 +196,34 @@ inline void ShenandoahHeapRegion::set_update_watermark_at_safepoint(HeapWord* w) _update_watermark = w; } +inline ShenandoahAffiliation ShenandoahHeapRegion::affiliation() const { + return ShenandoahHeap::heap()->region_affiliation(this); +} + +inline const char* ShenandoahHeapRegion::affiliation_name() const { + return shenandoah_affiliation_name(affiliation()); +} + +inline bool ShenandoahHeapRegion::is_young() const { + return affiliation() == YOUNG_GENERATION; +} + +inline bool ShenandoahHeapRegion::is_old() const { + return affiliation() == OLD_GENERATION; +} + +inline bool ShenandoahHeapRegion::is_affiliated() const { + return affiliation() != FREE; +} + +inline void ShenandoahHeapRegion::save_top_before_promote() { + _top_before_promoted = _top; +} + +inline void ShenandoahHeapRegion::restore_top_before_promote() { + _top = _top_before_promoted; + _top_before_promoted = nullptr; + } + + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp new file mode 100644 index 00000000000..3d3483a5b69 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" +#include "gc/shenandoah/shenandoahMarkingContext.hpp" +#include "gc/shenandoah/shenandoahSharedVariables.hpp" + +ShenandoahSynchronizePinnedRegionStates::ShenandoahSynchronizePinnedRegionStates() : + _lock(ShenandoahHeap::heap()->lock()) { } + +void ShenandoahSynchronizePinnedRegionStates::heap_region_do(ShenandoahHeapRegion* r) { + // Drop "pinned" state from regions that no longer have a pinned count. Put + // regions with a pinned count into the "pinned" state. + if (r->is_active()) { + synchronize_pin_count(r); + } +} + +void ShenandoahSynchronizePinnedRegionStates::synchronize_pin_count(ShenandoahHeapRegion* r) { + if (r->is_pinned()) { + if (r->pin_count() == 0) { + ShenandoahHeapLocker locker(_lock); + r->make_unpinned(); + } + } else { + if (r->pin_count() > 0) { + ShenandoahHeapLocker locker(_lock); + r->make_pinned(); + } + } +} + +ShenandoahFinalMarkUpdateRegionStateClosure::ShenandoahFinalMarkUpdateRegionStateClosure(ShenandoahMarkingContext *ctx) : + _ctx(ctx) { } + +void ShenandoahFinalMarkUpdateRegionStateClosure::heap_region_do(ShenandoahHeapRegion* r) { + if (r->is_active()) { + if (_ctx != nullptr) { + // _ctx may be null when this closure is used to sync only the pin status + // update the watermark of old regions. For old regions we cannot reset + // the TAMS because we rely on that to keep promoted objects alive after + // old marking is complete. + + // All allocations past TAMS are implicitly live, adjust the region data. + // Bitmaps/TAMS are swapped at this point, so we need to poll complete bitmap. + HeapWord *tams = _ctx->top_at_mark_start(r); + HeapWord *top = r->top(); + if (top > tams) { + r->increase_live_data_alloc_words(pointer_delta(top, tams)); + } + } + + // We are about to select the collection set, make sure it knows about + // current pinning status. Also, this allows trashing more regions that + // now have their pinning status dropped. + _pins.synchronize_pin_count(r); + + // Remember limit for updating refs. It's guaranteed that we get no + // from-space-refs written from here on. + r->set_update_watermark_at_safepoint(r->top()); + } else { + assert(!r->has_live(), "Region " SIZE_FORMAT " should have no live data", r->index()); + assert(_ctx == nullptr || _ctx->top_at_mark_start(r) == r->top(), + "Region " SIZE_FORMAT " should have correct TAMS", r->index()); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp new file mode 100644 index 00000000000..0daf268628c --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCLOSURES_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCLOSURES_HPP + + +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" + +// Applies the given closure to all regions with the given affiliation +template +class ShenandoahIncludeRegionClosure : public ShenandoahHeapRegionClosure { +private: + ShenandoahHeapRegionClosure* _closure; + +public: + explicit ShenandoahIncludeRegionClosure(ShenandoahHeapRegionClosure* closure): _closure(closure) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + if (r->affiliation() == AFFILIATION) { + _closure->heap_region_do(r); + } + } + + bool is_thread_safe() override { + return _closure->is_thread_safe(); + } +}; + +// Applies the given closure to all regions without the given affiliation +template +class ShenandoahExcludeRegionClosure : public ShenandoahHeapRegionClosure { +private: + ShenandoahHeapRegionClosure* _closure; + +public: + explicit ShenandoahExcludeRegionClosure(ShenandoahHeapRegionClosure* closure): _closure(closure) {} + + void heap_region_do(ShenandoahHeapRegion* r) override { + if (r->affiliation() != AFFILIATION) { + _closure->heap_region_do(r); + } + } + + bool is_thread_safe() override { + return _closure->is_thread_safe(); + } +}; + +// Makes regions pinned or unpinned according to the region's pin count +class ShenandoahSynchronizePinnedRegionStates : public ShenandoahHeapRegionClosure { +private: + ShenandoahHeapLock* const _lock; + +public: + ShenandoahSynchronizePinnedRegionStates(); + + void heap_region_do(ShenandoahHeapRegion* r) override; + bool is_thread_safe() override { return true; } + + void synchronize_pin_count(ShenandoahHeapRegion* r); +}; + +class ShenandoahMarkingContext; + +// Synchronizes region pinned status, sets update watermark and adjust live data tally for regions +class ShenandoahFinalMarkUpdateRegionStateClosure : public ShenandoahHeapRegionClosure { +private: + ShenandoahMarkingContext* const _ctx; + ShenandoahSynchronizePinnedRegionStates _pins; +public: + explicit ShenandoahFinalMarkUpdateRegionStateClosure(ShenandoahMarkingContext* ctx); + + void heap_region_do(ShenandoahHeapRegion* r) override; + bool is_thread_safe() override { return true; } +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCLOSURES_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp index 3fb6b329f2c..73250cecd6f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,13 +24,17 @@ */ #include "precompiled.hpp" + +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahHeapRegionCounters.hpp" +#include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "runtime/atomic.hpp" #include "runtime/perfData.inline.hpp" +#include "utilities/defaultStream.hpp" ShenandoahHeapRegionCounters::ShenandoahHeapRegionCounters() : _last_sample_millis(0) @@ -49,6 +54,9 @@ ShenandoahHeapRegionCounters::ShenandoahHeapRegionCounters() : cname = PerfDataManager::counter_name(_name_space, "max_regions"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, num_regions, CHECK); + cname = PerfDataManager::counter_name(_name_space, "protocol_version"); + PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, VERSION_NUMBER, CHECK); + cname = PerfDataManager::counter_name(_name_space, "region_size"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_None, ShenandoahHeapRegion::region_size_bytes() >> 10, CHECK); @@ -57,14 +65,14 @@ ShenandoahHeapRegionCounters::ShenandoahHeapRegionCounters() : PerfData::U_None, CHECK); _regions_data = NEW_C_HEAP_ARRAY(PerfVariable*, num_regions, mtGC); + // Initializing performance data resources for each region for (uint i = 0; i < num_regions; i++) { const char* reg_name = PerfDataManager::name_space(_name_space, "region", i); const char* data_name = PerfDataManager::counter_name(reg_name, "data"); const char* ns = PerfDataManager::ns_to_string(SUN_GC); const char* fullname = PerfDataManager::counter_name(ns, data_name); assert(!PerfDataManager::exists(fullname), "must not exist"); - _regions_data[i] = PerfDataManager::create_long_variable(SUN_GC, data_name, - PerfData::U_None, CHECK); + _regions_data[i] = PerfDataManager::create_long_variable(SUN_GC, data_name, PerfData::U_None, CHECK); } } } @@ -73,27 +81,42 @@ ShenandoahHeapRegionCounters::~ShenandoahHeapRegionCounters() { if (_name_space != nullptr) FREE_C_HEAP_ARRAY(char, _name_space); } +void ShenandoahHeapRegionCounters::write_snapshot(PerfLongVariable** regions, + PerfLongVariable* ts, + PerfLongVariable* status, + size_t num_regions, + size_t region_size, size_t protocol_version) { + LogTarget(Trace, gc, region) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + + ls.print_cr(JLONG_FORMAT " " JLONG_FORMAT " " SIZE_FORMAT " " SIZE_FORMAT " " SIZE_FORMAT, + ts->get_value(), status->get_value(), num_regions, region_size, protocol_version); + if (num_regions > 0) { + ls.print(JLONG_FORMAT, regions[0]->get_value()); + } + for (uint i = 1; i < num_regions; ++i) { + ls.print(" " JLONG_FORMAT, regions[i]->get_value()); + } + ls.cr(); + } +} + void ShenandoahHeapRegionCounters::update() { if (ShenandoahRegionSampling) { jlong current = nanos_to_millis(os::javaTimeNanos()); jlong last = _last_sample_millis; - if (current - last > ShenandoahRegionSamplingRate && - Atomic::cmpxchg(&_last_sample_millis, last, current) == last) { + if (current - last > ShenandoahRegionSamplingRate && Atomic::cmpxchg(&_last_sample_millis, last, current) == last) { ShenandoahHeap* heap = ShenandoahHeap::heap(); - jlong status = 0; - if (heap->is_concurrent_mark_in_progress()) status |= 1 << 0; - if (heap->is_evacuation_in_progress()) status |= 1 << 1; - if (heap->is_update_refs_in_progress()) status |= 1 << 2; - _status->set_value(status); - + _status->set_value(encode_heap_status(heap)); _timestamp->set_value(os::elapsed_counter()); - size_t num_regions = heap->num_regions(); - { ShenandoahHeapLocker locker(heap->lock()); size_t rs = ShenandoahHeapRegion::region_size_bytes(); + size_t num_regions = heap->num_regions(); for (uint i = 0; i < num_regions; i++) { ShenandoahHeapRegion* r = heap->get_region(i); jlong data = 0; @@ -101,12 +124,79 @@ void ShenandoahHeapRegionCounters::update() { data |= ((100 * r->get_live_data_bytes() / rs) & PERCENT_MASK) << LIVE_SHIFT; data |= ((100 * r->get_tlab_allocs() / rs) & PERCENT_MASK) << TLAB_SHIFT; data |= ((100 * r->get_gclab_allocs() / rs) & PERCENT_MASK) << GCLAB_SHIFT; + data |= ((100 * r->get_plab_allocs() / rs) & PERCENT_MASK) << PLAB_SHIFT; data |= ((100 * r->get_shared_allocs() / rs) & PERCENT_MASK) << SHARED_SHIFT; + + data |= (r->age() & AGE_MASK) << AGE_SHIFT; + data |= (r->affiliation() & AFFILIATION_MASK) << AFFILIATION_SHIFT; data |= (r->state_ordinal() & STATUS_MASK) << STATUS_SHIFT; _regions_data[i]->set_value(data); } + + // If logging enabled, dump current region snapshot to log file + write_snapshot(_regions_data, _timestamp, _status, num_regions, rs >> 10, VERSION_NUMBER); } + } + } +} +static int encode_phase(ShenandoahHeap* heap) { + if (heap->is_evacuation_in_progress() || heap->is_full_gc_move_in_progress()) { + return 2; + } + if (heap->is_update_refs_in_progress() || heap->is_full_gc_move_in_progress()) { + return 3; + } + if (heap->is_concurrent_mark_in_progress() || heap->is_full_gc_in_progress()) { + return 1; + } + assert(heap->is_idle(), "What is it doing?"); + return 0; +} + +static int get_generation_shift(ShenandoahGeneration* generation) { + switch (generation->type()) { + case NON_GEN: + case GLOBAL: + return 0; + case OLD: + return 2; + case YOUNG: + return 4; + default: + ShouldNotReachHere(); + return -1; + } +} + +jlong ShenandoahHeapRegionCounters::encode_heap_status(ShenandoahHeap* heap) { + + if (heap->is_idle() && !heap->is_full_gc_in_progress()) { + return 0; + } + + jlong status = 0; + if (!heap->mode()->is_generational()) { + status = encode_phase(heap); + } else { + int phase = encode_phase(heap); + ShenandoahGeneration* generation = heap->active_generation(); + assert(generation != nullptr, "Expected active generation in this mode."); + int shift = get_generation_shift(generation); + status |= ((phase & 0x3) << shift); + if (heap->is_concurrent_old_mark_in_progress()) { + status |= (1 << 2); } + log_develop_trace(gc)("%s, phase=%u, old_mark=%s, status=" JLONG_FORMAT, + generation->name(), phase, BOOL_TO_STR(heap->is_concurrent_old_mark_in_progress()), status); } + + if (heap->is_degenerated_gc_in_progress()) { + status |= (1 << 6); + } + if (heap->is_full_gc_in_progress()) { + status |= (1 << 7); + } + + return status; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp index f0d4c2ad38f..c139980af41 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,6 +27,7 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCOUNTERS_HPP #include "memory/allocation.hpp" +#include "logging/logFileStreamOutput.hpp" /** * This provides the following in JVMStat: @@ -37,9 +39,14 @@ * * variables: * - sun.gc.shenandoah.regions.status current GC status: - * - bit 0 set when marking in progress - * - bit 1 set when evacuation in progress - * - bit 2 set when update refs in progress + * | global | old | young | mode | + * | 0..1 | 2..3 | 4..5 | 6..7 | + * + * For each generation: + * 0 = idle, 1 = marking, 2 = evacuating, 3 = updating refs + * + * For mode: + * 0 = concurrent, 1 = degenerated, 2 = full * * two variable counters per region, with $max_regions (see above) counters: * - sun.gc.shenandoah.regions.region.$i.data @@ -51,24 +58,31 @@ * - bits 14-20 tlab allocated memory in percent * - bits 21-27 gclab allocated memory in percent * - bits 28-34 shared allocated memory in percent - * - bits 35-41 + * - bits 35-41 plab allocated memory in percent * - bits 42-50 - * - bits 51-57 + * - bits 51-55 age + * - bits 56-57 affiliation: 0 = free, young = 1, old = 2 * - bits 58-63 status * - bits describe the state as recorded in ShenandoahHeapRegion */ class ShenandoahHeapRegionCounters : public CHeapObj { private: - static const jlong PERCENT_MASK = 0x7f; - static const jlong STATUS_MASK = 0x3f; + static const jlong PERCENT_MASK = 0x7f; + static const jlong AGE_MASK = 0x1f; + static const jlong AFFILIATION_MASK = 0x03; + static const jlong STATUS_MASK = 0x3f; - static const jlong USED_SHIFT = 0; - static const jlong LIVE_SHIFT = 7; - static const jlong TLAB_SHIFT = 14; - static const jlong GCLAB_SHIFT = 21; - static const jlong SHARED_SHIFT = 28; + static const jlong USED_SHIFT = 0; + static const jlong LIVE_SHIFT = 7; + static const jlong TLAB_SHIFT = 14; + static const jlong GCLAB_SHIFT = 21; + static const jlong SHARED_SHIFT = 28; + static const jlong PLAB_SHIFT = 35; + static const jlong AGE_SHIFT = 51; + static const jlong AFFILIATION_SHIFT = 56; + static const jlong STATUS_SHIFT = 58; - static const jlong STATUS_SHIFT = 58; + static const jlong VERSION_NUMBER = 2; char* _name_space; PerfLongVariable** _regions_data; @@ -76,10 +90,19 @@ class ShenandoahHeapRegionCounters : public CHeapObj { PerfLongVariable* _status; volatile jlong _last_sample_millis; + void write_snapshot(PerfLongVariable** regions, + PerfLongVariable* ts, + PerfLongVariable* status, + size_t num_regions, + size_t region_size, size_t protocolVersion); + public: ShenandoahHeapRegionCounters(); ~ShenandoahHeapRegionCounters(); void update(); + +private: + static jlong encode_heap_status(ShenandoahHeap* heap) ; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCOUNTERS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp index 775b84a8966..686295c3e1b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -27,16 +28,13 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" -ShenandoahMark::ShenandoahMark() : - _task_queues(ShenandoahHeap::heap()->marking_context()->task_queues()) { -} - void ShenandoahMark::start_mark() { if (!CodeCache::is_gc_marking_cycle_active()) { CodeCache::on_gc_marking_cycle_start(); @@ -46,34 +44,34 @@ void ShenandoahMark::start_mark() { void ShenandoahMark::end_mark() { // Unlike other GCs, we do not arm the nmethods // when marking terminates. - CodeCache::on_gc_marking_cycle_finish(); + if (!ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress()) { + CodeCache::on_gc_marking_cycle_finish(); + } } -void ShenandoahMark::clear() { - // Clean up marking stacks. - ShenandoahObjToScanQueueSet* queues = ShenandoahHeap::heap()->marking_context()->task_queues(); - queues->clear(); - - // Cancel SATB buffers. - ShenandoahBarrierSet::satb_mark_queue_set().abandon_partial_marking(); +ShenandoahMark::ShenandoahMark(ShenandoahGeneration* generation) : + _generation(generation), + _task_queues(generation->task_queues()), + _old_gen_task_queues(generation->old_gen_task_queues()) { } template -void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahReferenceProcessor *rp, StringDedup::Requests* const req) { +void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahReferenceProcessor *rp, StringDedup::Requests* const req, bool update_refs) { ShenandoahObjToScanQueue* q = get_queue(w); + ShenandoahObjToScanQueue* old_q = get_old_queue(w); ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahLiveData* ld = heap->get_liveness_cache(w); // TODO: We can clean up this if we figure out how to do templated oop closures that // play nice with specialized_oop_iterators. - if (heap->has_forwarded_objects()) { + if (update_refs) { using Closure = ShenandoahMarkUpdateRefsClosure; - Closure cl(q, rp); + Closure cl(q, rp, old_q); mark_loop_work(&cl, ld, w, t, req); } else { using Closure = ShenandoahMarkRefsClosure; - Closure cl(q, rp); + Closure cl(q, rp, old_q); mark_loop_work(&cl, ld, w, t, req); } @@ -83,12 +81,29 @@ void ShenandoahMark::mark_loop_prework(uint w, TaskTerminator *t, ShenandoahRefe template void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, ShenandoahGenerationType generation, StringDedup::Requests* const req) { - mark_loop_prework(worker_id, terminator, rp, req); + bool update_refs = ShenandoahHeap::heap()->has_forwarded_objects(); + switch (generation) { + case YOUNG: + mark_loop_prework(worker_id, terminator, rp, req, update_refs); + break; + case OLD: + // Old generation collection only performs marking, it should not update references. + mark_loop_prework(worker_id, terminator, rp, req, false); + break; + case GLOBAL: + mark_loop_prework(worker_id, terminator, rp, req, update_refs); + break; + case NON_GEN: + mark_loop_prework(worker_id, terminator, rp, req, update_refs); + break; + default: + ShouldNotReachHere(); + break; + } } void ShenandoahMark::mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, - ShenandoahGenerationType generation, bool cancellable, StringDedupMode dedup_mode, - StringDedup::Requests* const req) { + ShenandoahGenerationType generation, bool cancellable, StringDedupMode dedup_mode, StringDedup::Requests* const req) { if (cancellable) { switch(dedup_mode) { case NO_DEDUP: @@ -125,7 +140,12 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w ShenandoahObjToScanQueue* q; ShenandoahMarkTask t; - heap->ref_processor()->set_mark_closure(worker_id, cl); + // Do not use active_generation() : we must use the gc_generation() set by + // ShenandoahGCScope on the ControllerThread's stack; no safepoint may + // intervene to update active_generation, so we can't + // shenandoah_assert_generations_reconciled() here. + assert(heap->gc_generation()->type() == GENERATION, "Sanity: %d != %d", heap->gc_generation()->type(), GENERATION); + heap->gc_generation()->ref_processor()->set_mark_closure(worker_id, cl); /* * Process outstanding queues, if any. @@ -145,7 +165,7 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w for (uint i = 0; i < stride; i++) { if (q->pop(t)) { - do_task(q, cl, live_data, req, &t); + do_task(q, cl, live_data, req, &t, worker_id); } else { assert(q->is_empty(), "Must be empty"); q = queues->claim_next(); @@ -154,8 +174,9 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w } } q = get_queue(worker_id); + ShenandoahObjToScanQueue* old_q = get_old_queue(worker_id); - ShenandoahSATBBufferClosure drain_satb(q); + ShenandoahSATBBufferClosure drain_satb(q, old_q); SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); /* @@ -165,7 +186,6 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w if (CANCELLABLE && heap->check_cancelled_gc_and_yield()) { return; } - while (satb_mq_set.completed_buffers_num() > 0) { satb_mq_set.apply_closure_to_completed_buffer(&drain_satb); } @@ -174,7 +194,7 @@ void ShenandoahMark::mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint w for (uint i = 0; i < stride; i++) { if (q->pop(t) || queues->steal(worker_id, t)) { - do_task(q, cl, live_data, req, &t); + do_task(q, cl, live_data, req, &t, worker_id); work++; } else { break; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index fab913bfd94..ae8d52a3d0e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,10 +26,12 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARK_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHMARK_HPP +#include "gc/shared/ageTable.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/taskTerminator.hpp" #include "gc/shenandoah/shenandoahGenerationType.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" enum StringDedupMode { @@ -45,16 +48,16 @@ class ShenandoahReferenceProcessor; // maintained by task queues, mark bitmap and SATB buffers (concurrent mark) class ShenandoahMark: public StackObj { protected: + ShenandoahGeneration* const _generation; ShenandoahObjToScanQueueSet* const _task_queues; + ShenandoahObjToScanQueueSet* const _old_gen_task_queues; protected: - ShenandoahMark(); + ShenandoahMark(ShenandoahGeneration* generation); public: template - static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); - - static void clear(); + static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); // Loom support void start_mark(); @@ -62,12 +65,20 @@ class ShenandoahMark: public StackObj { // Helpers inline ShenandoahObjToScanQueueSet* task_queues() const; + ShenandoahObjToScanQueueSet* old_task_queues() { + return _old_gen_task_queues; + } + inline ShenandoahObjToScanQueue* get_queue(uint index) const; + inline ShenandoahObjToScanQueue* get_old_queue(uint index) const; + + inline ShenandoahGeneration* generation() { return _generation; }; -// ---------- Marking loop and tasks private: +// ---------- Marking loop and tasks + template - inline void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task); + inline void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); template inline void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, bool weak); @@ -76,13 +87,23 @@ class ShenandoahMark: public StackObj { inline void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); template - inline void count_liveness(ShenandoahLiveData* live_data, oop obj); + inline void count_liveness(ShenandoahLiveData* live_data, oop obj, uint worker_id); template void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); template - void mark_loop_prework(uint worker_id, TaskTerminator *terminator, ShenandoahReferenceProcessor *rp, StringDedup::Requests* const req); + void mark_loop_prework(uint worker_id, TaskTerminator *terminator, ShenandoahReferenceProcessor *rp, StringDedup::Requests* const req, bool update_refs); + + template + static bool in_generation(ShenandoahHeap* const heap, oop obj); + + template + static void mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); + + static void mark_ref(ShenandoahObjToScanQueue* q, + ShenandoahMarkingContext* const mark_context, + bool weak, oop obj); template inline void dedup_string(oop obj, StringDedup::Requests* const req); @@ -90,6 +111,7 @@ class ShenandoahMark: public StackObj { template void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, ShenandoahGenerationType generation, StringDedup::Requests* const req); + void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahReferenceProcessor *rp, ShenandoahGenerationType generation, bool cancellable, StringDedupMode dedup_mode, StringDedup::Requests* const req); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 2eca17bde27..0239f961c65 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -28,11 +29,14 @@ #include "gc/shenandoah/shenandoahMark.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" +#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.inline.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" @@ -58,7 +62,7 @@ void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { } template -void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task) { +void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id) { oop obj = task->obj(); shenandoah_assert_not_forwarded(nullptr, obj); @@ -95,7 +99,7 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD // Avoid double-counting objects that are visited twice due to upgrade // from final- to strong mark. if (task->count_liveness()) { - count_liveness(live_data, obj); + count_liveness(live_data, obj, worker_id); } } else { // Case 4: Array chunk, has sensible chunk id. Process it. @@ -104,14 +108,27 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD } template -inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj) { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - size_t region_idx = heap->heap_region_index_containing(obj); - ShenandoahHeapRegion* region = heap->get_region(region_idx); - size_t size = obj->size(); +inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, uint worker_id) { + const ShenandoahHeap* const heap = ShenandoahHeap::heap(); + const size_t region_idx = heap->heap_region_index_containing(obj); + ShenandoahHeapRegion* const region = heap->get_region(region_idx); + const size_t size = obj->size(); + + // Age census for objects in the young generation + if (GENERATION == YOUNG || (GENERATION == GLOBAL && region->is_young())) { + assert(heap->mode()->is_generational(), "Only if generational"); + if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) { + assert(region->is_young(), "Only for young objects"); + uint age = ShenandoahHeap::get_object_age(obj); + ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census(); + CENSUS_NOISE(census->add(age, region->age(), region->youth(), size, worker_id);) + NO_CENSUS_NOISE(census->add(age, region->age(), size, worker_id);) + } + } if (!region->is_humongous_start()) { assert(!region->is_humongous(), "Cannot have continuations here"); + assert(region->is_affiliated(), "Do not count live data within Free Regular Region " SIZE_FORMAT, region_idx); ShenandoahLiveData cur = live_data[region_idx]; size_t new_val = size + cur; if (new_val >= SHENANDOAH_LIVEDATA_MAX) { @@ -126,9 +143,11 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob shenandoah_assert_in_correct_region(nullptr, obj); size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); + assert(region->is_affiliated(), "Do not count live data within FREE Humongous Start Region " SIZE_FORMAT, region_idx); for (size_t i = region_idx; i < region_idx + num_regions; i++) { ShenandoahHeapRegion* chain_reg = heap->get_region(i); assert(chain_reg->is_humongous(), "Expecting a humongous region"); + assert(chain_reg->is_affiliated(), "Do not count live data within FREE Humongous Continuation Region " SIZE_FORMAT, i); chain_reg->increase_live_data_gc_words(chain_reg->used() >> LogHeapWordSize); } } @@ -235,50 +254,121 @@ template class ShenandoahSATBBufferClosure : public SATBBufferClosure { private: ShenandoahObjToScanQueue* _queue; + ShenandoahObjToScanQueue* _old_queue; ShenandoahHeap* _heap; ShenandoahMarkingContext* const _mark_context; public: - ShenandoahSATBBufferClosure(ShenandoahObjToScanQueue* q) : + ShenandoahSATBBufferClosure(ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q) : _queue(q), + _old_queue(old_q), _heap(ShenandoahHeap::heap()), _mark_context(_heap->marking_context()) { } void do_buffer(void **buffer, size_t size) { - assert(size == 0 || !_heap->has_forwarded_objects(), "Forwarded objects are not expected here"); + assert(size == 0 || !_heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded objects are not expected here"); for (size_t i = 0; i < size; ++i) { oop *p = (oop *) &buffer[i]; - ShenandoahMark::mark_through_ref(p, _queue, _mark_context, false); + ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, false); } } }; +template +bool ShenandoahMark::in_generation(ShenandoahHeap* const heap, oop obj) { + // Each in-line expansion of in_generation() resolves GENERATION at compile time. + if (GENERATION == YOUNG) { + return heap->is_in_young(obj); + } + + if (GENERATION == OLD) { + return heap->is_in_old(obj); + } + + assert((GENERATION == GLOBAL || GENERATION == NON_GEN), "Unexpected generation type"); + assert(heap->is_in(obj), "Object must be in heap"); + return true; +} + template -inline void ShenandoahMark::mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { +inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { + // Note: This is a very hot code path, so the code should be conditional on GENERATION template + // parameter where possible, in order to generate the most efficient code. + T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + shenandoah_assert_not_forwarded(p, obj); + shenandoah_assert_not_in_cset_except(p, obj, heap->cancelled_gc()); + if (in_generation(heap, obj)) { + mark_ref(q, mark_context, weak, obj); + shenandoah_assert_marked(p, obj); + if (GENERATION == YOUNG && heap->is_in_old(p)) { + // Mark card as dirty because remembered set scanning still finds interesting pointer. + heap->old_generation()->mark_card_as_dirty((HeapWord*)p); + } else if (GENERATION == GLOBAL && heap->is_in_old(p) && heap->is_in_young(obj)) { + // Mark card as dirty because GLOBAL marking finds interesting pointer. + heap->old_generation()->mark_card_as_dirty((HeapWord*)p); + } + } else if (old_q != nullptr) { + // Young mark, bootstrapping old_q or concurrent with old_q marking. + mark_ref(old_q, mark_context, weak, obj); + shenandoah_assert_marked(p, obj); + } else if (GENERATION == OLD) { + // Old mark, found a young pointer. + if (heap->is_in(p)) { + assert(heap->is_in_young(obj), "Expected young object."); + heap->old_generation()->mark_card_as_dirty(p); + } + } + } +} + +template<> +inline void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { + mark_non_generational_ref(p, q, mark_context, weak); +} + +template<> +inline void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { + mark_non_generational_ref(p, q, mark_context, weak); +} + +template +inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, + ShenandoahMarkingContext* const mark_context, bool weak) { + oop o = RawAccess<>::oop_load(p); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + shenandoah_assert_not_forwarded(p, obj); shenandoah_assert_not_in_cset_except(p, obj, ShenandoahHeap::heap()->cancelled_gc()); - bool skip_live = false; - bool marked; - if (weak) { - marked = mark_context->mark_weak(obj); - } else { - marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); - } - if (marked) { - bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); - assert(pushed, "overflow queue should always succeed pushing"); - } + mark_ref(q, mark_context, weak, obj); shenandoah_assert_marked(p, obj); } } +inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, + ShenandoahMarkingContext* const mark_context, + bool weak, oop obj) { + bool skip_live = false; + bool marked; + if (weak) { + marked = mark_context->mark_weak(obj); + } else { + marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); + } + if (marked) { + bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); + assert(pushed, "overflow queue should always succeed pushing"); + } +} + ShenandoahObjToScanQueueSet* ShenandoahMark::task_queues() const { return _task_queues; } @@ -286,4 +376,12 @@ ShenandoahObjToScanQueueSet* ShenandoahMark::task_queues() const { ShenandoahObjToScanQueue* ShenandoahMark::get_queue(uint index) const { return _task_queues->queue(index); } + +ShenandoahObjToScanQueue* ShenandoahMark::get_old_queue(uint index) const { + if (_old_gen_task_queues != nullptr) { + return _old_gen_task_queues->queue(index); + } + return nullptr; +} + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARK_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp index fa2e15a98c7..dcd6dd49f93 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * Copyright Amazon.com Inc. 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 @@ -43,9 +44,32 @@ size_t ShenandoahMarkBitMap::mark_distance() { return MinObjAlignmentInBytes * BitsPerByte / 2; } +bool ShenandoahMarkBitMap::is_bitmap_clear_range(const HeapWord* start, const HeapWord* end) const { + // Similar to get_next_marked_addr(), without assertion. + // Round addr up to a possible object boundary to be safe. + if (start == end) { + return true; + } + size_t const addr_offset = address_to_index(align_up(start, HeapWordSize << LogMinObjAlignment)); + size_t const limit_offset = address_to_index(end); + size_t const next_offset = get_next_one_offset(addr_offset, limit_offset); + HeapWord* result = index_to_address(next_offset); + return (result == end); +} + + HeapWord* ShenandoahMarkBitMap::get_next_marked_addr(const HeapWord* addr, const HeapWord* limit) const { +#ifdef ASSERT + ShenandoahHeap* heap = ShenandoahHeap::heap(); + ShenandoahHeapRegion* r = heap->heap_region_containing(addr); + ShenandoahMarkingContext* ctx = heap->marking_context(); + HeapWord* tams = ctx->top_at_mark_start(r); assert(limit != nullptr, "limit must not be null"); + assert(limit <= r->top(), "limit must be less than top"); + assert(addr <= tams, "addr must be less than TAMS"); +#endif + // Round addr up to a possible object boundary to be safe. size_t const addr_offset = address_to_index(align_up(addr, HeapWordSize << LogMinObjAlignment)); size_t const limit_offset = address_to_index(limit); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp index 06ea6a8e232..f7cb3478ea9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. and/or its affiliates. + * Copyright Amazon.com Inc. 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 @@ -159,6 +160,8 @@ class ShenandoahMarkBitMap { inline bool is_marked_strong(HeapWord* w) const; inline bool is_marked_weak(HeapWord* addr) const; + bool is_bitmap_clear_range(const HeapWord* start, const HeapWord* end) const; + // Return the address corresponding to the next marked bit at or after // "addr", and before "limit", if "limit" is non-null. If there is no // such bit, returns "limit" if that is non-null, or else "endWord()". diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp index eaed74ceeb5..ded9fbd97f5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,31 +26,14 @@ #include "precompiled.hpp" #include "gc/shared/markBitMap.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.hpp" -#include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" -#include "utilities/stack.inline.hpp" -ShenandoahMarkingContext::ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions, uint max_queues) : +ShenandoahMarkingContext::ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions) : _mark_bit_map(heap_region, bitmap_region), _top_bitmaps(NEW_C_HEAP_ARRAY(HeapWord*, num_regions, mtGC)), _top_at_mark_starts_base(NEW_C_HEAP_ARRAY(HeapWord*, num_regions, mtGC)), _top_at_mark_starts(_top_at_mark_starts_base - - ((uintx) heap_region.start() >> ShenandoahHeapRegion::region_size_bytes_shift())), - _task_queues(new ShenandoahObjToScanQueueSet(max_queues)) { - assert(max_queues > 0, "At least one queue"); - for (uint i = 0; i < max_queues; ++i) { - ShenandoahObjToScanQueue* task_queue = new ShenandoahObjToScanQueue(); - _task_queues->register_queue(i, task_queue); - } -} - -ShenandoahMarkingContext::~ShenandoahMarkingContext() { - for (uint i = 0; i < _task_queues->size(); ++i) { - ShenandoahObjToScanQueue* q = _task_queues->queue(i); - delete q; - } - delete _task_queues; + ((uintx) heap_region.start() >> ShenandoahHeapRegion::region_size_bytes_shift())) { } bool ShenandoahMarkingContext::is_bitmap_clear() const { @@ -57,32 +41,59 @@ bool ShenandoahMarkingContext::is_bitmap_clear() const { size_t num_regions = heap->num_regions(); for (size_t idx = 0; idx < num_regions; idx++) { ShenandoahHeapRegion* r = heap->get_region(idx); - if (heap->is_bitmap_slice_committed(r) && !is_bitmap_clear_range(r->bottom(), r->end())) { + if (r->is_affiliated() && heap->is_bitmap_slice_committed(r) + && !is_bitmap_range_within_region_clear(r->bottom(), r->end())) { return false; } } return true; } -bool ShenandoahMarkingContext::is_bitmap_clear_range(HeapWord* start, HeapWord* end) const { - return _mark_bit_map.get_next_marked_addr(start, end) == end; +bool ShenandoahMarkingContext::is_bitmap_range_within_region_clear(const HeapWord* start, const HeapWord* end) const { + assert(start <= end, "Invalid start " PTR_FORMAT " and end " PTR_FORMAT, p2i(start), p2i(end)); + if (start < end) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + size_t start_idx = heap->heap_region_index_containing(start); +#ifdef ASSERT + size_t end_idx = heap->heap_region_index_containing(end - 1); + assert(start_idx == end_idx, "Expected range to be within same region (" SIZE_FORMAT ", " SIZE_FORMAT ")", start_idx, end_idx); +#endif + ShenandoahHeapRegion* r = heap->get_region(start_idx); + if (!heap->is_bitmap_slice_committed(r)) { + return true; + } + } + return _mark_bit_map.is_bitmap_clear_range(start, end); } void ShenandoahMarkingContext::initialize_top_at_mark_start(ShenandoahHeapRegion* r) { size_t idx = r->index(); HeapWord *bottom = r->bottom(); + _top_at_mark_starts_base[idx] = bottom; _top_bitmaps[idx] = bottom; + + log_debug(gc)("SMC:initialize_top_at_mark_start for Region " SIZE_FORMAT ", TAMS: " PTR_FORMAT ", TopOfBitMap: " PTR_FORMAT, + r->index(), p2i(bottom), p2i(r->end())); +} + +HeapWord* ShenandoahMarkingContext::top_bitmap(ShenandoahHeapRegion* r) { + return _top_bitmaps[r->index()]; } void ShenandoahMarkingContext::clear_bitmap(ShenandoahHeapRegion* r) { HeapWord* bottom = r->bottom(); HeapWord* top_bitmap = _top_bitmaps[r->index()]; + + log_debug(gc)("SMC:clear_bitmap for %s Region " SIZE_FORMAT ", top_bitmap: " PTR_FORMAT, + r->affiliation_name(), r->index(), p2i(top_bitmap)); + if (top_bitmap > bottom) { _mark_bit_map.clear_range_large(MemRegion(bottom, top_bitmap)); _top_bitmaps[r->index()] = bottom; } - assert(is_bitmap_clear_range(bottom, r->end()), + + assert(is_bitmap_range_within_region_clear(bottom, r->end()), "Region " SIZE_FORMAT " should have no marks in bitmap", r->index()); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp index 62baf3e61ea..854f42f2604 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -32,6 +33,7 @@ #include "oops/oopsHierarchy.hpp" class ShenandoahObjToScanQueueSet; +class ShenandoahHeapRegion; /** * Encapsulate a marking bitmap with the top-at-mark-start and top-bitmaps array. @@ -47,12 +49,8 @@ class ShenandoahMarkingContext : public CHeapObj { ShenandoahSharedFlag _is_complete; - // Marking task queues - ShenandoahObjToScanQueueSet* _task_queues; - public: - ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions, uint max_queues); - ~ShenandoahMarkingContext(); + ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions); /* * Marks the object. Returns true if the object has not been marked before and has @@ -68,29 +66,30 @@ class ShenandoahMarkingContext : public CHeapObj { inline bool is_marked_strong(oop obj) const; inline bool is_marked_strong(HeapWord* raw_obj) const; inline bool is_marked_weak(oop obj) const; + inline bool is_marked_or_old(oop obj) const; + inline bool is_marked_strong_or_old(oop obj) const; - inline HeapWord* get_next_marked_addr(HeapWord* addr, HeapWord* limit) const; + inline HeapWord* get_next_marked_addr(const HeapWord* addr, const HeapWord* limit) const; - inline bool allocated_after_mark_start(oop obj) const; - inline bool allocated_after_mark_start(HeapWord* addr) const; + inline bool allocated_after_mark_start(const oop obj) const; + inline bool allocated_after_mark_start(const HeapWord* addr) const; - inline HeapWord* top_at_mark_start(ShenandoahHeapRegion* r) const; + inline HeapWord* top_at_mark_start(const ShenandoahHeapRegion* r) const; inline void capture_top_at_mark_start(ShenandoahHeapRegion* r); inline void reset_top_at_mark_start(ShenandoahHeapRegion* r); void initialize_top_at_mark_start(ShenandoahHeapRegion* r); + HeapWord* top_bitmap(ShenandoahHeapRegion* r); + inline void reset_top_bitmap(ShenandoahHeapRegion *r); void clear_bitmap(ShenandoahHeapRegion *r); bool is_bitmap_clear() const; - bool is_bitmap_clear_range(HeapWord* start, HeapWord* end) const; + bool is_bitmap_range_within_region_clear(const HeapWord* start, const HeapWord* end) const; bool is_complete(); void mark_complete(); void mark_incomplete(); - - // Task queues - ShenandoahObjToScanQueueSet* task_queues() const { return _task_queues; } }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp index 1ba3caf26b7..75a16e15549 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,8 +27,8 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_INLINE_HPP #include "gc/shenandoah/shenandoahMarkingContext.hpp" - #include "gc/shenandoah/shenandoahMarkBitMap.inline.hpp" +#include "logging/log.hpp" inline bool ShenandoahMarkingContext::mark_strong(oop obj, bool& was_upgraded) { return !allocated_after_mark_start(obj) && _mark_bit_map.mark_strong(cast_from_oop(obj), was_upgraded); @@ -57,23 +58,36 @@ inline bool ShenandoahMarkingContext::is_marked_weak(oop obj) const { return allocated_after_mark_start(obj) || _mark_bit_map.is_marked_weak(cast_from_oop(obj)); } -inline HeapWord* ShenandoahMarkingContext::get_next_marked_addr(HeapWord* start, HeapWord* limit) const { +inline bool ShenandoahMarkingContext::is_marked_or_old(oop obj) const { + return is_marked(obj) || ShenandoahHeap::heap()->is_in_old_during_young_collection(obj); +} + +inline bool ShenandoahMarkingContext::is_marked_strong_or_old(oop obj) const { + return is_marked_strong(obj) || ShenandoahHeap::heap()->is_in_old_during_young_collection(obj); +} + +inline HeapWord* ShenandoahMarkingContext::get_next_marked_addr(const HeapWord* start, const HeapWord* limit) const { return _mark_bit_map.get_next_marked_addr(start, limit); } inline bool ShenandoahMarkingContext::allocated_after_mark_start(oop obj) const { - HeapWord* addr = cast_from_oop(obj); + const HeapWord* addr = cast_from_oop(obj); return allocated_after_mark_start(addr); } -inline bool ShenandoahMarkingContext::allocated_after_mark_start(HeapWord* addr) const { +inline bool ShenandoahMarkingContext::allocated_after_mark_start(const HeapWord* addr) const { uintx index = ((uintx) addr) >> ShenandoahHeapRegion::region_size_bytes_shift(); HeapWord* top_at_mark_start = _top_at_mark_starts[index]; - bool alloc_after_mark_start = addr >= top_at_mark_start; + const bool alloc_after_mark_start = addr >= top_at_mark_start; return alloc_after_mark_start; } inline void ShenandoahMarkingContext::capture_top_at_mark_start(ShenandoahHeapRegion *r) { + if (!r->is_affiliated()) { + // Non-affiliated regions do not need their TAMS updated + return; + } + size_t idx = r->index(); HeapWord* old_tams = _top_at_mark_starts_base[idx]; HeapWord* new_tams = r->top(); @@ -81,10 +95,16 @@ inline void ShenandoahMarkingContext::capture_top_at_mark_start(ShenandoahHeapRe assert(new_tams >= old_tams, "Region " SIZE_FORMAT", TAMS updates should be monotonic: " PTR_FORMAT " -> " PTR_FORMAT, idx, p2i(old_tams), p2i(new_tams)); - assert(is_bitmap_clear_range(old_tams, new_tams), + assert((new_tams == r->bottom()) || (old_tams == r->bottom()) || (new_tams >= _top_bitmaps[idx]), + "Region " SIZE_FORMAT", top_bitmaps updates should be monotonic: " PTR_FORMAT " -> " PTR_FORMAT, + idx, p2i(_top_bitmaps[idx]), p2i(new_tams)); + assert(old_tams == r->bottom() || is_bitmap_range_within_region_clear(old_tams, new_tams), "Region " SIZE_FORMAT ", bitmap should be clear while adjusting TAMS: " PTR_FORMAT " -> " PTR_FORMAT, idx, p2i(old_tams), p2i(new_tams)); + log_debug(gc)("Capturing TAMS for %s Region " SIZE_FORMAT ", was: " PTR_FORMAT ", now: " PTR_FORMAT, + r->affiliation_name(), idx, p2i(old_tams), p2i(new_tams)); + _top_at_mark_starts_base[idx] = new_tams; _top_bitmaps[idx] = new_tams; } @@ -93,12 +113,12 @@ inline void ShenandoahMarkingContext::reset_top_at_mark_start(ShenandoahHeapRegi _top_at_mark_starts_base[r->index()] = r->bottom(); } -inline HeapWord* ShenandoahMarkingContext::top_at_mark_start(ShenandoahHeapRegion* r) const { +inline HeapWord* ShenandoahMarkingContext::top_at_mark_start(const ShenandoahHeapRegion* r) const { return _top_at_mark_starts_base[r->index()]; } inline void ShenandoahMarkingContext::reset_top_bitmap(ShenandoahHeapRegion* r) { - assert(is_bitmap_clear_range(r->bottom(), r->end()), + assert(is_bitmap_range_within_region_clear(r->bottom(), r->end()), "Region " SIZE_FORMAT " should have no marks in bitmap", r->index()); _top_bitmaps[r->index()] = r->bottom(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp index 339446e12e9..6cbb5454ac1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -24,14 +25,28 @@ #include "precompiled.hpp" #include "gc/shenandoah/shenandoahMemoryPool.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" -ShenandoahMemoryPool::ShenandoahMemoryPool(ShenandoahHeap* heap) : - CollectedMemoryPool("Shenandoah", +ShenandoahMemoryPool::ShenandoahMemoryPool(ShenandoahHeap* heap, + const char* name) : + CollectedMemoryPool(name, heap->initial_capacity(), heap->max_capacity(), true /* support_usage_threshold */), _heap(heap) {} +ShenandoahMemoryPool::ShenandoahMemoryPool(ShenandoahHeap* heap, + const char* name, + size_t initial_capacity, + size_t max_capacity) : + CollectedMemoryPool(name, + initial_capacity, + max_capacity, + true /* support_usage_threshold */), + _heap(heap) {} + + MemoryUsage ShenandoahMemoryPool::get_memory_usage() { size_t initial = initial_size(); size_t max = max_size(); @@ -51,3 +66,44 @@ MemoryUsage ShenandoahMemoryPool::get_memory_usage() { return MemoryUsage(initial, used, committed, max); } + +size_t ShenandoahMemoryPool::used_in_bytes() { + return _heap->used(); +} + +size_t ShenandoahMemoryPool::max_size() const { + return _heap->max_capacity(); +} + +ShenandoahGenerationalMemoryPool::ShenandoahGenerationalMemoryPool(ShenandoahHeap* heap, const char* name, + ShenandoahGeneration* generation) : + ShenandoahMemoryPool(heap, name, 0, heap->max_capacity()), + _generation(generation) { } + +MemoryUsage ShenandoahGenerationalMemoryPool::get_memory_usage() { + size_t initial = initial_size(); + size_t max = max_size(); + size_t used = used_in_bytes(); + size_t committed = _generation->used_regions_size(); + + return MemoryUsage(initial, used, committed, max); +} + +size_t ShenandoahGenerationalMemoryPool::used_in_bytes() { + return _generation->used(); +} + +size_t ShenandoahGenerationalMemoryPool::max_size() const { + return _generation->max_capacity(); +} + + +ShenandoahYoungGenMemoryPool::ShenandoahYoungGenMemoryPool(ShenandoahHeap* heap) : + ShenandoahGenerationalMemoryPool(heap, + "Shenandoah Young Gen", + heap->young_generation()) { } + +ShenandoahOldGenMemoryPool::ShenandoahOldGenMemoryPool(ShenandoahHeap* heap) : + ShenandoahGenerationalMemoryPool(heap, + "Shenandoah Old Gen", + heap->old_generation()) { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp index 2149213afa8..e26d61a3a6c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -25,21 +26,46 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMEMORYPOOL_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHMEMORYPOOL_HPP -#ifndef SERIALGC #include "gc/shenandoah/shenandoahHeap.hpp" #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" -#endif class ShenandoahMemoryPool : public CollectedMemoryPool { -private: +protected: ShenandoahHeap* _heap; public: - ShenandoahMemoryPool(ShenandoahHeap* pool); - MemoryUsage get_memory_usage(); - size_t used_in_bytes() { return _heap->used(); } - size_t max_size() const { return _heap->max_capacity(); } + explicit ShenandoahMemoryPool(ShenandoahHeap* heap, + const char* name = "Shenandoah"); + MemoryUsage get_memory_usage() override; + size_t used_in_bytes() override; + size_t max_size() const override; + +protected: + ShenandoahMemoryPool(ShenandoahHeap* heap, + const char* name, + size_t initial_capacity, + size_t max_capacity); +}; + +class ShenandoahGenerationalMemoryPool: public ShenandoahMemoryPool { +private: + ShenandoahGeneration* _generation; +public: + explicit ShenandoahGenerationalMemoryPool(ShenandoahHeap* heap, const char* name, ShenandoahGeneration* generation); + MemoryUsage get_memory_usage() override; + size_t used_in_bytes() override; + size_t max_size() const override; +}; + +class ShenandoahYoungGenMemoryPool : public ShenandoahGenerationalMemoryPool { +public: + explicit ShenandoahYoungGenMemoryPool(ShenandoahHeap* heap); +}; + +class ShenandoahOldGenMemoryPool : public ShenandoahGenerationalMemoryPool { +public: + explicit ShenandoahOldGenMemoryPool(ShenandoahHeap* heap); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMEMORYPOOL_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp new file mode 100644 index 00000000000..f4cbdbdd493 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp @@ -0,0 +1,185 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahMmuTracker.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "logging/log.hpp" +#include "runtime/os.hpp" +#include "runtime/task.hpp" + +class ShenandoahMmuTask : public PeriodicTask { + ShenandoahMmuTracker* _mmu_tracker; +public: + explicit ShenandoahMmuTask(ShenandoahMmuTracker* mmu_tracker) : + PeriodicTask(GCPauseIntervalMillis), _mmu_tracker(mmu_tracker) {} + + void task() override { + _mmu_tracker->report(); + } +}; + +class ThreadTimeAccumulator : public ThreadClosure { + public: + size_t total_time; + ThreadTimeAccumulator() : total_time(0) {} + void do_thread(Thread* thread) override { + total_time += os::thread_cpu_time(thread); + } +}; + +ShenandoahMmuTracker::ShenandoahMmuTracker() : + _most_recent_timestamp(0.0), + _most_recent_gc_time(0.0), + _most_recent_gcu(0.0), + _most_recent_mutator_time(0.0), + _most_recent_mu(0.0), + _most_recent_periodic_time_stamp(0.0), + _most_recent_periodic_gc_time(0.0), + _most_recent_periodic_mutator_time(0.0), + _mmu_periodic_task(new ShenandoahMmuTask(this)) { +} + +ShenandoahMmuTracker::~ShenandoahMmuTracker() { + _mmu_periodic_task->disenroll(); + delete _mmu_periodic_task; +} + +void ShenandoahMmuTracker::fetch_cpu_times(double &gc_time, double &mutator_time) { + ThreadTimeAccumulator cl; + // We include only the gc threads because those are the only threads + // we are responsible for. + ShenandoahHeap::heap()->gc_threads_do(&cl); + double most_recent_gc_thread_time = double(cl.total_time) / NANOSECS_PER_SEC; + gc_time = most_recent_gc_thread_time; + + double process_real_time(0.0), process_user_time(0.0), process_system_time(0.0); + bool valid = os::getTimesSecs(&process_real_time, &process_user_time, &process_system_time); + assert(valid, "don't know why this would not be valid"); + mutator_time =(process_user_time + process_system_time) - most_recent_gc_thread_time; +} + +void ShenandoahMmuTracker::update_utilization(size_t gcid, const char* msg) { + double current = os::elapsedTime(); + _most_recent_gcid = gcid; + _most_recent_is_full = false; + + if (gcid == 0) { + fetch_cpu_times(_most_recent_gc_time, _most_recent_mutator_time); + + _most_recent_timestamp = current; + } else { + double gc_cycle_period = current - _most_recent_timestamp; + _most_recent_timestamp = current; + + double gc_thread_time, mutator_thread_time; + fetch_cpu_times(gc_thread_time, mutator_thread_time); + double gc_time = gc_thread_time - _most_recent_gc_time; + _most_recent_gc_time = gc_thread_time; + _most_recent_gcu = gc_time / (_active_processors * gc_cycle_period); + double mutator_time = mutator_thread_time - _most_recent_mutator_time; + _most_recent_mutator_time = mutator_thread_time; + _most_recent_mu = mutator_time / (_active_processors * gc_cycle_period); + log_info(gc, ergo)("At end of %s: GCU: %.1f%%, MU: %.1f%% during period of %.3fs", + msg, _most_recent_gcu * 100, _most_recent_mu * 100, gc_cycle_period); + } +} + +void ShenandoahMmuTracker::record_young(size_t gcid) { + update_utilization(gcid, "Concurrent Young GC"); +} + +void ShenandoahMmuTracker::record_global(size_t gcid) { + update_utilization(gcid, "Concurrent Global GC"); +} + +void ShenandoahMmuTracker::record_bootstrap(size_t gcid) { + // Not likely that this will represent an "ideal" GCU, but doesn't hurt to try + update_utilization(gcid, "Concurrent Bootstrap GC"); +} + +void ShenandoahMmuTracker::record_old_marking_increment(bool old_marking_done) { + // No special processing for old marking + double now = os::elapsedTime(); + double duration = now - _most_recent_timestamp; + + double gc_time, mutator_time; + fetch_cpu_times(gc_time, mutator_time); + double gcu = (gc_time - _most_recent_gc_time) / duration; + double mu = (mutator_time - _most_recent_mutator_time) / duration; + log_info(gc, ergo)("At end of %s: GCU: %.1f%%, MU: %.1f%% for duration %.3fs (totals to be subsumed in next gc report)", + old_marking_done? "last OLD marking increment": "OLD marking increment", + gcu * 100, mu * 100, duration); +} + +void ShenandoahMmuTracker::record_mixed(size_t gcid) { + update_utilization(gcid, "Mixed Concurrent GC"); +} + +void ShenandoahMmuTracker::record_degenerated(size_t gcid, bool is_old_bootstrap) { + if ((gcid == _most_recent_gcid) && _most_recent_is_full) { + // Do nothing. This is a redundant recording for the full gc that just completed. + } else if (is_old_bootstrap) { + update_utilization(gcid, "Degenerated Bootstrap Old GC"); + } else { + update_utilization(gcid, "Degenerated Young GC"); + } +} + +void ShenandoahMmuTracker::record_full(size_t gcid) { + update_utilization(gcid, "Full GC"); + _most_recent_is_full = true; +} + +void ShenandoahMmuTracker::report() { + // This is only called by the periodic thread. + double current = os::elapsedTime(); + double time_delta = current - _most_recent_periodic_time_stamp; + _most_recent_periodic_time_stamp = current; + + double gc_time, mutator_time; + fetch_cpu_times(gc_time, mutator_time); + + double gc_delta = gc_time - _most_recent_periodic_gc_time; + _most_recent_periodic_gc_time = gc_time; + + double mutator_delta = mutator_time - _most_recent_periodic_mutator_time; + _most_recent_periodic_mutator_time = mutator_time; + + double mu = mutator_delta / (_active_processors * time_delta); + double gcu = gc_delta / (_active_processors * time_delta); + log_debug(gc)("Periodic Sample: GCU = %.3f%%, MU = %.3f%% during most recent %.1fs", gcu * 100, mu * 100, time_delta); +} + +void ShenandoahMmuTracker::initialize() { + // initialize static data + _active_processors = os::initial_active_processor_count(); + + _most_recent_periodic_time_stamp = os::elapsedTime(); + fetch_cpu_times(_most_recent_periodic_gc_time, _most_recent_periodic_mutator_time); + _mmu_periodic_task->enroll(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.hpp new file mode 100644 index 00000000000..53b6fdada55 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.hpp @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMMUTRACKER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHMMUTRACKER_HPP + +#include "utilities/numberSeq.hpp" + +class ShenandoahGeneration; +class ShenandoahMmuTask; + +/** + * This class is responsible for tracking and adjusting the minimum mutator + * utilization (MMU). MMU is defined as the percentage of CPU time available + * to mutator threads over an arbitrary, fixed interval of time. This interval + * defaults to 5 seconds and is configured by GCPauseIntervalMillis. The class + * maintains a decaying average of the last 10 values. The MMU is measured + * by summing all of the time given to the GC threads and comparing this to + * the total CPU time for the process. There are OS APIs to support this on + * all major platforms. + * + * The time spent by GC threads is attributed to the young or old generation. + * The time given to the controller and regulator threads is attributed to the + * global generation. At the end of every collection, the average MMU is inspected. + * If it is below `GCTimeRatio`, this class will attempt to increase the capacity + * of the generation that is consuming the most CPU time. The assumption being + * that increasing memory will reduce the collection frequency and raise the + * MMU. + */ +class ShenandoahMmuTracker { +private: + // These variables hold recent snapshots of cumulative quantities that are used for calculating + // CPU time consumed by GC and mutator threads during each GC cycle. + double _most_recent_timestamp; + double _most_recent_gc_time; + double _most_recent_gcu; + double _most_recent_mutator_time; + double _most_recent_mu; + + // These variables hold recent snapshots of cumulative quantities that are used for reporting + // periodic consumption of CPU time by GC and mutator threads. + double _most_recent_periodic_time_stamp; + double _most_recent_periodic_gc_time; + double _most_recent_periodic_mutator_time; + + size_t _most_recent_gcid; + uint _active_processors; + + bool _most_recent_is_full; + + ShenandoahMmuTask* _mmu_periodic_task; + TruncatedSeq _mmu_average; + + void update_utilization(size_t gcid, const char* msg); + static void fetch_cpu_times(double &gc_time, double &mutator_time); + +public: + explicit ShenandoahMmuTracker(); + ~ShenandoahMmuTracker(); + + // This enrolls the periodic task after everything is initialized. + void initialize(); + + // At completion of each GC cycle (not including interrupted cycles), we invoke one of the following to record the + // GC utilization during this cycle. Incremental efforts spent in an interrupted GC cycle will be accumulated into + // the CPU time reports for the subsequent completed [degenerated or full] GC cycle. + // + // We may redundantly record degen and full in the case that a degen upgrades to full. When this happens, we will invoke + // both record_full() and record_degenerated() with the same value of gcid. record_full() is called first and the log + // reports such a cycle as a FULL cycle. + void record_young(size_t gcid); + void record_global(size_t gcid); + void record_bootstrap(size_t gcid); + void record_old_marking_increment(bool old_marking_done); + void record_mixed(size_t gcid); + void record_full(size_t gcid); + void record_degenerated(size_t gcid, bool is_old_boostrap); + + // This is called by the periodic task timer. The interval is defined by + // GCPauseIntervalMillis and defaults to 5 seconds. This method computes + // the MMU over the elapsed interval and records it in a running average. + void report(); +}; + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHMMUTRACKER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 75687302ca5..5ba24159088 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -125,13 +125,13 @@ void ShenandoahNMethod::heal_nmethod(nmethod* nm) { assert(data->lock()->owned_by_self(), "Must hold the lock"); ShenandoahHeap* const heap = ShenandoahHeap::heap(); - if (heap->is_concurrent_mark_in_progress()) { - ShenandoahKeepAliveClosure cl; - data->oops_do(&cl); - } else if (heap->is_concurrent_weak_root_in_progress() || - heap->is_concurrent_strong_root_in_progress() ) { + if (heap->is_concurrent_weak_root_in_progress() || + heap->is_concurrent_strong_root_in_progress()) { ShenandoahEvacOOMScope evac_scope; heal_nmethod_metadata(data); + } else if (heap->is_concurrent_mark_in_progress()) { + ShenandoahKeepAliveClosure cl; + data->oops_do(&cl); } else { // There is possibility that GC is cancelled when it arrives final mark. // In this case, concurrent root phase is skipped and degenerated GC should be diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp index ec8f2231097..3c7ba8e4243 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -120,6 +121,70 @@ double HdrSeq::percentile(double level) const { return maximum(); } +void HdrSeq::add(const HdrSeq& other) { + if (other.num() == 0) { + // Other sequence is empty, return + return; + } + + for (int mag = 0; mag < MagBuckets; mag++) { + int* other_bucket = other._hdr[mag]; + if (other_bucket == nullptr) { + // Nothing to do + continue; + } + int* bucket = _hdr[mag]; + if (bucket != nullptr) { + // Add into our bucket + for (int val = 0; val < ValBuckets; val++) { + bucket[val] += other_bucket[val]; + } + } else { + // Create our bucket and copy the contents over + bucket = NEW_C_HEAP_ARRAY(int, ValBuckets, mtInternal); + for (int val = 0; val < ValBuckets; val++) { + bucket[val] = other_bucket[val]; + } + _hdr[mag] = bucket; + } + } + + // This is a hacky way to only update the fields we want. + // This inlines NumberSeq code without going into AbsSeq and + // dealing with decayed average/variance, which we do not + // know how to compute yet. + _last = other._last; + _maximum = MAX2(_maximum, other._maximum); + _sum += other._sum; + _sum_of_squares += other._sum_of_squares; + _num += other._num; + + // Until JDK-8298902 is fixed, we taint the decaying statistics + _davg = NAN; + _dvariance = NAN; +} + +void HdrSeq::clear() { + // Clear the storage + for (int mag = 0; mag < MagBuckets; mag++) { + int* bucket = _hdr[mag]; + if (bucket != nullptr) { + for (int c = 0; c < ValBuckets; c++) { + bucket[c] = 0; + } + } + } + + // Clear other fields too + _last = 0; + _maximum = 0; + _sum = 0; + _sum_of_squares = 0; + _num = 0; + _davg = 0; + _dvariance = 0; +} + BinaryMagnitudeSeq::BinaryMagnitudeSeq() { _mags = NEW_C_HEAP_ARRAY(size_t, BitsPerSize_t, mtInternal); clear(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp index 42f91f6a9b8..68f3cfba97a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp @@ -49,7 +49,9 @@ class HdrSeq: public NumberSeq { ~HdrSeq(); virtual void add(double val); + void add(const HdrSeq& other); double percentile(double level) const; + void clear(); }; // Binary magnitude sequence stores the power-of-two histogram. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp new file mode 100644 index 00000000000..a5d3302091c --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -0,0 +1,161 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" +#include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGC.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "prims/jvmtiTagMap.hpp" +#include "utilities/events.hpp" + + +ShenandoahOldGC::ShenandoahOldGC(ShenandoahOldGeneration* generation, ShenandoahSharedFlag& allow_preemption) : + ShenandoahConcurrentGC(generation, false), _old_generation(generation), _allow_preemption(allow_preemption) { +} + +// Final mark for old-gen is different for young than old, so we +// override the implementation. +void ShenandoahOldGC::op_final_mark() { + + ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); + assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Should be at safepoint"); + assert(!heap->has_forwarded_objects(), "No forwarded objects on this path"); + + if (ShenandoahVerify) { + heap->verifier()->verify_roots_no_forwarded(); + } + + if (!heap->cancelled_gc()) { + assert(_mark.generation()->is_old(), "Generation of Old-Gen GC should be OLD"); + _mark.finish_mark(); + assert(!heap->cancelled_gc(), "STW mark cannot OOM"); + + // Old collection is complete, the young generation no longer needs this + // reference to the old concurrent mark so clean it up. + heap->young_generation()->set_old_gen_task_queues(nullptr); + + // We need to do this because weak root cleaning reports the number of dead handles + JvmtiTagMap::set_needs_cleaning(); + + _generation->prepare_regions_and_collection_set(true); + + heap->set_unload_classes(false); + heap->prepare_concurrent_roots(); + + // Believe verification following old-gen concurrent mark needs to be different than verification following + // young-gen concurrent mark, so am commenting this out for now: + // if (ShenandoahVerify) { + // heap->verifier()->verify_after_concmark(); + // } + + if (VerifyAfterGC) { + Universe::verify(); + } + } +} + +bool ShenandoahOldGC::collect(GCCause::Cause cause) { + auto heap = ShenandoahGenerationalHeap::heap(); + assert(!_old_generation->is_doing_mixed_evacuations(), "Should not start an old gc with pending mixed evacuations"); + assert(!_old_generation->is_preparing_for_mark(), "Old regions need to be parsable during concurrent mark."); + + // Enable preemption of old generation mark. + _allow_preemption.set(); + + // Continue concurrent mark, do not reset regions, do not mark roots, do not collect $200. + entry_mark(); + + // If we failed to unset the preemption flag, it means another thread has already unset it. + if (!_allow_preemption.try_unset()) { + // The regulator thread has unset the preemption guard. That thread will shortly cancel + // the gc, but the control thread is now racing it. Wait until this thread sees the + // cancellation. + while (!heap->cancelled_gc()) { + SpinPause(); + } + } + + if (heap->cancelled_gc()) { + return false; + } + + // Complete marking under STW + vmop_entry_final_mark(); + + if (_generation->is_concurrent_mark_in_progress()) { + assert(heap->cancelled_gc(), "Safepoint operation observed gc cancellation"); + // GC may have been cancelled before final mark, but after the preceding cancellation check. + return false; + } + + // We aren't dealing with old generation evacuation yet. Our heuristic + // should not have built a cset in final mark. + assert(!heap->is_evacuation_in_progress(), "Old gen evacuations are not supported"); + + // Process weak roots that might still point to regions that would be broken by cleanup + if (heap->is_concurrent_weak_root_in_progress()) { + entry_weak_refs(); + entry_weak_roots(); + } + + // Final mark might have reclaimed some immediate garbage, kick cleanup to reclaim + // the space. This would be the last action if there is nothing to evacuate. + entry_cleanup_early(); + + heap->free_set()->log_status_under_lock(); + + assert(!heap->is_concurrent_strong_root_in_progress(), "No evacuations during old gc."); + + // We must execute this vm operation if we completed final mark. We cannot + // return from here with weak roots in progress. This is not a valid gc state + // for any young collections (or allocation failures) that interrupt the old + // collection. + vmop_entry_final_roots(); + + // We do not rebuild_free following increments of old marking because memory has not been reclaimed. However, we may + // need to transfer memory to OLD in order to efficiently support the mixed evacuations that might immediately follow. + size_t allocation_runway = heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(0); + heap->compute_old_generation_balance(allocation_runway, 0); + + ShenandoahGenerationalHeap::TransferResult result; + { + ShenandoahHeapLocker locker(heap->lock()); + result = heap->balance_generations(); + } + + LogTarget(Info, gc, ergo) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + result.print_on("Old Mark", &ls); + } + return true; +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.hpp new file mode 100644 index 00000000000..2c637f5f72a --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.hpp @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHOLDGC_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHOLDGC_HPP + +#include "gc/shared/gcCause.hpp" +#include "gc/shenandoah/shenandoahConcurrentGC.hpp" +#include "gc/shenandoah/shenandoahVerifier.hpp" + +class ShenandoahOldGeneration; + +class ShenandoahOldGC : public ShenandoahConcurrentGC { + public: + ShenandoahOldGC(ShenandoahOldGeneration* generation, ShenandoahSharedFlag& allow_preemption); + bool collect(GCCause::Cause cause) override; + + protected: + void op_final_mark() override; + + private: + ShenandoahOldGeneration* _old_generation; + ShenandoahSharedFlag& _allow_preemption; +}; + + +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHOLDGC_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp new file mode 100644 index 00000000000..36007023d46 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -0,0 +1,790 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shared/strongRootsScope.hpp" +#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahWorkerPolicy.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "runtime/threads.hpp" +#include "utilities/events.hpp" + +class ShenandoahFlushAllSATB : public ThreadClosure { +private: + SATBMarkQueueSet& _satb_qset; + +public: + explicit ShenandoahFlushAllSATB(SATBMarkQueueSet& satb_qset) : + _satb_qset(satb_qset) {} + + void do_thread(Thread* thread) override { + // Transfer any partial buffer to the qset for completed buffer processing. + _satb_qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread)); + } +}; + +class ShenandoahProcessOldSATB : public SATBBufferClosure { +private: + ShenandoahObjToScanQueue* _queue; + ShenandoahHeap* _heap; + ShenandoahMarkingContext* const _mark_context; + size_t _trashed_oops; + +public: + explicit ShenandoahProcessOldSATB(ShenandoahObjToScanQueue* q) : + _queue(q), + _heap(ShenandoahHeap::heap()), + _mark_context(_heap->marking_context()), + _trashed_oops(0) {} + + void do_buffer(void** buffer, size_t size) override { + assert(size == 0 || !_heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded objects are not expected here"); + for (size_t i = 0; i < size; ++i) { + oop *p = (oop *) &buffer[i]; + ShenandoahHeapRegion* region = _heap->heap_region_containing(*p); + if (region->is_old() && region->is_active()) { + ShenandoahMark::mark_through_ref(p, _queue, nullptr, _mark_context, false); + } else { + _trashed_oops++; + } + } + } + + size_t trashed_oops() const { + return _trashed_oops; + } +}; + +class ShenandoahPurgeSATBTask : public WorkerTask { +private: + ShenandoahObjToScanQueueSet* _mark_queues; + volatile size_t _trashed_oops; + +public: + explicit ShenandoahPurgeSATBTask(ShenandoahObjToScanQueueSet* queues) : + WorkerTask("Purge SATB"), + _mark_queues(queues), + _trashed_oops(0) { + Threads::change_thread_claim_token(); + } + + ~ShenandoahPurgeSATBTask() { + if (_trashed_oops > 0) { + log_debug(gc)("Purged " SIZE_FORMAT " oops from old generation SATB buffers", _trashed_oops); + } + } + + void work(uint worker_id) override { + ShenandoahParallelWorkerSession worker_session(worker_id); + ShenandoahSATBMarkQueueSet &satb_queues = ShenandoahBarrierSet::satb_mark_queue_set(); + ShenandoahFlushAllSATB flusher(satb_queues); + Threads::possibly_parallel_threads_do(true /* is_par */, &flusher); + + ShenandoahObjToScanQueue* mark_queue = _mark_queues->queue(worker_id); + ShenandoahProcessOldSATB processor(mark_queue); + while (satb_queues.apply_closure_to_completed_buffer(&processor)) {} + + Atomic::add(&_trashed_oops, processor.trashed_oops()); + } +}; + +class ShenandoahConcurrentCoalesceAndFillTask : public WorkerTask { +private: + uint _nworkers; + ShenandoahHeapRegion** _coalesce_and_fill_region_array; + uint _coalesce_and_fill_region_count; + volatile bool _is_preempted; + +public: + ShenandoahConcurrentCoalesceAndFillTask(uint nworkers, + ShenandoahHeapRegion** coalesce_and_fill_region_array, + uint region_count) : + WorkerTask("Shenandoah Concurrent Coalesce and Fill"), + _nworkers(nworkers), + _coalesce_and_fill_region_array(coalesce_and_fill_region_array), + _coalesce_and_fill_region_count(region_count), + _is_preempted(false) { + } + + void work(uint worker_id) override { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_coalesce_and_fill, ShenandoahPhaseTimings::ScanClusters, worker_id); + for (uint region_idx = worker_id; region_idx < _coalesce_and_fill_region_count; region_idx += _nworkers) { + ShenandoahHeapRegion* r = _coalesce_and_fill_region_array[region_idx]; + if (r->is_humongous()) { + // There is only one object in this region and it is not garbage, + // so no need to coalesce or fill. + continue; + } + + if (!r->oop_coalesce_and_fill(true)) { + // Coalesce and fill has been preempted + Atomic::store(&_is_preempted, true); + return; + } + } + } + + // Value returned from is_completed() is only valid after all worker thread have terminated. + bool is_completed() { + return !Atomic::load(&_is_preempted); + } +}; + +ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity) + : ShenandoahGeneration(OLD, max_queues, max_capacity, soft_max_capacity), + _coalesce_and_fill_region_array(NEW_C_HEAP_ARRAY(ShenandoahHeapRegion*, ShenandoahHeap::heap()->num_regions(), mtGC)), + _old_heuristics(nullptr), + _region_balance(0), + _promoted_reserve(0), + _promoted_expended(0), + _promotion_potential(0), + _pad_for_promote_in_place(0), + _promotable_humongous_regions(0), + _promotable_regular_regions(0), + _is_parsable(true), + _card_scan(nullptr), + _state(WAITING_FOR_BOOTSTRAP), + _growth_before_compaction(INITIAL_GROWTH_BEFORE_COMPACTION), + _min_growth_before_compaction ((ShenandoahMinOldGenGrowthPercent * FRACTIONAL_DENOMINATOR) / 100) +{ + _live_bytes_after_last_mark = ShenandoahHeap::heap()->capacity() * INITIAL_LIVE_FRACTION / FRACTIONAL_DENOMINATOR; + // Always clear references for old generation + ref_processor()->set_soft_reference_policy(true); + + if (ShenandoahCardBarrier) { + ShenandoahCardTable* card_table = ShenandoahBarrierSet::barrier_set()->card_table(); + size_t card_count = card_table->cards_required(ShenandoahHeap::heap()->reserved_region().word_size()); + auto rs = new ShenandoahDirectCardMarkRememberedSet(card_table, card_count); + _card_scan = new ShenandoahScanRemembered(rs); + } +} + +void ShenandoahOldGeneration::set_promoted_reserve(size_t new_val) { + shenandoah_assert_heaplocked_or_safepoint(); + _promoted_reserve = new_val; +} + +size_t ShenandoahOldGeneration::get_promoted_reserve() const { + return _promoted_reserve; +} + +void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { + shenandoah_assert_heaplocked_or_safepoint(); + _promoted_reserve += increment; +} + +void ShenandoahOldGeneration::reset_promoted_expended() { + shenandoah_assert_heaplocked_or_safepoint(); + Atomic::store(&_promoted_expended, (size_t) 0); +} + +size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { + shenandoah_assert_heaplocked_or_safepoint(); + assert(get_promoted_expended() + increment <= get_promoted_reserve(), "Do not expend more promotion than budgeted"); + return Atomic::add(&_promoted_expended, increment); +} + +size_t ShenandoahOldGeneration::unexpend_promoted(size_t decrement) { + return Atomic::sub(&_promoted_expended, decrement); +} + +size_t ShenandoahOldGeneration::get_promoted_expended() const { + return Atomic::load(&_promoted_expended); +} + +bool ShenandoahOldGeneration::can_allocate(const ShenandoahAllocRequest &req) const { + assert(req.type() != ShenandoahAllocRequest::_alloc_gclab, "GCLAB pertains only to young-gen memory"); + + const size_t requested_bytes = req.size() * HeapWordSize; + // The promotion reserve may also be used for evacuations. If we can promote this object, + // then we can also evacuate it. + if (can_promote(requested_bytes)) { + // The promotion reserve should be able to accommodate this request. The request + // might still fail if alignment with the card table increases the size. The request + // may also fail if the heap is badly fragmented and the free set cannot find room for it. + return true; + } + + if (req.type() == ShenandoahAllocRequest::_alloc_plab) { + // The promotion reserve cannot accommodate this plab request. Check if we still have room for + // evacuations. Note that we cannot really know how much of the plab will be used for evacuations, + // so here we only check that some evacuation reserve still exists. + return get_evacuation_reserve() > 0; + } + + // This is a shared allocation request. We've already checked that it can't be promoted, so if + // it is a promotion, we return false. Otherwise, it is a shared evacuation request, and we allow + // the allocation to proceed. + return !req.is_promotion(); +} + +void +ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAllocRequest &req) { + // Note: Even when a mutator is performing a promotion outside a LAB, we use a 'shared_gc' request. + if (req.is_gc_alloc()) { + const size_t actual_size = req.actual_size() * HeapWordSize; + if (req.type() == ShenandoahAllocRequest::_alloc_plab) { + // We've created a new plab. Now we configure it whether it will be used for promotions + // and evacuations - or just evacuations. + Thread* thread = Thread::current(); + ShenandoahThreadLocalData::reset_plab_promoted(thread); + + // The actual size of the allocation may be larger than the requested bytes (due to alignment on card boundaries). + // If this puts us over our promotion budget, we need to disable future PLAB promotions for this thread. + if (can_promote(actual_size)) { + // Assume the entirety of this PLAB will be used for promotion. This prevents promotion from overreach. + // When we retire this plab, we'll unexpend what we don't really use. + expend_promoted(actual_size); + ShenandoahThreadLocalData::enable_plab_promotions(thread); + ShenandoahThreadLocalData::set_plab_actual_size(thread, actual_size); + } else { + // Disable promotions in this thread because entirety of this PLAB must be available to hold old-gen evacuations. + ShenandoahThreadLocalData::disable_plab_promotions(thread); + ShenandoahThreadLocalData::set_plab_actual_size(thread, 0); + } + } else if (req.is_promotion()) { + // Shared promotion. + expend_promoted(actual_size); + } + } +} + +size_t ShenandoahOldGeneration::get_live_bytes_after_last_mark() const { + return _live_bytes_after_last_mark; +} + +void ShenandoahOldGeneration::set_live_bytes_after_last_mark(size_t bytes) { + if (bytes == 0) { + // Restart search for best old-gen size to the initial state + _live_bytes_after_last_mark = ShenandoahHeap::heap()->capacity() * INITIAL_LIVE_FRACTION / FRACTIONAL_DENOMINATOR; + _growth_before_compaction = INITIAL_GROWTH_BEFORE_COMPACTION; + } else { + _live_bytes_after_last_mark = bytes; + _growth_before_compaction /= 2; + if (_growth_before_compaction < _min_growth_before_compaction) { + _growth_before_compaction = _min_growth_before_compaction; + } + } +} + +void ShenandoahOldGeneration::handle_failed_transfer() { + _old_heuristics->trigger_cannot_expand(); +} + +size_t ShenandoahOldGeneration::usage_trigger_threshold() const { + size_t result = _live_bytes_after_last_mark + (_live_bytes_after_last_mark * _growth_before_compaction) / FRACTIONAL_DENOMINATOR; + return result; +} + +bool ShenandoahOldGeneration::contains(ShenandoahAffiliation affiliation) const { + return affiliation == OLD_GENERATION; +} +bool ShenandoahOldGeneration::contains(ShenandoahHeapRegion* region) const { + return region->is_old(); +} + +void ShenandoahOldGeneration::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) { + ShenandoahIncludeRegionClosure old_regions_cl(cl); + ShenandoahHeap::heap()->parallel_heap_region_iterate(&old_regions_cl); +} + +void ShenandoahOldGeneration::heap_region_iterate(ShenandoahHeapRegionClosure* cl) { + ShenandoahIncludeRegionClosure old_regions_cl(cl); + ShenandoahHeap::heap()->heap_region_iterate(&old_regions_cl); +} + +void ShenandoahOldGeneration::set_concurrent_mark_in_progress(bool in_progress) { + ShenandoahHeap::heap()->set_concurrent_old_mark_in_progress(in_progress); +} + +bool ShenandoahOldGeneration::is_concurrent_mark_in_progress() { + return ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress(); +} + +void ShenandoahOldGeneration::cancel_marking() { + if (is_concurrent_mark_in_progress()) { + log_debug(gc)("Abandon SATB buffers"); + ShenandoahBarrierSet::satb_mark_queue_set().abandon_partial_marking(); + } + + ShenandoahGeneration::cancel_marking(); +} + +void ShenandoahOldGeneration::cancel_gc() { + shenandoah_assert_safepoint(); + if (is_idle()) { +#ifdef ASSERT + validate_waiting_for_bootstrap(); +#endif + } else { + log_info(gc)("Terminating old gc cycle."); + // Stop marking + cancel_marking(); + // Stop tracking old regions + abandon_collection_candidates(); + // Remove old generation access to young generation mark queues + ShenandoahHeap::heap()->young_generation()->set_old_gen_task_queues(nullptr); + // Transition to IDLE now. + transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + } +} + +void ShenandoahOldGeneration::prepare_gc() { + // Now that we have made the old generation parsable, it is safe to reset the mark bitmap. + assert(state() != FILLING, "Cannot reset old without making it parsable"); + + ShenandoahGeneration::prepare_gc(); +} + +bool ShenandoahOldGeneration::entry_coalesce_and_fill() { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + + static const char* msg = "Coalescing and filling (Old)"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_coalesce_and_fill); + + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + EventMark em("%s", msg); + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_marking(), + msg); + + return coalesce_and_fill(); +} + +// Make the old generation regions parsable, so they can be safely +// scanned when looking for objects in memory indicated by dirty cards. +bool ShenandoahOldGeneration::coalesce_and_fill() { + transition_to(FILLING); + + // This code will see the same set of regions to fill on each resumption as it did + // on the initial run. That's okay because each region keeps track of its own coalesce + // and fill state. Regions that were filled on a prior attempt will not try to fill again. + uint coalesce_and_fill_regions_count = _old_heuristics->get_coalesce_and_fill_candidates(_coalesce_and_fill_region_array); + assert(coalesce_and_fill_regions_count <= ShenandoahHeap::heap()->num_regions(), "Sanity"); + if (coalesce_and_fill_regions_count == 0) { + // No regions need to be filled. + abandon_collection_candidates(); + return true; + } + + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + WorkerThreads* workers = heap->workers(); + uint nworkers = workers->active_workers(); + ShenandoahConcurrentCoalesceAndFillTask task(nworkers, _coalesce_and_fill_region_array, coalesce_and_fill_regions_count); + + log_debug(gc)("Starting (or resuming) coalesce-and-fill of " UINT32_FORMAT " old heap regions", coalesce_and_fill_regions_count); + workers->run_task(&task); + if (task.is_completed()) { + // We no longer need to track regions that need to be coalesced and filled. + abandon_collection_candidates(); + return true; + } else { + // Coalesce-and-fill has been preempted. We'll finish that effort in the future. Do not invoke + // ShenandoahGeneration::prepare_gc() until coalesce-and-fill is done because it resets the mark bitmap + // and invokes set_mark_incomplete(). Coalesce-and-fill depends on the mark bitmap. + log_debug(gc)("Suspending coalesce-and-fill of old heap regions"); + return false; + } +} + +void ShenandoahOldGeneration::transfer_pointers_from_satb() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + shenandoah_assert_safepoint(); + assert(heap->is_concurrent_old_mark_in_progress(), "Only necessary during old marking."); + log_debug(gc)("Transfer SATB buffers"); + uint nworkers = heap->workers()->active_workers(); + StrongRootsScope scope(nworkers); + + ShenandoahPurgeSATBTask purge_satb_task(task_queues()); + heap->workers()->run_task(&purge_satb_task); +} + +bool ShenandoahOldGeneration::contains(oop obj) const { + return ShenandoahHeap::heap()->is_in_old(obj); +} + +void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + assert(!heap->is_full_gc_in_progress(), "Only for concurrent and degenerated GC"); + + { + ShenandoahGCPhase phase(concurrent ? + ShenandoahPhaseTimings::final_update_region_states : + ShenandoahPhaseTimings::degen_gc_final_update_region_states); + ShenandoahFinalMarkUpdateRegionStateClosure cl(complete_marking_context()); + + parallel_heap_region_iterate(&cl); + heap->assert_pinned_region_status(); + } + + { + // This doesn't actually choose a collection set, but prepares a list of + // regions as 'candidates' for inclusion in a mixed collection. + ShenandoahGCPhase phase(concurrent ? + ShenandoahPhaseTimings::choose_cset : + ShenandoahPhaseTimings::degen_gc_choose_cset); + ShenandoahHeapLocker locker(heap->lock()); + _old_heuristics->prepare_for_old_collections(); + } + + { + // Though we did not choose a collection set above, we still may have + // freed up immediate garbage regions so proceed with rebuilding the free set. + ShenandoahGCPhase phase(concurrent ? + ShenandoahPhaseTimings::final_rebuild_freeset : + ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); + ShenandoahHeapLocker locker(heap->lock()); + size_t cset_young_regions, cset_old_regions; + size_t first_old, last_old, num_old; + heap->free_set()->prepare_to_rebuild(cset_young_regions, cset_old_regions, first_old, last_old, num_old); + // This is just old-gen completion. No future budgeting required here. The only reason to rebuild the freeset here + // is in case there was any immediate old garbage identified. + heap->free_set()->finish_rebuild(cset_young_regions, cset_old_regions, num_old); + } +} + +const char* ShenandoahOldGeneration::state_name(State state) { + switch (state) { + case WAITING_FOR_BOOTSTRAP: return "Waiting for Bootstrap"; + case FILLING: return "Coalescing"; + case BOOTSTRAPPING: return "Bootstrapping"; + case MARKING: return "Marking"; + case EVACUATING: return "Evacuating"; + case EVACUATING_AFTER_GLOBAL: return "Evacuating (G)"; + default: + ShouldNotReachHere(); + return "Unknown"; + } +} + +void ShenandoahOldGeneration::transition_to(State new_state) { + if (_state != new_state) { + log_debug(gc)("Old generation transition from %s to %s", state_name(_state), state_name(new_state)); + EventMark event("Old was %s, now is %s", state_name(_state), state_name(new_state)); + validate_transition(new_state); + _state = new_state; + } +} + +#ifdef ASSERT +// This diagram depicts the expected state transitions for marking the old generation +// and preparing for old collections. When a young generation cycle executes, the +// remembered set scan must visit objects in old regions. Visiting an object which +// has become dead on previous old cycles will result in crashes. To avoid visiting +// such objects, the remembered set scan will use the old generation mark bitmap when +// possible. It is _not_ possible to use the old generation bitmap when old marking +// is active (bitmap is not complete). For this reason, the old regions are made +// parsable _before_ the old generation bitmap is reset. The diagram does not depict +// cancellation of old collections by global or full collections. +// +// When a global collection supersedes an old collection, the global mark still +// "completes" the old mark bitmap. Subsequent remembered set scans may use the +// old generation mark bitmap, but any uncollected old regions must still be made parsable +// before the next old generation cycle begins. For this reason, a global collection may +// create mixed collection candidates and coalesce and fill candidates and will put +// the old generation in the respective states (EVACUATING or FILLING). After a Full GC, +// the mark bitmaps are all reset, all regions are parsable and the mark context will +// not be "complete". After a Full GC, remembered set scans will _not_ use the mark bitmap +// and we expect the old generation to be waiting for bootstrap. +// +// +-----------------+ +// +------------> | FILLING | <---+ +// | +--------> | | | +// | | +-----------------+ | +// | | | | +// | | | Filling Complete | <-> A global collection may +// | | v | move the old generation +// | | +-----------------+ | directly from waiting for +// +-- |-- |--------> | WAITING | | bootstrap to filling or +// | | | +---- | FOR BOOTSTRAP | ----+ evacuating. It may also +// | | | | +-----------------+ move from filling to waiting +// | | | | | for bootstrap. +// | | | | | Reset Bitmap +// | | | | v +// | | | | +-----------------+ +----------------------+ +// | | | | | BOOTSTRAP | <-> | YOUNG GC | +// | | | | | | | (RSet Parses Region) | +// | | | | +-----------------+ +----------------------+ +// | | | | | +// | | | | | Old Marking +// | | | | v +// | | | | +-----------------+ +----------------------+ +// | | | | | MARKING | <-> | YOUNG GC | +// | | +--------- | | | (RSet Parses Region) | +// | | | +-----------------+ +----------------------+ +// | | | | +// | | | | Has Evacuation Candidates +// | | | v +// | | | +-----------------+ +--------------------+ +// | | +---> | EVACUATING | <-> | YOUNG GC | +// | +------------- | | | (RSet Uses Bitmap) | +// | +-----------------+ +--------------------+ +// | | +// | | Global Cycle Coalesces and Fills Old Regions +// | v +// | +-----------------+ +--------------------+ +// +----------------- | EVACUATING | <-> | YOUNG GC | +// | AFTER GLOBAL | | (RSet Uses Bitmap) | +// +-----------------+ +--------------------+ +// +// +void ShenandoahOldGeneration::validate_transition(State new_state) { + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + switch (new_state) { + case FILLING: + assert(_state != BOOTSTRAPPING, "Cannot begin making old regions parsable after bootstrapping"); + assert(is_mark_complete(), "Cannot begin filling without first completing marking, state is '%s'", state_name(_state)); + assert(_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot begin filling without something to fill."); + break; + case WAITING_FOR_BOOTSTRAP: + // GC cancellation can send us back here from any state. + validate_waiting_for_bootstrap(); + break; + case BOOTSTRAPPING: + assert(_state == WAITING_FOR_BOOTSTRAP, "Cannot reset bitmap without making old regions parsable, state is '%s'", state_name(_state)); + assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot bootstrap with mixed collection candidates"); + assert(!heap->is_prepare_for_old_mark_in_progress(), "Cannot still be making old regions parsable."); + break; + case MARKING: + assert(_state == BOOTSTRAPPING, "Must have finished bootstrapping before marking, state is '%s'", state_name(_state)); + assert(heap->young_generation()->old_gen_task_queues() != nullptr, "Young generation needs old mark queues."); + assert(heap->is_concurrent_old_mark_in_progress(), "Should be marking old now."); + break; + case EVACUATING_AFTER_GLOBAL: + assert(_state == EVACUATING, "Must have been evacuating, state is '%s'", state_name(_state)); + break; + case EVACUATING: + assert(_state == WAITING_FOR_BOOTSTRAP || _state == MARKING, "Cannot have old collection candidates without first marking, state is '%s'", state_name(_state)); + assert(_old_heuristics->unprocessed_old_collection_candidates() > 0, "Must have collection candidates here."); + break; + default: + fatal("Unknown new state"); + } +} + +bool ShenandoahOldGeneration::validate_waiting_for_bootstrap() { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + assert(!heap->is_concurrent_old_mark_in_progress(), "Cannot become ready for bootstrap during old mark."); + assert(heap->young_generation()->old_gen_task_queues() == nullptr, "Cannot become ready for bootstrap when still setup for bootstrapping."); + assert(!is_concurrent_mark_in_progress(), "Cannot be marking in IDLE"); + assert(!heap->young_generation()->is_bootstrap_cycle(), "Cannot have old mark queues if IDLE"); + assert(!_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot have coalesce and fill candidates in IDLE"); + assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot have mixed collection candidates in IDLE"); + return true; +} +#endif + +ShenandoahHeuristics* ShenandoahOldGeneration::initialize_heuristics(ShenandoahMode* gc_mode) { + _old_heuristics = new ShenandoahOldHeuristics(this, ShenandoahGenerationalHeap::heap()); + _old_heuristics->set_guaranteed_gc_interval(ShenandoahGuaranteedOldGCInterval); + _heuristics = _old_heuristics; + return _heuristics; +} + +void ShenandoahOldGeneration::record_success_concurrent(bool abbreviated) { + heuristics()->record_success_concurrent(); + ShenandoahHeap::heap()->shenandoah_policy()->record_success_old(); +} + +void ShenandoahOldGeneration::handle_failed_evacuation() { + if (_failed_evacuation.try_set()) { + log_debug(gc)("Old gen evac failure."); + } +} + +void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { + // We squelch excessive reports to reduce noise in logs. + const size_t MaxReportsPerEpoch = 4; + static size_t last_report_epoch = 0; + static size_t epoch_report_count = 0; + auto heap = ShenandoahGenerationalHeap::heap(); + + size_t promotion_reserve; + size_t promotion_expended; + + const size_t gc_id = heap->control_thread()->get_gc_id(); + + if ((gc_id != last_report_epoch) || (epoch_report_count++ < MaxReportsPerEpoch)) { + { + // Promotion failures should be very rare. Invest in providing useful diagnostic info. + ShenandoahHeapLocker locker(heap->lock()); + promotion_reserve = get_promoted_reserve(); + promotion_expended = get_promoted_expended(); + } + PLAB* const plab = ShenandoahThreadLocalData::plab(thread); + const size_t words_remaining = (plab == nullptr)? 0: plab->words_remaining(); + const char* promote_enabled = ShenandoahThreadLocalData::allow_plab_promotions(thread)? "enabled": "disabled"; + + log_info(gc, ergo)("Promotion failed, size " SIZE_FORMAT ", has plab? %s, PLAB remaining: " SIZE_FORMAT + ", plab promotions %s, promotion reserve: " SIZE_FORMAT ", promotion expended: " SIZE_FORMAT + ", old capacity: " SIZE_FORMAT ", old_used: " SIZE_FORMAT ", old unaffiliated regions: " SIZE_FORMAT, + size * HeapWordSize, plab == nullptr? "no": "yes", + words_remaining * HeapWordSize, promote_enabled, promotion_reserve, promotion_expended, + max_capacity(), used(), free_unaffiliated_regions()); + + if ((gc_id == last_report_epoch) && (epoch_report_count >= MaxReportsPerEpoch)) { + log_debug(gc, ergo)("Squelching additional promotion failure reports for current epoch"); + } else if (gc_id != last_report_epoch) { + last_report_epoch = gc_id; + epoch_report_count = 1; + } + } +} + +void ShenandoahOldGeneration::handle_evacuation(HeapWord* obj, size_t words, bool promotion) { + // Only register the copy of the object that won the evacuation race. + _card_scan->register_object_without_lock(obj); + + // Mark the entire range of the evacuated object as dirty. At next remembered set scan, + // we will clear dirty bits that do not hold interesting pointers. It's more efficient to + // do this in batch, in a background GC thread than to try to carefully dirty only cards + // that hold interesting pointers right now. + _card_scan->mark_range_as_dirty(obj, words); + + if (promotion) { + // This evacuation was a promotion, track this as allocation against old gen + increase_allocated(words * HeapWordSize); + } +} + +bool ShenandoahOldGeneration::has_unprocessed_collection_candidates() { + return _old_heuristics->unprocessed_old_collection_candidates() > 0; +} + +size_t ShenandoahOldGeneration::unprocessed_collection_candidates_live_memory() { + return _old_heuristics->unprocessed_old_collection_candidates_live_memory(); +} + +void ShenandoahOldGeneration::abandon_collection_candidates() { + _old_heuristics->abandon_collection_candidates(); +} + +void ShenandoahOldGeneration::prepare_for_mixed_collections_after_global_gc() { + assert(is_mark_complete(), "Expected old generation mark to be complete after global cycle."); + _old_heuristics->prepare_for_old_collections(); + log_info(gc, ergo)("After choosing global collection set, mixed candidates: " UINT32_FORMAT ", coalescing candidates: " SIZE_FORMAT, + _old_heuristics->unprocessed_old_collection_candidates(), + _old_heuristics->coalesce_and_fill_candidates_count()); +} + +void ShenandoahOldGeneration::parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) { + // Iterate over old and free regions (exclude young). + ShenandoahExcludeRegionClosure exclude_cl(cl); + ShenandoahGeneration::parallel_heap_region_iterate_free(&exclude_cl); +} + +void ShenandoahOldGeneration::set_parsable(bool parsable) { + _is_parsable = parsable; + if (_is_parsable) { + // The current state would have been chosen during final mark of the global + // collection, _before_ any decisions about class unloading have been made. + // + // After unloading classes, we have made the old generation regions parsable. + // We can skip filling or transition to a state that knows everything has + // already been filled. + switch (state()) { + case ShenandoahOldGeneration::EVACUATING: + transition_to(ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL); + break; + case ShenandoahOldGeneration::FILLING: + assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Expected no mixed collection candidates"); + assert(_old_heuristics->coalesce_and_fill_candidates_count() > 0, "Expected coalesce and fill candidates"); + // When the heuristic put the old generation in this state, it didn't know + // that we would unload classes and make everything parsable. But, we know + // that now so we can override this state. + abandon_collection_candidates(); + transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + break; + default: + // We can get here during a full GC. The full GC will cancel anything + // happening in the old generation and return it to the waiting for bootstrap + // state. The full GC will then record that the old regions are parsable + // after rebuilding the remembered set. + assert(is_idle(), "Unexpected state %s at end of global GC", state_name()); + break; + } + } +} + +void ShenandoahOldGeneration::complete_mixed_evacuations() { + assert(is_doing_mixed_evacuations(), "Mixed evacuations should be in progress"); + if (!_old_heuristics->has_coalesce_and_fill_candidates()) { + // No candidate regions to coalesce and fill + transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + return; + } + + if (state() == ShenandoahOldGeneration::EVACUATING) { + transition_to(ShenandoahOldGeneration::FILLING); + return; + } + + // Here, we have no more candidates for mixed collections. The candidates for coalescing + // and filling have already been processed during the global cycle, so there is nothing + // more to do. + assert(state() == ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL, "Should be evacuating after a global cycle"); + abandon_collection_candidates(); + transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); +} + +void ShenandoahOldGeneration::abandon_mixed_evacuations() { + switch(state()) { + case ShenandoahOldGeneration::EVACUATING: + transition_to(ShenandoahOldGeneration::FILLING); + break; + case ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL: + abandon_collection_candidates(); + transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + break; + default: + log_warning(gc)("Abandon mixed evacuations in unexpected state: %s", state_name(state())); + ShouldNotReachHere(); + break; + } +} + +void ShenandoahOldGeneration::clear_cards_for(ShenandoahHeapRegion* region) { + _card_scan->mark_range_as_empty(region->bottom(), pointer_delta(region->end(), region->bottom())); +} + +void ShenandoahOldGeneration::mark_card_as_dirty(void* location) { + _card_scan->mark_card_as_dirty((HeapWord*)location); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp new file mode 100644 index 00000000000..ed12650b8ce --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -0,0 +1,324 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP + +#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.hpp" +#include "gc/shenandoah/shenandoahSharedVariables.hpp" + +class ShenandoahHeapRegion; +class ShenandoahHeapRegionClosure; +class ShenandoahOldHeuristics; + +class ShenandoahOldGeneration : public ShenandoahGeneration { +private: + ShenandoahHeapRegion** _coalesce_and_fill_region_array; + ShenandoahOldHeuristics* _old_heuristics; + + // After determining the desired size of the old generation (see compute_old_generation_balance), this + // quantity represents the number of regions above (surplus) or below (deficit) that size. + // This value is computed prior to the actual exchange of any regions. A positive value represents + // a surplus of old regions which will be transferred from old _to_ young. A negative value represents + // a deficit of regions that will be replenished by a transfer _from_ young to old. + ssize_t _region_balance; + + // Set when evacuation in the old generation fails. When this is set, the control thread will initiate a + // full GC instead of a futile degenerated cycle. + ShenandoahSharedFlag _failed_evacuation; + + // Bytes reserved within old-gen to hold the results of promotion. This is separate from + // and in addition to the evacuation reserve for intra-generation evacuations (ShenandoahGeneration::_evacuation_reserve). + // If there is more data ready to be promoted than can fit within this reserve, the promotion of some objects will be + // deferred until a subsequent evacuation pass. + size_t _promoted_reserve; + + // Bytes of old-gen memory expended on promotions. This may be modified concurrently + // by mutators and gc workers when promotion LABs are retired during evacuation. It + // is therefore always accessed through atomic operations. This is increased when a + // PLAB is allocated for promotions. The value is decreased by the amount of memory + // remaining in a PLAB when it is retired. + size_t _promoted_expended; + + // Represents the quantity of live bytes we expect to promote in place during the next + // evacuation cycle. This value is used by the young heuristic to trigger mixed collections. + // It is also used when computing the optimum size for the old generation. + size_t _promotion_potential; + + // When a region is selected to be promoted in place, the remaining free memory is filled + // in to prevent additional allocations (preventing premature promotion of newly allocated + // objects. This field records the total amount of padding used for such regions. + size_t _pad_for_promote_in_place; + + // During construction of the collection set, we keep track of regions that are eligible + // for promotion in place. These fields track the count of those humongous and regular regions. + // This data is used to force the evacuation phase even when the collection set is otherwise + // empty. + size_t _promotable_humongous_regions; + size_t _promotable_regular_regions; + + // True if old regions may be safely traversed by the remembered set scan. + bool _is_parsable; + + bool coalesce_and_fill(); + +public: + ShenandoahOldGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity); + + ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override; + + const char* name() const override { + return "Old"; + } + + ShenandoahOldHeuristics* heuristics() const override { + return _old_heuristics; + } + + // See description in field declaration + void set_promoted_reserve(size_t new_val); + size_t get_promoted_reserve() const; + + // The promotion reserve is increased when rebuilding the free set transfers a region to the old generation + void augment_promoted_reserve(size_t increment); + + // This zeros out the expended promotion count after the promotion reserve is computed + void reset_promoted_expended(); + + // This is incremented when allocations are made to copy promotions into the old generation + size_t expend_promoted(size_t increment); + + // This is used to return unused memory from a retired promotion LAB + size_t unexpend_promoted(size_t decrement); + + // This is used on the allocation path to gate promotions that would exceed the reserve + size_t get_promoted_expended() const; + + // Test if there is enough memory reserved for this promotion + bool can_promote(size_t requested_bytes) const { + size_t promotion_avail = get_promoted_reserve(); + size_t promotion_expended = get_promoted_expended(); + return promotion_expended + requested_bytes <= promotion_avail; + } + + // Test if there is enough memory available in the old generation to accommodate this request. + // The request will be subject to constraints on promotion and evacuation reserves. + bool can_allocate(const ShenandoahAllocRequest& req) const; + + // Updates the promotion expenditure tracking and configures whether the plab may be used + // for promotions and evacuations, or just evacuations. + void configure_plab_for_current_thread(const ShenandoahAllocRequest &req); + + // See description in field declaration + void set_region_balance(ssize_t balance) { _region_balance = balance; } + ssize_t get_region_balance() const { return _region_balance; } + // See description in field declaration + void set_promotion_potential(size_t val) { _promotion_potential = val; }; + size_t get_promotion_potential() const { return _promotion_potential; }; + + // See description in field declaration + void set_pad_for_promote_in_place(size_t pad) { _pad_for_promote_in_place = pad; } + size_t get_pad_for_promote_in_place() const { return _pad_for_promote_in_place; } + + // See description in field declaration + void set_expected_humongous_region_promotions(size_t region_count) { _promotable_humongous_regions = region_count; } + void set_expected_regular_region_promotions(size_t region_count) { _promotable_regular_regions = region_count; } + size_t get_expected_in_place_promotions() const { return _promotable_humongous_regions + _promotable_regular_regions; } + bool has_in_place_promotions() const { return get_expected_in_place_promotions() > 0; } + + // Class unloading may render the card table offsets unusable, if they refer to unmarked objects + bool is_parsable() const { return _is_parsable; } + void set_parsable(bool parsable); + + // This will signal the heuristic to trigger an old generation collection + void handle_failed_transfer(); + + // This will signal the control thread to run a full GC instead of a futile degenerated gc + void handle_failed_evacuation(); + + // This logs that an evacuation to the old generation has failed + void handle_failed_promotion(Thread* thread, size_t size); + + // A successful evacuation re-dirties the cards and registers the object with the remembered set + void handle_evacuation(HeapWord* obj, size_t words, bool promotion); + + // Clear the flag after it is consumed by the control thread + bool clear_failed_evacuation() { + return _failed_evacuation.try_unset(); + } + + // Transition to the next state after mixed evacuations have completed + void complete_mixed_evacuations(); + + // Abandon any future mixed collections. This is invoked when all old regions eligible for + // inclusion in a mixed evacuation are pinned. This should be rare. + void abandon_mixed_evacuations(); + +private: + ShenandoahScanRemembered* _card_scan; + +public: + ShenandoahScanRemembered* card_scan() { return _card_scan; } + + // Clear cards for given region + void clear_cards_for(ShenandoahHeapRegion* region); + + // Mark card for this location as dirty + void mark_card_as_dirty(void* location); + + void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + + void parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) override; + + void heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + + bool contains(ShenandoahAffiliation affiliation) const override; + bool contains(ShenandoahHeapRegion* region) const override; + bool contains(oop obj) const override; + + void set_concurrent_mark_in_progress(bool in_progress) override; + bool is_concurrent_mark_in_progress() override; + + bool entry_coalesce_and_fill(); + void prepare_for_mixed_collections_after_global_gc(); + void prepare_gc() override; + void prepare_regions_and_collection_set(bool concurrent) override; + void record_success_concurrent(bool abbreviated) override; + void cancel_marking() override; + + // Cancels old gc and transitions to the idle state + void cancel_gc(); + + // We leave the SATB barrier on for the entirety of the old generation + // marking phase. In some cases, this can cause a write to a perfectly + // reachable oop to enqueue a pointer that later becomes garbage (because + // it points at an object that is later chosen for the collection set). There are + // also cases where the referent of a weak reference ends up in the SATB + // and is later collected. In these cases the oop in the SATB buffer becomes + // invalid and the _next_ cycle will crash during its marking phase. To + // avoid this problem, we "purge" the SATB buffers during the final update + // references phase if (and only if) an old generation mark is in progress. + // At this stage we can safely determine if any of the oops in the SATB + // buffer belong to trashed regions (before they are recycled). As it + // happens, flushing a SATB queue also filters out oops which have already + // been marked - which is the case for anything that is being evacuated + // from the collection set. + // + // Alternatively, we could inspect the state of the heap and the age of the + // object at the barrier, but we reject this approach because it is likely + // the performance impact would be too severe. + void transfer_pointers_from_satb(); + + // True if there are old regions waiting to be selected for a mixed collection + bool has_unprocessed_collection_candidates(); + + bool is_doing_mixed_evacuations() const { + return state() == EVACUATING || state() == EVACUATING_AFTER_GLOBAL; + } + + bool is_preparing_for_mark() const { + return state() == FILLING; + } + + bool is_idle() const { + return state() == WAITING_FOR_BOOTSTRAP; + } + + bool is_bootstrapping() const { + return state() == BOOTSTRAPPING; + } + + // Amount of live memory (bytes) in regions waiting for mixed collections + size_t unprocessed_collection_candidates_live_memory(); + + // Abandon any regions waiting for mixed collections + void abandon_collection_candidates(); + +public: + enum State { + FILLING, WAITING_FOR_BOOTSTRAP, BOOTSTRAPPING, MARKING, EVACUATING, EVACUATING_AFTER_GLOBAL + }; + +#ifdef ASSERT + bool validate_waiting_for_bootstrap(); +#endif + +private: + State _state; + + static const size_t FRACTIONAL_DENOMINATOR = 65536; + + // During initialization of the JVM, we search for the correct old-gen size by initially performing old-gen + // collection when old-gen usage is 50% more (INITIAL_GROWTH_BEFORE_COMPACTION) than the initial old-gen size + // estimate (3.125% of heap). The next old-gen trigger occurs when old-gen grows 25% larger than its live + // memory at the end of the first old-gen collection. Then we trigger again when old-gen grows 12.5% + // more than its live memory at the end of the previous old-gen collection. Thereafter, we trigger each time + // old-gen grows more than 12.5% following the end of its previous old-gen collection. + static const size_t INITIAL_GROWTH_BEFORE_COMPACTION = FRACTIONAL_DENOMINATOR / 2; // 50.0% + + // INITIAL_LIVE_FRACTION represents the initial guess of how large old-gen should be. We estimate that old-gen + // needs to consume 6.25% of the total heap size. And we "pretend" that we start out with this amount of live + // old-gen memory. The first old-collection trigger will occur when old-gen occupies 50% more than this initial + // approximation of the old-gen memory requirement, in other words when old-gen usage is 150% of 6.25%, which + // is 9.375% of the total heap size. + static const uint16_t INITIAL_LIVE_FRACTION = FRACTIONAL_DENOMINATOR / 16; // 6.25% + + size_t _live_bytes_after_last_mark; + + // How much growth in usage before we trigger old collection, per FRACTIONAL_DENOMINATOR (65_536) + size_t _growth_before_compaction; + const size_t _min_growth_before_compaction; // Default is 12.5% + + void validate_transition(State new_state) NOT_DEBUG_RETURN; + +public: + State state() const { + return _state; + } + + const char* state_name() const { + return state_name(_state); + } + + void transition_to(State new_state); + + size_t get_live_bytes_after_last_mark() const; + void set_live_bytes_after_last_mark(size_t new_live); + + size_t usage_trigger_threshold() const; + + bool can_start_gc() { + return _state == WAITING_FOR_BOOTSTRAP; + } + + static const char* state_name(State state); + +}; + + +#endif //SHARE_VM_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp index 50fd543851f..f247308ec56 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -97,6 +98,7 @@ bool ShenandoahPhaseTimings::is_worker_phase(Phase phase) { assert(phase >= 0 && phase < _num_phases, "Out of bounds"); switch (phase) { case init_evac: + case init_scan_rset: case finish_mark: case purge_weak_par: case full_gc_mark: @@ -108,15 +110,18 @@ bool ShenandoahPhaseTimings::is_worker_phase(Phase phase) { case full_gc_weakrefs: case full_gc_purge_class_unload: case full_gc_purge_weak_par: + case degen_gc_coalesce_and_fill: case degen_gc_weakrefs: case degen_gc_purge_class_unload: case degen_gc_purge_weak_par: case heap_iteration_roots: + case conc_mark: case conc_mark_roots: case conc_thread_roots: case conc_weak_roots_work: case conc_weak_refs: case conc_strong_roots: + case conc_coalesce_and_fill: return true; default: return false; @@ -312,17 +317,17 @@ void ShenandoahPhaseTimings::print_global_on(outputStream* out) const { } ShenandoahWorkerTimingsTracker::ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::Phase phase, - ShenandoahPhaseTimings::ParPhase par_phase, uint worker_id) : + ShenandoahPhaseTimings::ParPhase par_phase, uint worker_id, bool cumulative) : _timings(ShenandoahHeap::heap()->phase_timings()), _phase(phase), _par_phase(par_phase), _worker_id(worker_id) { - assert(_timings->worker_data(_phase, _par_phase)->get(_worker_id) == ShenandoahWorkerData::uninitialized(), + assert(_timings->worker_data(_phase, _par_phase)->get(_worker_id) == ShenandoahWorkerData::uninitialized() || cumulative, "Should not be set yet: %s", ShenandoahPhaseTimings::phase_name(_timings->worker_par_phase(_phase, _par_phase))); _start_time = os::elapsedTime(); } ShenandoahWorkerTimingsTracker::~ShenandoahWorkerTimingsTracker() { - _timings->worker_data(_phase, _par_phase)->set(_worker_id, os::elapsedTime() - _start_time); + _timings->worker_data(_phase, _par_phase)->set_or_add(_worker_id, os::elapsedTime() - _start_time); if (ShenandoahPhaseTimings::is_root_work_phase(_phase)) { ShenandoahPhaseTimings::Phase root_phase = _phase; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index 01c83e08d37..05ab60c0bb6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -44,19 +45,26 @@ class outputStream; f(CNT_PREFIX ## CLDUnlink, DESC_PREFIX "Unlink CLDs") \ f(CNT_PREFIX ## WeakRefProc, DESC_PREFIX "Weak References") \ f(CNT_PREFIX ## ParallelMark, DESC_PREFIX "Parallel Mark") \ + f(CNT_PREFIX ## ScanClusters, DESC_PREFIX "Scan Clusters") \ // end #define SHENANDOAH_PHASE_DO(f) \ f(conc_reset, "Concurrent Reset") \ - \ + f(conc_reset_old, "Concurrent Reset (OLD)") \ f(init_mark_gross, "Pause Init Mark (G)") \ f(init_mark, "Pause Init Mark (N)") \ f(init_manage_tlabs, " Manage TLABs") \ + f(init_swap_rset, " Swap Remembered Set") \ + f(init_transfer_satb, " Transfer Old From SATB") \ f(init_update_region_states, " Update Region States") \ \ + f(init_scan_rset, "Concurrent Scan Remembered Set") \ + SHENANDOAH_PAR_PHASE_DO(init_scan_rset_, " RS: ", f) \ + \ f(conc_mark_roots, "Concurrent Mark Roots ") \ SHENANDOAH_PAR_PHASE_DO(conc_mark_roots, " CMR: ", f) \ f(conc_mark, "Concurrent Marking") \ + SHENANDOAH_PAR_PHASE_DO(conc_mark, " CM: ", f) \ f(conc_mark_satb_flush, " Flush SATB") \ \ f(final_mark_gross, "Pause Final Mark (G)") \ @@ -96,7 +104,7 @@ class outputStream; f(conc_strong_roots, "Concurrent Strong Roots") \ SHENANDOAH_PAR_PHASE_DO(conc_strong_roots_, " CSR: ", f) \ f(conc_evac, "Concurrent Evacuation") \ - \ + f(promote_in_place, "Concurrent Promote Regions") \ f(final_roots_gross, "Pause Final Roots (G)") \ f(final_roots, "Pause Final Roots (N)") \ \ @@ -115,6 +123,8 @@ class outputStream; f(final_update_refs_rebuild_freeset, " Rebuild Free Set") \ \ f(conc_cleanup_complete, "Concurrent Cleanup") \ + f(conc_coalesce_and_fill, "Concurrent Coalesce and Fill") \ + SHENANDOAH_PAR_PHASE_DO(conc_coalesce_, " CC&F: ", f) \ \ f(degen_gc_gross, "Pause Degenerated GC (G)") \ f(degen_gc, "Pause Degenerated GC (N)") \ @@ -144,6 +154,9 @@ class outputStream; f(degen_gc_update_roots, " Degen Update Roots") \ SHENANDOAH_PAR_PHASE_DO(degen_gc_update_, " DU: ", f) \ f(degen_gc_cleanup_complete, " Cleanup") \ + f(degen_gc_promote_regions, " Degen Promote Regions") \ + f(degen_gc_coalesce_and_fill, " Degen Coalesce and Fill") \ + SHENANDOAH_PAR_PHASE_DO(degen_coalesce_, " DC&F", f) \ \ f(full_gc_gross, "Pause Full GC (G)") \ f(full_gc, "Pause Full GC (N)") \ @@ -170,8 +183,10 @@ class outputStream; f(full_gc_copy_objects, " Copy Objects") \ f(full_gc_copy_objects_regular, " Regular Objects") \ f(full_gc_copy_objects_humong, " Humongous Objects") \ + f(full_gc_recompute_generation_usage, " Recompute generation usage") \ f(full_gc_copy_objects_reset_complete, " Reset Complete Bitmap") \ f(full_gc_copy_objects_rebuild, " Rebuild Region Sets") \ + f(full_gc_reconstruct_remembered_set, " Reconstruct Remembered Set") \ f(full_gc_heapdump_post, " Post Heap Dump") \ \ f(conc_uncommit, "Concurrent Uncommit") \ @@ -250,7 +265,10 @@ class ShenandoahWorkerTimingsTracker : public StackObj { double _start_time; EventGCPhaseParallel _event; public: - ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::Phase phase, ShenandoahPhaseTimings::ParPhase par_phase, uint worker_id); + ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::Phase phase, + ShenandoahPhaseTimings::ParPhase par_phase, + uint worker_id, + bool cumulative = false); ~ShenandoahWorkerTimingsTracker(); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index fe1d6d69bd8..deddf984625 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Red Hat, Inc. and/or its affiliates. + * Copyright Amazon.com Inc. 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 @@ -26,8 +27,10 @@ #include "precompiled.hpp" #include "classfile/javaClasses.hpp" #include "gc/shared/workerThread.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "runtime/atomic.hpp" @@ -57,17 +60,40 @@ static const char* reference_type_name(ReferenceType type) { } } +template +static void card_mark_barrier(T* field, oop value) { + assert(ShenandoahCardBarrier, "Card-mark barrier should be on"); + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + assert(heap->is_in_or_null(value), "Should be in heap"); + if (heap->is_in_old(field) && heap->is_in_young(value)) { + // For Shenandoah, each generation collects all the _referents_ that belong to the + // collected generation. We can end up with discovered lists that contain a mixture + // of old and young _references_. These references are linked together through the + // discovered field in java.lang.Reference. In some cases, creating or editing this + // list may result in the creation of _new_ old-to-young pointers which must dirty + // the corresponding card. Failing to do this may cause heap verification errors and + // lead to incorrect GC behavior. + heap->old_generation()->mark_card_as_dirty(field); + } +} + template static void set_oop_field(T* field, oop value); template <> void set_oop_field(oop* field, oop value) { *field = value; + if (ShenandoahCardBarrier) { + card_mark_barrier(field, value); + } } template <> void set_oop_field(narrowOop* field, oop value) { *field = CompressedOops::encode(value); + if (ShenandoahCardBarrier) { + card_mark_barrier(field, value); + } } static oop lrb(oop obj) { @@ -268,6 +294,7 @@ bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(reference); T heap_oop = RawAccess<>::oop_load(referent_addr); oop referent = CompressedOops::decode(heap_oop); + ShenandoahHeap* heap = ShenandoahHeap::heap(); if (is_inactive(reference, referent, type)) { log_trace(gc,ref)("Reference inactive: " PTR_FORMAT, p2i(reference)); @@ -284,6 +311,11 @@ bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType return false; } + if (!heap->is_in_active_generation(referent)) { + log_trace(gc,ref)("Referent outside of active generation: " PTR_FORMAT, p2i(referent)); + return false; + } + return true; } @@ -349,6 +381,9 @@ bool ShenandoahReferenceProcessor::discover(oop reference, ReferenceType type, u } // Add reference to discovered list + // Each worker thread has a private copy of refproc_data, which includes a private discovered list. This means + // there's no risk that a different worker thread will try to manipulate my discovered list head while I'm making + // reference the head of my discovered list. ShenandoahRefProcThreadLocal& refproc_data = _ref_proc_thread_locals[worker_id]; oop discovered_head = refproc_data.discovered_list_head(); if (discovered_head == nullptr) { @@ -357,6 +392,17 @@ bool ShenandoahReferenceProcessor::discover(oop reference, ReferenceType type, u discovered_head = reference; } if (reference_cas_discovered(reference, discovered_head)) { + // We successfully set this reference object's next pointer to discovered_head. This marks reference as discovered. + // If reference_cas_discovered fails, that means some other worker thread took credit for discovery of this reference, + // and that other thread will place reference on its discovered list, so I can ignore reference. + + // In case we have created an interesting pointer, mark the remembered set card as dirty. + if (ShenandoahCardBarrier) { + T* addr = reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); + card_mark_barrier(addr, discovered_head); + } + + // Make the discovered_list_head point to reference. refproc_data.set_discovered_list_head(reference); assert(refproc_data.discovered_list_head() == reference, "reference must be new discovered head"); log_trace(gc, ref)("Discovered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); @@ -371,7 +417,8 @@ bool ShenandoahReferenceProcessor::discover_reference(oop reference, ReferenceTy return false; } - log_trace(gc, ref)("Encountered Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); + log_trace(gc, ref)("Encountered Reference: " PTR_FORMAT " (%s, %s)", + p2i(reference), reference_type_name(type), ShenandoahHeap::heap()->heap_region_containing(reference)->affiliation_name()); uint worker_id = WorkerThread::worker_id(); _ref_proc_thread_locals[worker_id].inc_encountered(type); @@ -386,8 +433,9 @@ template oop ShenandoahReferenceProcessor::drop(oop reference, ReferenceType type) { log_trace(gc, ref)("Dropped Reference: " PTR_FORMAT " (%s)", p2i(reference), reference_type_name(type)); -#ifdef ASSERT HeapWord* raw_referent = reference_referent_raw(reference); + +#ifdef ASSERT assert(raw_referent == nullptr || ShenandoahHeap::heap()->marking_context()->is_marked(raw_referent), "only drop references with alive referents"); #endif @@ -395,6 +443,13 @@ oop ShenandoahReferenceProcessor::drop(oop reference, ReferenceType type) { // Unlink and return next in list oop next = reference_discovered(reference); reference_set_discovered(reference, nullptr); + // When this reference was discovered, it would not have been marked. If it ends up surviving + // the cycle, we need to dirty the card if the reference is old and the referent is young. Note + // that if the reference is not dropped, then its pointer to the referent will be nulled before + // evacuation begins so card does not need to be dirtied. + if (ShenandoahCardBarrier) { + card_mark_barrier(cast_from_oop(reference), cast_to_oop(raw_referent)); + } return next; } @@ -446,11 +501,12 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahRefProcThreadLoc } // Prepend discovered references to internal pending list + // set_oop_field maintains the card mark barrier as this list is constructed. if (!CompressedOops::is_null(*list)) { oop head = lrb(CompressedOops::decode_not_null(*list)); shenandoah_assert_not_in_cset_except(&head, head, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); oop prev = Atomic::xchg(&_pending_list, head); - RawAccess<>::oop_store(p, prev); + set_oop_field(p, prev); if (prev == nullptr) { // First to prepend to list, record tail _pending_list_tail = reinterpret_cast(p); @@ -522,10 +578,23 @@ void ShenandoahReferenceProcessor::process_references(ShenandoahPhaseTimings::Ph void ShenandoahReferenceProcessor::enqueue_references_locked() { // Prepend internal pending list to external pending list shenandoah_assert_not_in_cset_except(&_pending_list, _pending_list, ShenandoahHeap::heap()->cancelled_gc() || !ShenandoahLoadRefBarrier); + + // During reference processing, we maintain a local list of references that are identified by + // _pending_list and _pending_list_tail. _pending_list_tail points to the next field of the last Reference object on + // the local list. + // + // There is also a global list of reference identified by Universe::_reference_pending_list + + // The following code has the effect of: + // 1. Making the global Universe::_reference_pending_list point to my local list + // 2. Overwriting the next field of the last Reference on my local list to point at the previous head of the + // global Universe::_reference_pending_list + + oop former_head_of_global_list = Universe::swap_reference_pending_list(_pending_list); if (UseCompressedOops) { - *reinterpret_cast(_pending_list_tail) = CompressedOops::encode(Universe::swap_reference_pending_list(_pending_list)); + set_oop_field(reinterpret_cast(_pending_list_tail), former_head_of_global_list); } else { - *reinterpret_cast(_pending_list_tail) = Universe::swap_reference_pending_list(_pending_list); + set_oop_field(reinterpret_cast(_pending_list_tail), former_head_of_global_list); } } @@ -534,7 +603,6 @@ void ShenandoahReferenceProcessor::enqueue_references(bool concurrent) { // Nothing to enqueue return; } - if (!concurrent) { // When called from mark-compact or degen-GC, the locking is done by the VMOperation, enqueue_references_locked(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp new file mode 100644 index 00000000000..696e0a9b695 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -0,0 +1,164 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahRegulatorThread.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "logging/log.hpp" + +ShenandoahRegulatorThread::ShenandoahRegulatorThread(ShenandoahGenerationalControlThread* control_thread) : + ConcurrentGCThread(), + _control_thread(control_thread), + _sleep(ShenandoahControlIntervalMin), + _last_sleep_adjust_time(os::elapsedTime()) { + shenandoah_assert_generational(); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + _old_heuristics = heap->old_generation()->heuristics(); + _young_heuristics = heap->young_generation()->heuristics(); + _global_heuristics = heap->global_generation()->heuristics(); + + set_name("Shenandoah Regulator Thread"); + create_and_start(); +} + +void ShenandoahRegulatorThread::run_service() { + if (ShenandoahAllowOldMarkingPreemption) { + regulate_young_and_old_cycles(); + } else { + regulate_young_and_global_cycles(); + } + + log_debug(gc)("%s: Done.", name()); +} + +void ShenandoahRegulatorThread::regulate_young_and_old_cycles() { + while (!should_terminate()) { + ShenandoahGenerationalControlThread::GCMode mode = _control_thread->gc_mode(); + if (mode == ShenandoahGenerationalControlThread::none) { + if (should_start_metaspace_gc()) { + if (request_concurrent_gc(GLOBAL)) { + log_debug(gc)("Heuristics request for global (unload classes) accepted."); + } + } else { + if (_young_heuristics->should_start_gc()) { + // Give the old generation a chance to run. The old generation cycle + // begins with a 'bootstrap' cycle that will also collect young. + if (start_old_cycle()) { + log_debug(gc)("Heuristics request for old collection accepted"); + } else if (request_concurrent_gc(YOUNG)) { + log_debug(gc)("Heuristics request for young collection accepted"); + } + } + } + } else if (mode == ShenandoahGenerationalControlThread::servicing_old) { + if (start_young_cycle()) { + log_debug(gc)("Heuristics request to interrupt old for young collection accepted"); + } + } + + regulator_sleep(); + } +} + + +void ShenandoahRegulatorThread::regulate_young_and_global_cycles() { + while (!should_terminate()) { + if (_control_thread->gc_mode() == ShenandoahGenerationalControlThread::none) { + if (start_global_cycle()) { + log_debug(gc)("Heuristics request for global collection accepted."); + } else if (start_young_cycle()) { + log_debug(gc)("Heuristics request for young collection accepted."); + } + } + + regulator_sleep(); + } +} + +void ShenandoahRegulatorThread::regulator_sleep() { + // Wait before performing the next action. If allocation happened during this wait, + // we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle, + // back off exponentially. + double current = os::elapsedTime(); + + if (ShenandoahHeap::heap()->has_changed()) { + _sleep = ShenandoahControlIntervalMin; + } else if ((current - _last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ + _sleep = MIN2(ShenandoahControlIntervalMax, MAX2(1u, _sleep * 2)); + _last_sleep_adjust_time = current; + } + + os::naked_short_sleep(_sleep); + if (LogTarget(Debug, gc, thread)::is_enabled()) { + double elapsed = os::elapsedTime() - current; + double hiccup = elapsed - double(_sleep); + if (hiccup > 0.001) { + log_debug(gc, thread)("Regulator hiccup time: %.3fs", hiccup); + } + } +} + +bool ShenandoahRegulatorThread::start_old_cycle() { + return _old_heuristics->should_start_gc() && request_concurrent_gc(OLD); +} + +bool ShenandoahRegulatorThread::start_young_cycle() { + return _young_heuristics->should_start_gc() && request_concurrent_gc(YOUNG); +} + +bool ShenandoahRegulatorThread::start_global_cycle() { + return _global_heuristics->should_start_gc() && request_concurrent_gc(GLOBAL); +} + +bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGenerationType generation) { + double now = os::elapsedTime(); + bool accepted = _control_thread->request_concurrent_gc(generation); + if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) { + double wait_time = os::elapsedTime() - now; + if (wait_time > 0.001) { + log_debug(gc, thread)("Regulator waited %.3fs for control thread to acknowledge request.", wait_time); + } + } + return accepted; +} + +void ShenandoahRegulatorThread::stop_service() { + log_debug(gc)("%s: Stop requested.", name()); +} + +bool ShenandoahRegulatorThread::should_start_metaspace_gc() { + // The generational mode can, at present, only unload classes during a global + // cycle. For this reason, we treat an oom in metaspace as a _trigger_ for a + // global cycle. But, we check other prerequisites before starting a gc that won't + // unload anything. + return ClassUnloadingWithConcurrentMark + && _global_heuristics->can_unload_classes() + && _global_heuristics->has_metaspace_oom(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp new file mode 100644 index 00000000000..f9f7e25f97c --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp @@ -0,0 +1,85 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHREGULATORTHREAD_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHREGULATORTHREAD_HPP + +#include "gc/shared/concurrentGCThread.hpp" + +class ShenandoahHeuristics; +class ShenandoahGenerationalControlThread; + +/* + * The purpose of this class (and thread) is to allow us to continue + * to evaluate heuristics during a garbage collection. This is necessary + * to allow young generation collections to interrupt an old generation + * collection which is in-progress. This puts heuristic triggers on the + * same footing as other gc requests (alloc failure, System.gc, etc.). + * However, this regulator does not block after submitting a gc request. + * + * We could use a PeriodicTask for this, but this thread will sleep longer + * when the allocation rate is lower and PeriodicTasks cannot adjust their + * sleep time. + */ +class ShenandoahRegulatorThread: public ConcurrentGCThread { + friend class VMStructs; + + public: + explicit ShenandoahRegulatorThread(ShenandoahGenerationalControlThread* control_thread); + + protected: + void run_service() override; + void stop_service() override; + + private: + // When mode is generational + void regulate_young_and_old_cycles(); + // When mode is generational, but ShenandoahAllowOldMarkingPreemption is false + void regulate_young_and_global_cycles(); + + // These return true if a cycle was started. + bool start_old_cycle(); + bool start_young_cycle(); + bool start_global_cycle(); + + // The generational mode can only unload classes in a global cycle. The regulator + // thread itself will trigger a global cycle if metaspace is out of memory. + bool should_start_metaspace_gc(); + + // Regulator will sleep longer when the allocation rate is lower. + void regulator_sleep(); + + // Provides instrumentation to track how long it takes to acknowledge a request. + bool request_concurrent_gc(ShenandoahGenerationType generation); + + ShenandoahGenerationalControlThread* _control_thread; + ShenandoahHeuristics* _young_heuristics; + ShenandoahHeuristics* _old_heuristics; + ShenandoahHeuristics* _global_heuristics; + + uint _sleep; + double _last_sleep_adjust_time; +}; + + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHREGULATORTHREAD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index 1b95fcc558f..b9d7b34cc7c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -30,8 +31,10 @@ #include "code/codeCache.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRootVerifier.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shared/oopStorage.inline.hpp" @@ -53,7 +56,7 @@ ShenandoahGCStateResetter::~ShenandoahGCStateResetter() { assert(_heap->gc_state() == _gc_state, "Should be restored"); } -void ShenandoahRootVerifier::roots_do(OopClosure* oops) { +void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops) { ShenandoahGCStateResetter resetter; shenandoah_assert_safepoint(); @@ -67,13 +70,19 @@ void ShenandoahRootVerifier::roots_do(OopClosure* oops) { OopStorageSet::storage(id)->oops_do(oops); } + ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (heap->mode()->is_generational() && heap->active_generation()->is_young()) { + shenandoah_assert_safepoint(); + ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->roots_do(oops); + } + // Do thread roots the last. This allows verification code to find // any broken objects from those special roots first, not the accidental // dangling reference from the thread root. Threads::possibly_parallel_oops_do(true, oops, nullptr); } -void ShenandoahRootVerifier::strong_roots_do(OopClosure* oops) { +void ShenandoahRootVerifier::strong_roots_do(OopIterateClosure* oops) { ShenandoahGCStateResetter resetter; shenandoah_assert_safepoint(); @@ -83,6 +92,12 @@ void ShenandoahRootVerifier::strong_roots_do(OopClosure* oops) { for (auto id : EnumRange()) { OopStorageSet::storage(id)->oops_do(oops); } + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + if (heap->mode()->is_generational() && heap->active_generation()->is_young()) { + ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->roots_do(oops); + } + // Do thread roots the last. This allows verification code to find // any broken objects from those special roots first, not the accidental // dangling reference from the thread root. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp index 54c95512a9c..da7ca864dbb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -41,8 +42,8 @@ class ShenandoahGCStateResetter : public StackObj { class ShenandoahRootVerifier : public AllStatic { public: // Used to seed ShenandoahVerifier, do not honor root type filter - static void roots_do(OopClosure* cl); - static void strong_roots_do(OopClosure* cl); + static void roots_do(OopIterateClosure* cl); + static void strong_roots_do(OopIterateClosure* cl); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHROOTVERIFIER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp index bda7e9b9545..c30d2507676 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp @@ -27,7 +27,6 @@ #include "gc/shared/bufferNode.hpp" #include "gc/shared/satbMarkQueue.hpp" -#include "gc/shenandoah/shenandoahHeap.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutex.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index 2e581d918d4..1b565006f74 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -29,6 +30,7 @@ #include "gc/shared/taskTerminator.hpp" #include "gc/shared/workerThread.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationType.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" @@ -56,10 +58,10 @@ void ShenandoahSTWMarkTask::work(uint worker_id) { _mark->finish_mark(worker_id); } -ShenandoahSTWMark::ShenandoahSTWMark(bool full_gc) : - ShenandoahMark(), +ShenandoahSTWMark::ShenandoahSTWMark(ShenandoahGeneration* generation, bool full_gc) : + ShenandoahMark(generation), _root_scanner(full_gc ? ShenandoahPhaseTimings::full_gc_mark : ShenandoahPhaseTimings::degen_gc_stw_mark), - _terminator(ShenandoahHeap::heap()->workers()->active_workers(), ShenandoahHeap::heap()->marking_context()->task_queues()), + _terminator(ShenandoahHeap::heap()->workers()->active_workers(), task_queues()), _full_gc(full_gc) { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a Shenandoah safepoint"); } @@ -72,7 +74,9 @@ void ShenandoahSTWMark::mark() { ShenandoahCodeRoots::arm_nmethods_for_mark(); // Weak reference processing - ShenandoahReferenceProcessor* rp = heap->ref_processor(); + assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); + shenandoah_assert_generations_reconciled(); rp->reset_thread_locals(); rp->set_soft_reference_policy(heap->soft_ref_policy()->should_clear_all_soft_refs()); @@ -91,6 +95,11 @@ void ShenandoahSTWMark::mark() { { // Mark + if (_generation->is_young()) { + // But only scan the remembered set for young generation. + _generation->scan_remembered_set(false /* is_concurrent */); + } + StrongRootsScope scope(nworkers); ShenandoahSTWMarkTask task(this); heap->workers()->run_task(&task); @@ -98,7 +107,7 @@ void ShenandoahSTWMark::mark() { assert(task_queues()->is_empty(), "Should be empty"); } - heap->mark_complete_marking_context(); + _generation->set_mark_complete(); end_mark(); // Mark is finished, can disarm the nmethods now. @@ -109,17 +118,43 @@ void ShenandoahSTWMark::mark() { } void ShenandoahSTWMark::mark_roots(uint worker_id) { - ShenandoahReferenceProcessor* rp = ShenandoahHeap::heap()->ref_processor(); - ShenandoahMarkRefsClosure cl(task_queues()->queue(worker_id), rp); - _root_scanner.roots_do(&cl, worker_id); + assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); + auto queue = task_queues()->queue(worker_id); + switch (_generation->type()) { + case NON_GEN: { + ShenandoahMarkRefsClosure init_mark(queue, rp, nullptr); + _root_scanner.roots_do(&init_mark, worker_id); + break; + } + case GLOBAL: { + ShenandoahMarkRefsClosure init_mark(queue, rp, nullptr); + _root_scanner.roots_do(&init_mark, worker_id); + break; + } + case YOUNG: { + ShenandoahMarkRefsClosure init_mark(queue, rp, nullptr); + _root_scanner.roots_do(&init_mark, worker_id); + break; + } + case OLD: + // We never exclusively mark the old generation on a safepoint. This would be encompassed + // by a 'global' collection. Note that both GLOBAL and NON_GEN mark the entire heap, but + // the GLOBAL closure is specialized for the generational mode. + default: + ShouldNotReachHere(); + } } void ShenandoahSTWMark::finish_mark(uint worker_id) { + assert(ShenandoahHeap::heap()->gc_generation() == _generation, "Marking unexpected generation"); ShenandoahPhaseTimings::Phase phase = _full_gc ? ShenandoahPhaseTimings::full_gc_mark : ShenandoahPhaseTimings::degen_gc_stw_mark; ShenandoahWorkerTimingsTracker timer(phase, ShenandoahPhaseTimings::ParallelMark, worker_id); - ShenandoahReferenceProcessor* rp = ShenandoahHeap::heap()->ref_processor(); + ShenandoahReferenceProcessor* rp = _generation->ref_processor(); + shenandoah_assert_generations_reconciled(); StringDedup::Requests requests; - mark_loop(worker_id, &_terminator, rp, NON_GEN, false /* not cancellable */, + mark_loop(worker_id, &_terminator, rp, + _generation->type(), false /* not cancellable */, ShenandoahStringDedup::is_enabled() ? ALWAYS_DEDUP : NO_DEDUP, &requests); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.hpp index 3ce1d25d33f..d2066ecafb9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.hpp @@ -26,8 +26,10 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHSTWMARK_HPP #include "gc/shenandoah/shenandoahMark.hpp" +#include "gc/shenandoah/shenandoahRootProcessor.hpp" class ShenandoahSTWMarkTask; +class ShenandoahGeneration; class ShenandoahSTWMark : public ShenandoahMark { friend class ShenandoahSTWMarkTask; @@ -37,7 +39,7 @@ class ShenandoahSTWMark : public ShenandoahMark { TaskTerminator _terminator; bool _full_gc; public: - ShenandoahSTWMark(bool full_gc); + ShenandoahSTWMark(ShenandoahGeneration* generation, bool full_gc); void mark(); private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp new file mode 100644 index 00000000000..5f09801b929 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -0,0 +1,973 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahReferenceProcessor.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "logging/log.hpp" + +size_t ShenandoahDirectCardMarkRememberedSet::last_valid_index() const { + return _card_table->last_valid_index(); +} + +size_t ShenandoahDirectCardMarkRememberedSet::total_cards() const { + return _total_card_count; +} + +size_t ShenandoahDirectCardMarkRememberedSet::card_index_for_addr(HeapWord *p) const { + return _card_table->index_for(p); +} + +HeapWord* ShenandoahDirectCardMarkRememberedSet::addr_for_card_index(size_t card_index) const { + return _whole_heap_base + CardTable::card_size_in_words() * card_index; +} + +bool ShenandoahDirectCardMarkRememberedSet::is_write_card_dirty(size_t card_index) const { + CardValue* bp = &(_card_table->write_byte_map())[card_index]; + return (bp[0] == CardTable::dirty_card_val()); +} + +bool ShenandoahDirectCardMarkRememberedSet::is_card_dirty(size_t card_index) const { + CardValue* bp = &(_card_table->read_byte_map())[card_index]; + return (bp[0] == CardTable::dirty_card_val()); +} + +void ShenandoahDirectCardMarkRememberedSet::mark_card_as_dirty(size_t card_index) { + CardValue* bp = &(_card_table->write_byte_map())[card_index]; + bp[0] = CardTable::dirty_card_val(); +} + +void ShenandoahDirectCardMarkRememberedSet::mark_range_as_dirty(size_t card_index, size_t num_cards) { + CardValue* bp = &(_card_table->write_byte_map())[card_index]; + while (num_cards-- > 0) { + *bp++ = CardTable::dirty_card_val(); + } +} + +void ShenandoahDirectCardMarkRememberedSet::mark_card_as_clean(size_t card_index) { + CardValue* bp = &(_card_table->write_byte_map())[card_index]; + bp[0] = CardTable::clean_card_val(); +} + +void ShenandoahDirectCardMarkRememberedSet::mark_range_as_clean(size_t card_index, size_t num_cards) { + CardValue* bp = &(_card_table->write_byte_map())[card_index]; + while (num_cards-- > 0) { + *bp++ = CardTable::clean_card_val(); + } +} + +bool ShenandoahDirectCardMarkRememberedSet::is_card_dirty(HeapWord* p) const { + size_t index = card_index_for_addr(p); + CardValue* bp = &(_card_table->read_byte_map())[index]; + return (bp[0] == CardTable::dirty_card_val()); +} + +bool ShenandoahDirectCardMarkRememberedSet::is_write_card_dirty(HeapWord* p) const { + size_t index = card_index_for_addr(p); + CardValue* bp = &(_card_table->write_byte_map())[index]; + return (bp[0] == CardTable::dirty_card_val()); +} + +void ShenandoahDirectCardMarkRememberedSet::mark_card_as_dirty(HeapWord* p) { + size_t index = card_index_for_addr(p); + CardValue* bp = &(_card_table->write_byte_map())[index]; + bp[0] = CardTable::dirty_card_val(); +} + +void ShenandoahDirectCardMarkRememberedSet::mark_range_as_dirty(HeapWord* p, size_t num_heap_words) { + CardValue* bp = &(_card_table->write_byte_map_base())[uintptr_t(p) >> _card_shift]; + CardValue* end_bp = &(_card_table->write_byte_map_base())[uintptr_t(p + num_heap_words) >> _card_shift]; + // If (p + num_heap_words) is not aligned on card boundary, we also need to dirty last card. + if (((unsigned long long) (p + num_heap_words)) & (CardTable::card_size() - 1)) { + end_bp++; + } + while (bp < end_bp) { + *bp++ = CardTable::dirty_card_val(); + } +} + +void ShenandoahDirectCardMarkRememberedSet::mark_card_as_clean(HeapWord* p) { + size_t index = card_index_for_addr(p); + CardValue* bp = &(_card_table->write_byte_map())[index]; + bp[0] = CardTable::clean_card_val(); +} + +void ShenandoahDirectCardMarkRememberedSet::mark_range_as_clean(HeapWord* p, size_t num_heap_words) { + CardValue* bp = &(_card_table->write_byte_map_base())[uintptr_t(p) >> _card_shift]; + CardValue* end_bp = &(_card_table->write_byte_map_base())[uintptr_t(p + num_heap_words) >> _card_shift]; + // If (p + num_heap_words) is not aligned on card boundary, we also need to clean last card. + if (((unsigned long long) (p + num_heap_words)) & (CardTable::card_size() - 1)) { + end_bp++; + } + while (bp < end_bp) { + *bp++ = CardTable::clean_card_val(); + } +} + +// No lock required because arguments align with card boundaries. +void ShenandoahCardCluster::reset_object_range(HeapWord* from, HeapWord* to) { + assert(((((unsigned long long) from) & (CardTable::card_size() - 1)) == 0) && + ((((unsigned long long) to) & (CardTable::card_size() - 1)) == 0), + "reset_object_range bounds must align with card boundaries"); + size_t card_at_start = _rs->card_index_for_addr(from); + size_t num_cards = (to - from) / CardTable::card_size_in_words(); + + for (size_t i = 0; i < num_cards; i++) { + _object_starts[card_at_start + i].short_word = 0; + } +} + +// Assume only one thread at a time registers objects pertaining to +// each card-table entry's range of memory. +void ShenandoahCardCluster::register_object(HeapWord* address) { + shenandoah_assert_heaplocked(); + + register_object_without_lock(address); +} + +void ShenandoahCardCluster::register_object_without_lock(HeapWord* address) { + size_t card_at_start = _rs->card_index_for_addr(address); + HeapWord* card_start_address = _rs->addr_for_card_index(card_at_start); + uint8_t offset_in_card = checked_cast(pointer_delta(address, card_start_address)); + + if (!starts_object(card_at_start)) { + set_starts_object_bit(card_at_start); + set_first_start(card_at_start, offset_in_card); + set_last_start(card_at_start, offset_in_card); + } else { + if (offset_in_card < get_first_start(card_at_start)) + set_first_start(card_at_start, offset_in_card); + if (offset_in_card > get_last_start(card_at_start)) + set_last_start(card_at_start, offset_in_card); + } +} + +void ShenandoahCardCluster::coalesce_objects(HeapWord* address, size_t length_in_words) { + + size_t card_at_start = _rs->card_index_for_addr(address); + HeapWord* card_start_address = _rs->addr_for_card_index(card_at_start); + size_t card_at_end = card_at_start + ((address + length_in_words) - card_start_address) / CardTable::card_size_in_words(); + + if (card_at_start == card_at_end) { + // There are no changes to the get_first_start array. Either get_first_start(card_at_start) returns this coalesced object, + // or it returns an object that precedes the coalesced object. + if (card_start_address + get_last_start(card_at_start) < address + length_in_words) { + uint8_t coalesced_offset = checked_cast(pointer_delta(address, card_start_address)); + // The object that used to be the last object starting within this card is being subsumed within the coalesced + // object. Since we always coalesce entire objects, this condition only occurs if the last object ends before or at + // the end of the card's memory range and there is no object following this object. In this case, adjust last_start + // to represent the start of the coalesced range. + set_last_start(card_at_start, coalesced_offset); + } + // Else, no changes to last_starts information. Either get_last_start(card_at_start) returns the object that immediately + // follows the coalesced object, or it returns an object that follows the object immediately following the coalesced object. + } else { + uint8_t coalesced_offset = checked_cast(pointer_delta(address, card_start_address)); + if (get_last_start(card_at_start) > coalesced_offset) { + // Existing last start is being coalesced, create new last start + set_last_start(card_at_start, coalesced_offset); + } + // otherwise, get_last_start(card_at_start) must equal coalesced_offset + + // All the cards between first and last get cleared. + for (size_t i = card_at_start + 1; i < card_at_end; i++) { + clear_starts_object_bit(i); + } + + uint8_t follow_offset = checked_cast((address + length_in_words) - _rs->addr_for_card_index(card_at_end)); + if (starts_object(card_at_end) && (get_first_start(card_at_end) < follow_offset)) { + // It may be that after coalescing within this last card's memory range, the last card + // no longer holds an object. + if (get_last_start(card_at_end) >= follow_offset) { + set_first_start(card_at_end, follow_offset); + } else { + // last_start is being coalesced so this card no longer has any objects. + clear_starts_object_bit(card_at_end); + } + } + // else + // card_at_end did not have an object, so it still does not have an object, or + // card_at_end had an object that starts after the coalesced object, so no changes required for card_at_end + + } +} + + +size_t ShenandoahCardCluster::get_first_start(size_t card_index) const { + assert(starts_object(card_index), "Can't get first start because no object starts here"); + return _object_starts[card_index].offsets.first & FirstStartBits; +} + +size_t ShenandoahCardCluster::get_last_start(size_t card_index) const { + assert(starts_object(card_index), "Can't get last start because no object starts here"); + return _object_starts[card_index].offsets.last; +} + +// Given a card_index, return the starting address of the first block in the heap +// that straddles into this card. If this card is co-initial with an object, then +// this would return the first address of the range that this card covers, which is +// where the card's first object also begins. +HeapWord* ShenandoahCardCluster::block_start(const size_t card_index) const { + + HeapWord* left = _rs->addr_for_card_index(card_index); + +#ifdef ASSERT + assert(ShenandoahHeap::heap()->mode()->is_generational(), "Do not use in non-generational mode"); + ShenandoahHeapRegion* region = ShenandoahHeap::heap()->heap_region_containing(left); + assert(region->is_old(), "Do not use for young regions"); + // For HumongousRegion:s it's more efficient to jump directly to the + // start region. + assert(!region->is_humongous(), "Use region->humongous_start_region() instead"); +#endif + if (starts_object(card_index) && get_first_start(card_index) == 0) { + // This card contains a co-initial object; a fortiori, it covers + // also the case of a card being the first in a region. + assert(oopDesc::is_oop(cast_to_oop(left)), "Should be an object"); + return left; + } + + HeapWord* p = nullptr; + oop obj = cast_to_oop(p); + ssize_t cur_index = (ssize_t)card_index; + assert(cur_index >= 0, "Overflow"); + assert(cur_index > 0, "Should have returned above"); + // Walk backwards over the cards... + while (--cur_index > 0 && !starts_object(cur_index)) { + // ... to the one that starts the object + } + // cur_index should start an object: we should not have walked + // past the left end of the region. + assert(cur_index >= 0 && (cur_index <= (ssize_t)card_index), "Error"); + assert(region->bottom() <= _rs->addr_for_card_index(cur_index), + "Fell off the bottom of containing region"); + assert(starts_object(cur_index), "Error"); + size_t offset = get_last_start(cur_index); + // can avoid call via card size arithmetic below instead + p = _rs->addr_for_card_index(cur_index) + offset; + // Recall that we already dealt with the co-initial object case above + assert(p < left, "obj should start before left"); + // While it is safe to ask an object its size in the loop that + // follows, the (ifdef'd out) loop should never be needed. + // 1. we ask this question only for regions in the old generation + // 2. there is no direct allocation ever by mutators in old generation + // regions. Only GC will ever allocate in old regions, and then + // too only during promotion/evacuation phases. Thus there is no danger + // of races between reading from and writing to the object start array, + // or of asking partially initialized objects their size (in the loop below). + // 3. only GC asks this question during phases when it is not concurrently + // evacuating/promoting, viz. during concurrent root scanning (before + // the evacuation phase) and during concurrent update refs (after the + // evacuation phase) of young collections. This is never called + // during old or global collections. + // 4. Every allocation under TAMS updates the object start array. + NOT_PRODUCT(obj = cast_to_oop(p);) + assert(oopDesc::is_oop(obj), "Should be an object"); +#define WALK_FORWARD_IN_BLOCK_START false + while (WALK_FORWARD_IN_BLOCK_START && p + obj->size() < left) { + p += obj->size(); + } +#undef WALK_FORWARD_IN_BLOCK_START // false + assert(p + obj->size() > left, "obj should end after left"); + return p; +} + +size_t ShenandoahScanRemembered::card_index_for_addr(HeapWord* p) { + return _rs->card_index_for_addr(p); +} + +HeapWord* ShenandoahScanRemembered::addr_for_card_index(size_t card_index) { + return _rs->addr_for_card_index(card_index); +} + +bool ShenandoahScanRemembered::is_card_dirty(size_t card_index) { + return _rs->is_card_dirty(card_index); +} + +bool ShenandoahScanRemembered::is_write_card_dirty(size_t card_index) { + return _rs->is_write_card_dirty(card_index); +} + +bool ShenandoahScanRemembered::is_card_dirty(HeapWord* p) { + return _rs->is_card_dirty(p); +} + +void ShenandoahScanRemembered::mark_card_as_dirty(HeapWord* p) { + _rs->mark_card_as_dirty(p); +} + +bool ShenandoahScanRemembered::is_write_card_dirty(HeapWord* p) { + return _rs->is_write_card_dirty(p); +} + +void ShenandoahScanRemembered::mark_range_as_dirty(HeapWord* p, size_t num_heap_words) { + _rs->mark_range_as_dirty(p, num_heap_words); +} + +void ShenandoahScanRemembered::mark_card_as_clean(HeapWord* p) { + _rs->mark_card_as_clean(p); +} + +void ShenandoahScanRemembered:: mark_range_as_clean(HeapWord* p, size_t num_heap_words) { + _rs->mark_range_as_clean(p, num_heap_words); +} + +void ShenandoahScanRemembered::reset_object_range(HeapWord* from, HeapWord* to) { + _scc->reset_object_range(from, to); +} + +void ShenandoahScanRemembered::register_object(HeapWord* addr) { + _scc->register_object(addr); +} + +void ShenandoahScanRemembered::register_object_without_lock(HeapWord* addr) { + _scc->register_object_without_lock(addr); +} + +bool ShenandoahScanRemembered::verify_registration(HeapWord* address, ShenandoahMarkingContext* ctx) { + + size_t index = card_index_for_addr(address); + if (!_scc->starts_object(index)) { + return false; + } + HeapWord* base_addr = addr_for_card_index(index); + size_t offset = _scc->get_first_start(index); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + // Verify that I can find this object within its enclosing card by scanning forward from first_start. + while (base_addr + offset < address) { + oop obj = cast_to_oop(base_addr + offset); + if (!ctx || ctx->is_marked(obj)) { + offset += obj->size(); + } else { + // If this object is not live, don't trust its size(); all objects above tams are live. + ShenandoahHeapRegion* r = heap->heap_region_containing(obj); + HeapWord* tams = ctx->top_at_mark_start(r); + offset = ctx->get_next_marked_addr(base_addr + offset, tams) - base_addr; + } + } + if (base_addr + offset != address){ + return false; + } + + // At this point, offset represents object whose registration we are verifying. We know that at least this object resides + // within this card's memory. + + // Make sure that last_offset is properly set for the enclosing card, but we can't verify this for + // candidate collection-set regions during mixed evacuations, so disable this check in general + // during mixed evacuations. + + ShenandoahHeapRegion* r = heap->heap_region_containing(base_addr + offset); + size_t max_offset = r->top() - base_addr; + if (max_offset > CardTable::card_size_in_words()) { + max_offset = CardTable::card_size_in_words(); + } + size_t prev_offset; + if (!ctx) { + do { + oop obj = cast_to_oop(base_addr + offset); + prev_offset = offset; + offset += obj->size(); + } while (offset < max_offset); + if (_scc->get_last_start(index) != prev_offset) { + return false; + } + + // base + offset represents address of first object that starts on following card, if there is one. + + // Notes: base_addr is addr_for_card_index(index) + // base_addr + offset is end of the object we are verifying + // cannot use card_index_for_addr(base_addr + offset) because it asserts arg < end of whole heap + size_t end_card_index = index + offset / CardTable::card_size_in_words(); + + if (end_card_index > index && end_card_index <= _rs->last_valid_index()) { + // If there is a following object registered on the next card, it should begin where this object ends. + if (_scc->starts_object(end_card_index) && + ((addr_for_card_index(end_card_index) + _scc->get_first_start(end_card_index)) != (base_addr + offset))) { + return false; + } + } + + // Assure that no other objects are registered "inside" of this one. + for (index++; index < end_card_index; index++) { + if (_scc->starts_object(index)) { + return false; + } + } + } else { + // This is a mixed evacuation or a global collect: rely on mark bits to identify which objects need to be properly registered + assert(!ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress(), "Cannot rely on mark context here."); + // If the object reaching or spanning the end of this card's memory is marked, then last_offset for this card + // should represent this object. Otherwise, last_offset is a don't care. + ShenandoahHeapRegion* region = heap->heap_region_containing(base_addr + offset); + HeapWord* tams = ctx->top_at_mark_start(region); + oop last_obj = nullptr; + do { + oop obj = cast_to_oop(base_addr + offset); + if (ctx->is_marked(obj)) { + prev_offset = offset; + offset += obj->size(); + last_obj = obj; + } else { + offset = ctx->get_next_marked_addr(base_addr + offset, tams) - base_addr; + // If there are no marked objects remaining in this region, offset equals tams - base_addr. If this offset is + // greater than max_offset, we will immediately exit this loop. Otherwise, the next iteration of the loop will + // treat the object at offset as marked and live (because address >= tams) and we will continue iterating object + // by consulting the size() fields of each. + } + } while (offset < max_offset); + if (last_obj != nullptr && prev_offset + last_obj->size() >= max_offset) { + // last marked object extends beyond end of card + if (_scc->get_last_start(index) != prev_offset) { + return false; + } + // otherwise, the value of _scc->get_last_start(index) is a don't care because it represents a dead object and we + // cannot verify its context + } + } + return true; +} + +void ShenandoahScanRemembered::coalesce_objects(HeapWord* addr, size_t length_in_words) { + _scc->coalesce_objects(addr, length_in_words); +} + +void ShenandoahScanRemembered::mark_range_as_empty(HeapWord* addr, size_t length_in_words) { + _rs->mark_range_as_clean(addr, length_in_words); + _scc->clear_objects_in_range(addr, length_in_words); +} + +size_t ShenandoahScanRemembered::cluster_for_addr(HeapWordImpl **addr) { + size_t card_index = _rs->card_index_for_addr(addr); + size_t result = card_index / ShenandoahCardCluster::CardsPerCluster; + return result; +} + +HeapWord* ShenandoahScanRemembered::addr_for_cluster(size_t cluster_no) { + size_t card_index = cluster_no * ShenandoahCardCluster::CardsPerCluster; + return addr_for_card_index(card_index); +} + +// This is used only for debug verification so don't worry about making the scan parallel. +void ShenandoahScanRemembered::roots_do(OopIterateClosure* cl) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + bool old_bitmap_stable = heap->old_generation()->is_mark_complete(); + log_info(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable)); + for (size_t i = 0, n = heap->num_regions(); i < n; ++i) { + ShenandoahHeapRegion* region = heap->get_region(i); + if (region->is_old() && region->is_active() && !region->is_cset()) { + HeapWord* start_of_range = region->bottom(); + HeapWord* end_of_range = region->top(); + size_t start_cluster_no = cluster_for_addr(start_of_range); + size_t num_heapwords = end_of_range - start_of_range; + unsigned int cluster_size = CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster; + size_t num_clusters = (size_t) ((num_heapwords - 1 + cluster_size) / cluster_size); + + // Remembered set scanner + if (region->is_humongous()) { + process_humongous_clusters(region->humongous_start_region(), start_cluster_no, num_clusters, end_of_range, cl, + false /* use_write_table */); + } else { + process_clusters(start_cluster_no, num_clusters, end_of_range, cl, + false /* use_write_table */, 0 /* fake worker id */); + } + } + } +} + +#ifndef PRODUCT +// Log given card stats +void ShenandoahScanRemembered::log_card_stats(HdrSeq* stats) { + for (int i = 0; i < MAX_CARD_STAT_TYPE; i++) { + log_info(gc, remset)("%18s: [ %8.2f %8.2f %8.2f %8.2f %8.2f ]", + _card_stats_name[i], + stats[i].percentile(0), stats[i].percentile(25), + stats[i].percentile(50), stats[i].percentile(75), + stats[i].maximum()); + } +} + +// Log card stats for all nworkers for a specific phase t +void ShenandoahScanRemembered::log_card_stats(uint nworkers, CardStatLogType t) { + assert(ShenandoahEnableCardStats, "Do not call"); + HdrSeq* sum_stats = card_stats_for_phase(t); + log_info(gc, remset)("%s", _card_stat_log_type[t]); + for (uint i = 0; i < nworkers; i++) { + log_worker_card_stats(i, sum_stats); + } + + // Every so often, log the cumulative global stats + if (++_card_stats_log_counter[t] >= ShenandoahCardStatsLogInterval) { + _card_stats_log_counter[t] = 0; + log_info(gc, remset)("Cumulative stats"); + log_card_stats(sum_stats); + } +} + +// Log card stats for given worker_id, & clear them after merging into given cumulative stats +void ShenandoahScanRemembered::log_worker_card_stats(uint worker_id, HdrSeq* sum_stats) { + assert(ShenandoahEnableCardStats, "Do not call"); + + HdrSeq* worker_card_stats = card_stats(worker_id); + log_info(gc, remset)("Worker %u Card Stats: ", worker_id); + log_card_stats(worker_card_stats); + // Merge worker stats into the cumulative stats & clear worker stats + merge_worker_card_stats_cumulative(worker_card_stats, sum_stats); +} + +void ShenandoahScanRemembered::merge_worker_card_stats_cumulative( + HdrSeq* worker_stats, HdrSeq* sum_stats) { + for (int i = 0; i < MAX_CARD_STAT_TYPE; i++) { + sum_stats[i].add(worker_stats[i]); + worker_stats[i].clear(); + } +} +#endif + +// A closure that takes an oop in the old generation and, if it's pointing +// into the young generation, dirties the corresponding remembered set entry. +// This is only used to rebuild the remembered set after a full GC. +class ShenandoahDirtyRememberedSetClosure : public BasicOopIterateClosure { +protected: + ShenandoahGenerationalHeap* const _heap; + ShenandoahScanRemembered* const _scanner; + +public: + ShenandoahDirtyRememberedSetClosure() : + _heap(ShenandoahGenerationalHeap::heap()), + _scanner(_heap->old_generation()->card_scan()) {} + + template + inline void work(T* p) { + assert(_heap->is_in_old(p), "Expecting to get an old gen address"); + T o = RawAccess<>::oop_load(p); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (_heap->is_in_young(obj)) { + // Dirty the card containing the cross-generational pointer. + _scanner->mark_card_as_dirty((HeapWord*) p); + } + } + } + + virtual void do_oop(narrowOop* p) { work(p); } + virtual void do_oop(oop* p) { work(p); } +}; + +ShenandoahDirectCardMarkRememberedSet::ShenandoahDirectCardMarkRememberedSet(ShenandoahCardTable* card_table, size_t total_card_count) : + LogCardValsPerIntPtr(log2i_exact(sizeof(intptr_t)) - log2i_exact(sizeof(CardValue))), + LogCardSizeInWords(log2i_exact(CardTable::card_size_in_words())) { + + // Paranoid assert for LogCardsPerIntPtr calculation above + assert(sizeof(intptr_t) > sizeof(CardValue), "LogsCardValsPerIntPtr would underflow"); + + _heap = ShenandoahHeap::heap(); + _card_table = card_table; + _total_card_count = total_card_count; + _card_shift = CardTable::card_shift(); + + _byte_map = _card_table->byte_for_index(0); + + _whole_heap_base = _card_table->addr_for(_byte_map); + _byte_map_base = _byte_map - (uintptr_t(_whole_heap_base) >> _card_shift); + + assert(total_card_count % ShenandoahCardCluster::CardsPerCluster == 0, "Invalid card count."); + assert(total_card_count > 0, "Card count cannot be zero."); +} + +// Merge any dirty values from write table into the read table, while leaving +// the write table unchanged. +void ShenandoahDirectCardMarkRememberedSet::merge_write_table(HeapWord* start, size_t word_count) { + size_t start_index = card_index_for_addr(start); +#ifdef ASSERT + // avoid querying card_index_for_addr() for an address past end of heap + size_t end_index = card_index_for_addr(start + word_count - 1) + 1; +#endif + assert(start_index % ((size_t)1 << LogCardValsPerIntPtr) == 0, "Expected a multiple of CardValsPerIntPtr"); + assert(end_index % ((size_t)1 << LogCardValsPerIntPtr) == 0, "Expected a multiple of CardValsPerIntPtr"); + + // We'll access in groups of intptr_t worth of card entries + intptr_t* const read_table = (intptr_t*) &(_card_table->read_byte_map())[start_index]; + intptr_t* const write_table = (intptr_t*) &(_card_table->write_byte_map())[start_index]; + + // Avoid division, use shift instead + assert(word_count % ((size_t)1 << (LogCardSizeInWords + LogCardValsPerIntPtr)) == 0, "Expected a multiple of CardSizeInWords*CardValsPerIntPtr"); + size_t const num = word_count >> (LogCardSizeInWords + LogCardValsPerIntPtr); + + for (size_t i = 0; i < num; i++) { + read_table[i] &= write_table[i]; + } +} + +// Destructively copy the write table to the read table, and clean the write table. +void ShenandoahDirectCardMarkRememberedSet::reset_remset(HeapWord* start, size_t word_count) { + size_t start_index = card_index_for_addr(start); +#ifdef ASSERT + // avoid querying card_index_for_addr() for an address past end of heap + size_t end_index = card_index_for_addr(start + word_count - 1) + 1; +#endif + assert(start_index % ((size_t)1 << LogCardValsPerIntPtr) == 0, "Expected a multiple of CardValsPerIntPtr"); + assert(end_index % ((size_t)1 << LogCardValsPerIntPtr) == 0, "Expected a multiple of CardValsPerIntPtr"); + + // We'll access in groups of intptr_t worth of card entries + intptr_t* const read_table = (intptr_t*) &(_card_table->read_byte_map())[start_index]; + intptr_t* const write_table = (intptr_t*) &(_card_table->write_byte_map())[start_index]; + + // Avoid division, use shift instead + assert(word_count % ((size_t)1 << (LogCardSizeInWords + LogCardValsPerIntPtr)) == 0, "Expected a multiple of CardSizeInWords*CardValsPerIntPtr"); + size_t const num = word_count >> (LogCardSizeInWords + LogCardValsPerIntPtr); + + for (size_t i = 0; i < num; i++) { + read_table[i] = write_table[i]; + write_table[i] = CardTable::clean_card_row_val(); + } +} + +ShenandoahScanRememberedTask::ShenandoahScanRememberedTask(ShenandoahObjToScanQueueSet* queue_set, + ShenandoahObjToScanQueueSet* old_queue_set, + ShenandoahReferenceProcessor* rp, + ShenandoahRegionChunkIterator* work_list, bool is_concurrent) : + WorkerTask("Scan Remembered Set"), + _queue_set(queue_set), _old_queue_set(old_queue_set), _rp(rp), _work_list(work_list), _is_concurrent(is_concurrent) { + bool old_bitmap_stable = ShenandoahHeap::heap()->old_generation()->is_mark_complete(); + log_info(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable)); +} + +void ShenandoahScanRememberedTask::work(uint worker_id) { + if (_is_concurrent) { + // This sets up a thread local reference to the worker_id which is needed by the weak reference processor. + ShenandoahConcurrentWorkerSession worker_session(worker_id); + ShenandoahSuspendibleThreadSetJoiner stsj; + do_work(worker_id); + } else { + // This sets up a thread local reference to the worker_id which is needed by the weak reference processor. + ShenandoahParallelWorkerSession worker_session(worker_id); + do_work(worker_id); + } +} + +void ShenandoahScanRememberedTask::do_work(uint worker_id) { + ShenandoahWorkerTimingsTracker x(ShenandoahPhaseTimings::init_scan_rset, ShenandoahPhaseTimings::ScanClusters, worker_id); + + ShenandoahObjToScanQueue* q = _queue_set->queue(worker_id); + ShenandoahObjToScanQueue* old = _old_queue_set == nullptr ? nullptr : _old_queue_set->queue(worker_id); + ShenandoahMarkRefsClosure cl(q, _rp, old); + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + ShenandoahScanRemembered* scanner = heap->old_generation()->card_scan(); + + // set up thread local closure for shen ref processor + _rp->set_mark_closure(worker_id, &cl); + struct ShenandoahRegionChunk assignment; + while (_work_list->next(&assignment)) { + ShenandoahHeapRegion* region = assignment._r; + log_debug(gc)("ShenandoahScanRememberedTask::do_work(%u), processing slice of region " + SIZE_FORMAT " at offset " SIZE_FORMAT ", size: " SIZE_FORMAT, + worker_id, region->index(), assignment._chunk_offset, assignment._chunk_size); + if (region->is_old()) { + size_t cluster_size = + CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster; + size_t clusters = assignment._chunk_size / cluster_size; + assert(clusters * cluster_size == assignment._chunk_size, "Chunk assignments must align on cluster boundaries"); + HeapWord* end_of_range = region->bottom() + assignment._chunk_offset + assignment._chunk_size; + + // During concurrent mark, region->top() equals TAMS with respect to the current young-gen pass. + if (end_of_range > region->top()) { + end_of_range = region->top(); + } + scanner->process_region_slice(region, assignment._chunk_offset, clusters, end_of_range, &cl, false, worker_id); + } +#ifdef ENABLE_REMEMBERED_SET_CANCELLATION + // This check is currently disabled to avoid crashes that occur + // when we try to cancel remembered set scanning; it should be re-enabled + // after the issues are fixed, as it would allow more prompt cancellation and + // transition to degenerated / full GCs. Note that work that has been assigned/ + // claimed above must be completed before we return here upon cancellation. + if (heap->check_cancelled_gc_and_yield(_is_concurrent)) { + return; + } +#endif + } +} + +size_t ShenandoahRegionChunkIterator::calc_regular_group_size() { + // The group size is calculated from the number of regions. Suppose the heap has N regions. The first group processes + // N/2 regions. The second group processes N/4 regions, the third group N/8 regions and so on. + // Note that infinite series N/2 + N/4 + N/8 + N/16 + ... sums to N. + // + // The normal group size is the number of regions / 2. + // + // In the case that the region_size_words is greater than _maximum_chunk_size_words, the first group_size is + // larger than the normal group size because each chunk in the group will be smaller than the region size. + // + // The last group also has more than the normal entries because it finishes the total scanning effort. The chunk sizes are + // different for each group. The intention is that the first group processes roughly half of the heap, the second processes + // half of the remaining heap, the third processes half of what remains and so on. The smallest chunk size + // is represented by _smallest_chunk_size_words. We do not divide work any smaller than this. + // + + size_t group_size = _heap->num_regions() / 2; + return group_size; +} + +size_t ShenandoahRegionChunkIterator::calc_first_group_chunk_size_b4_rebalance() { + size_t words_in_first_chunk = ShenandoahHeapRegion::region_size_words(); + return words_in_first_chunk; +} + +size_t ShenandoahRegionChunkIterator::calc_num_groups() { + size_t total_heap_size = _heap->num_regions() * ShenandoahHeapRegion::region_size_words(); + size_t num_groups = 0; + size_t cumulative_group_span = 0; + size_t current_group_span = _first_group_chunk_size_b4_rebalance * _regular_group_size; + size_t smallest_group_span = smallest_chunk_size_words() * _regular_group_size; + while ((num_groups < _maximum_groups) && (cumulative_group_span + current_group_span <= total_heap_size)) { + num_groups++; + cumulative_group_span += current_group_span; + if (current_group_span <= smallest_group_span) { + break; + } else { + current_group_span /= 2; // Each group spans half of what the preceding group spanned. + } + } + // Loop post condition: + // num_groups <= _maximum_groups + // cumulative_group_span is the memory spanned by num_groups + // current_group_span is the span of the last fully populated group (assuming loop iterates at least once) + // each of num_groups is fully populated with _regular_group_size chunks in each + // Non post conditions: + // cumulative_group_span may be less than total_heap size for one or more of the folowing reasons + // a) The number of regions remaining to be spanned is smaller than a complete group, or + // b) We have filled up all groups through _maximum_groups and still have not spanned all regions + + if (cumulative_group_span < total_heap_size) { + // We've got more regions to span + if ((num_groups < _maximum_groups) && (current_group_span > smallest_group_span)) { + num_groups++; // Place all remaining regions into a new not-full group (chunk_size half that of previous group) + } + // Else we are unable to create a new group because we've exceed the number of allowed groups or have reached the + // minimum chunk size. + + // Any remaining regions will be treated as if they are part of the most recently created group. This group will + // have more than _regular_group_size chunks within it. + } + return num_groups; +} + +size_t ShenandoahRegionChunkIterator::calc_total_chunks() { + size_t region_size_words = ShenandoahHeapRegion::region_size_words(); + size_t unspanned_heap_size = _heap->num_regions() * region_size_words; + size_t num_chunks = 0; + size_t cumulative_group_span = 0; + size_t current_group_span = _first_group_chunk_size_b4_rebalance * _regular_group_size; + size_t smallest_group_span = smallest_chunk_size_words() * _regular_group_size; + + // The first group gets special handling because the first chunk size can be no larger than _largest_chunk_size_words + if (region_size_words > _maximum_chunk_size_words) { + // In the case that we shrink the first group's chunk size, certain other groups will also be subsumed within the first group + size_t effective_chunk_size = _first_group_chunk_size_b4_rebalance; + while (effective_chunk_size >= _maximum_chunk_size_words) { + num_chunks += current_group_span / _maximum_chunk_size_words; + unspanned_heap_size -= current_group_span; + effective_chunk_size /= 2; + current_group_span /= 2; + } + } else { + num_chunks = _regular_group_size; + unspanned_heap_size -= current_group_span; + current_group_span /= 2; + } + size_t spanned_groups = 1; + while (unspanned_heap_size > 0) { + if (current_group_span <= unspanned_heap_size) { + unspanned_heap_size -= current_group_span; + num_chunks += _regular_group_size; + spanned_groups++; + + // _num_groups is the number of groups required to span the configured heap size. We are not allowed + // to change the number of groups. The last group is responsible for spanning all chunks not spanned + // by previously processed groups. + if (spanned_groups >= _num_groups) { + // The last group has more than _regular_group_size entries. + size_t chunk_span = current_group_span / _regular_group_size; + size_t extra_chunks = unspanned_heap_size / chunk_span; + assert (extra_chunks * chunk_span == unspanned_heap_size, "Chunks must precisely span regions"); + num_chunks += extra_chunks; + return num_chunks; + } else if (current_group_span <= smallest_group_span) { + // We cannot introduce new groups because we've reached the lower bound on group size. So this last + // group may hold extra chunks. + size_t chunk_span = smallest_chunk_size_words(); + size_t extra_chunks = unspanned_heap_size / chunk_span; + assert (extra_chunks * chunk_span == unspanned_heap_size, "Chunks must precisely span regions"); + num_chunks += extra_chunks; + return num_chunks; + } else { + current_group_span /= 2; + } + } else { + // This last group has fewer than _regular_group_size entries. + size_t chunk_span = current_group_span / _regular_group_size; + size_t last_group_size = unspanned_heap_size / chunk_span; + assert (last_group_size * chunk_span == unspanned_heap_size, "Chunks must precisely span regions"); + num_chunks += last_group_size; + return num_chunks; + } + } + return num_chunks; +} + +ShenandoahRegionChunkIterator::ShenandoahRegionChunkIterator(size_t worker_count) : + ShenandoahRegionChunkIterator(ShenandoahHeap::heap(), worker_count) +{ +} + +ShenandoahRegionChunkIterator::ShenandoahRegionChunkIterator(ShenandoahHeap* heap, size_t worker_count) : + _heap(heap), + _regular_group_size(calc_regular_group_size()), + _first_group_chunk_size_b4_rebalance(calc_first_group_chunk_size_b4_rebalance()), + _num_groups(calc_num_groups()), + _total_chunks(calc_total_chunks()), + _index(0) +{ +#ifdef ASSERT + size_t expected_chunk_size_words = _clusters_in_smallest_chunk * CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster; + assert(smallest_chunk_size_words() == expected_chunk_size_words, "_smallest_chunk_size (" SIZE_FORMAT") is not valid because it does not equal (" SIZE_FORMAT ")", + smallest_chunk_size_words(), expected_chunk_size_words); +#endif + assert(_num_groups <= _maximum_groups, + "The number of remembered set scanning groups must be less than or equal to maximum groups"); + assert(smallest_chunk_size_words() << (_maximum_groups - 1) == _maximum_chunk_size_words, + "Maximum number of groups needs to span maximum chunk size to smallest chunk size"); + + size_t words_in_region = ShenandoahHeapRegion::region_size_words(); + _region_index[0] = 0; + _group_offset[0] = 0; + if (words_in_region > _maximum_chunk_size_words) { + // In the case that we shrink the first group's chunk size, certain other groups will also be subsumed within the first group + size_t num_chunks = 0; + size_t effective_chunk_size = _first_group_chunk_size_b4_rebalance; + size_t current_group_span = effective_chunk_size * _regular_group_size; + while (effective_chunk_size >= _maximum_chunk_size_words) { + num_chunks += current_group_span / _maximum_chunk_size_words; + effective_chunk_size /= 2; + current_group_span /= 2; + } + _group_entries[0] = num_chunks; + _group_chunk_size[0] = _maximum_chunk_size_words; + } else { + _group_entries[0] = _regular_group_size; + _group_chunk_size[0] = _first_group_chunk_size_b4_rebalance; + } + + size_t previous_group_span = _group_entries[0] * _group_chunk_size[0]; + for (size_t i = 1; i < _num_groups; i++) { + _group_chunk_size[i] = _group_chunk_size[i-1] / 2; + size_t chunks_in_group = _regular_group_size; + size_t this_group_span = _group_chunk_size[i] * chunks_in_group; + size_t total_span_of_groups = previous_group_span + this_group_span; + _region_index[i] = previous_group_span / words_in_region; + _group_offset[i] = previous_group_span % words_in_region; + _group_entries[i] = _group_entries[i-1] + _regular_group_size; + previous_group_span = total_span_of_groups; + } + if (_group_entries[_num_groups-1] < _total_chunks) { + assert((_total_chunks - _group_entries[_num_groups-1]) * _group_chunk_size[_num_groups-1] + previous_group_span == + heap->num_regions() * words_in_region, "Total region chunks (" SIZE_FORMAT + ") do not span total heap regions (" SIZE_FORMAT ")", _total_chunks, _heap->num_regions()); + previous_group_span += (_total_chunks - _group_entries[_num_groups-1]) * _group_chunk_size[_num_groups-1]; + _group_entries[_num_groups-1] = _total_chunks; + } + assert(previous_group_span == heap->num_regions() * words_in_region, "Total region chunks (" SIZE_FORMAT + ") do not span total heap regions (" SIZE_FORMAT "): " SIZE_FORMAT " does not equal " SIZE_FORMAT, + _total_chunks, _heap->num_regions(), previous_group_span, heap->num_regions() * words_in_region); + + // Not necessary, but keeps things tidy + for (size_t i = _num_groups; i < _maximum_groups; i++) { + _region_index[i] = 0; + _group_offset[i] = 0; + _group_entries[i] = _group_entries[i-1]; + _group_chunk_size[i] = 0; + } +} + +void ShenandoahRegionChunkIterator::reset() { + _index = 0; +} + +ShenandoahReconstructRememberedSetTask::ShenandoahReconstructRememberedSetTask(ShenandoahRegionIterator* regions) + : WorkerTask("Shenandoah Reset Bitmap") + , _regions(regions) { } + +void ShenandoahReconstructRememberedSetTask::work(uint worker_id) { + ShenandoahParallelWorkerSession worker_session(worker_id); + ShenandoahHeapRegion* r = _regions->next(); + ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); + ShenandoahScanRemembered* scanner = heap->old_generation()->card_scan(); + ShenandoahDirtyRememberedSetClosure dirty_cards_for_cross_generational_pointers; + + while (r != nullptr) { + if (r->is_old() && r->is_active()) { + HeapWord* obj_addr = r->bottom(); + if (r->is_humongous_start()) { + // First, clear the remembered set + oop obj = cast_to_oop(obj_addr); + size_t size = obj->size(); + + // First, clear the remembered set for all spanned humongous regions + size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); + size_t region_span = num_regions * ShenandoahHeapRegion::region_size_words(); + scanner->reset_remset(r->bottom(), region_span); + size_t region_index = r->index(); + ShenandoahHeapRegion* humongous_region = heap->get_region(region_index); + while (num_regions-- != 0) { + scanner->reset_object_range(humongous_region->bottom(), humongous_region->end()); + region_index++; + humongous_region = heap->get_region(region_index); + } + + // Then register the humongous object and DIRTY relevant remembered set cards + scanner->register_object_without_lock(obj_addr); + obj->oop_iterate(&dirty_cards_for_cross_generational_pointers); + } else if (!r->is_humongous()) { + // First, clear the remembered set + scanner->reset_remset(r->bottom(), ShenandoahHeapRegion::region_size_words()); + scanner->reset_object_range(r->bottom(), r->end()); + + // Then iterate over all objects, registering object and DIRTYing relevant remembered set cards + HeapWord* t = r->top(); + while (obj_addr < t) { + oop obj = cast_to_oop(obj_addr); + scanner->register_object_without_lock(obj_addr); + obj_addr += obj->oop_iterate_size(&dirty_cards_for_cross_generational_pointers); + } + } // else, ignore humongous continuation region + } + // else, this region is FREE or YOUNG or inactive and we can ignore it. + r = _regions->next(); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp new file mode 100644 index 00000000000..2a0713bf4ed --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -0,0 +1,1001 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBERED_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBERED_HPP + +// Terminology used within this source file: +// +// Card Entry: This is the information that identifies whether a +// particular card-table entry is Clean or Dirty. A clean +// card entry denotes that the associated memory does not +// hold references to young-gen memory. +// +// Card Region, aka +// Card Memory: This is the region of memory that is assocated with a +// particular card entry. +// +// Card Cluster: A card cluster represents 64 card entries. A card +// cluster is the minimal amount of work performed at a +// time by a parallel thread. Note that the work required +// to scan a card cluster is somewhat variable in that the +// required effort depends on how many cards are dirty, how +// many references are held within the objects that span a +// DIRTY card's memory, and on the size of the object +// that spans the end of a DIRTY card's memory (because +// that object, if it's not an array, may need to be scanned in +// its entirety, when the object is imprecisely dirtied. Imprecise +// dirtying is when the card corresponding to the object header +// is dirtied, rather than the card on which the updated field lives). +// To better balance work amongst them, parallel worker threads dynamically +// claim clusters and are flexible in the number of clusters they +// process. +// +// A cluster represents a "natural" quantum of work to be performed by +// a parallel GC thread's background remembered set scanning efforts. +// The notion of cluster is similar to the notion of stripe in the +// implementation of parallel GC card scanning. However, a cluster is +// typically smaller than a stripe, enabling finer grain division of +// labor between multiple threads, and potentially better load balancing +// when dirty cards are not uniformly distributed in the heap, as is often +// the case with generational workloads where more recently promoted objects +// may be dirtied more frequently that older objects. +// +// For illustration, consider the following possible JVM configurations: +// +// Scenario 1: +// RegionSize is 128 MB +// Span of a card entry is 512 B +// Each card table entry consumes 1 B +// Assume one long word (8 B)of the card table represents a cluster. +// This long word holds 8 card table entries, spanning a +// total of 8*512 B = 4 KB of the heap +// The number of clusters per region is 128 MB / 4 KB = 32 K +// +// Scenario 2: +// RegionSize is 128 MB +// Span of each card entry is 128 B +// Each card table entry consumes 1 bit +// Assume one int word (4 B) of the card table represents a cluster. +// This int word holds 32 b/1 b = 32 card table entries, spanning a +// total of 32 * 128 B = 4 KB of the heap +// The number of clusters per region is 128 MB / 4 KB = 32 K +// +// Scenario 3: +// RegionSize is 128 MB +// Span of each card entry is 512 B +// Each card table entry consumes 1 bit +// Assume one long word (8 B) of card table represents a cluster. +// This long word holds 64 b/ 1 b = 64 card table entries, spanning a +// total of 64 * 512 B = 32 KB of the heap +// The number of clusters per region is 128 MB / 32 KB = 4 K +// +// At the start of a new young-gen concurrent mark pass, the gang of +// Shenandoah worker threads collaborate in performing the following +// actions: +// +// Let old_regions = number of ShenandoahHeapRegion comprising +// old-gen memory +// Let region_size = ShenandoahHeapRegion::region_size_bytes() +// represent the number of bytes in each region +// Let clusters_per_region = region_size / 512 +// Let rs represent the ShenandoahDirectCardMarkRememberedSet +// +// for each ShenandoahHeapRegion old_region in the whole heap +// determine the cluster number of the first cluster belonging +// to that region +// for each cluster contained within that region +// Assure that exactly one worker thread processes each +// cluster, each thread making a series of invocations of the +// following: +// +// rs->process_clusters(worker_id, ReferenceProcessor *, +// ShenandoahConcurrentMark *, cluster_no, cluster_count, +// HeapWord *end_of_range, OopClosure *oops); +// +// For efficiency, divide up the clusters so that different threads +// are responsible for processing different clusters. Processing costs +// may vary greatly between clusters for the following reasons: +// +// a) some clusters contain mostly dirty cards and other +// clusters contain mostly clean cards +// b) some clusters contain mostly primitive data and other +// clusters contain mostly reference data +// c) some clusters are spanned by very large non-array objects that +// begin in some other cluster. When a large non-array object +// beginning in a preceding cluster spans large portions of +// this cluster, then because of imprecise dirtying, the +// portion of the object in this cluster may be clean, but +// will need to be processed by the worker responsible for +// this cluster, potentially increasing its work. +// d) in the case that the end of this cluster is spanned by a +// very large non-array object, the worker for this cluster will +// be responsible for processing the portion of the object +// in this cluster. +// +// Though an initial division of labor between marking threads may +// assign equal numbers of clusters to be scanned by each thread, it +// should be expected that some threads will finish their assigned +// work before others. Therefore, some amount of the full remembered +// set scanning effort should be held back and assigned incrementally +// to the threads that end up with excess capacity. Consider the +// following strategy for dividing labor: +// +// 1. Assume there are 8 marking threads and 1024 remembered +// set clusters to be scanned. +// 2. Assign each thread to scan 64 clusters. This leaves +// 512 (1024 - (8*64)) clusters to still be scanned. +// 3. As the 8 server threads complete previous cluster +// scanning assignments, issue each of the next 8 scanning +// assignments as units of 32 additional cluster each. +// In the case that there is high variance in effort +// associated with previous cluster scanning assignments, +// multiples of these next assignments may be serviced by +// the server threads that were previously assigned lighter +// workloads. +// 4. Make subsequent scanning assignments as follows: +// a) 8 assignments of size 16 clusters +// b) 8 assignments of size 8 clusters +// c) 16 assignments of size 4 clusters +// +// When there is no more remembered set processing work to be +// assigned to a newly idled worker thread, that thread can move +// on to work on other tasks associated with root scanning until such +// time as all clusters have been examined. +// +// Remembered set scanning is designed to run concurrently with +// mutator threads, with multiple concurrent workers. Furthermore, the +// current implementation of remembered set scanning never clears a +// card once it has been marked. +// +// These limitations will be addressed in future enhancements to the +// existing implementation. + +#include "gc/shared/workerThread.hpp" +#include "gc/shenandoah/shenandoahCardStats.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" +#include "gc/shenandoah/shenandoahNumberSeq.hpp" +#include "gc/shenandoah/shenandoahTaskqueue.hpp" +#include "memory/iterator.hpp" +#include "utilities/globalDefinitions.hpp" + +class ShenandoahReferenceProcessor; +class ShenandoahConcurrentMark; +class ShenandoahHeap; +class ShenandoahHeapRegion; +class ShenandoahRegionIterator; +class ShenandoahMarkingContext; + +class CardTable; +typedef CardTable::CardValue CardValue; + +class ShenandoahDirectCardMarkRememberedSet: public CHeapObj { + +private: + + // Use symbolic constants defined in cardTable.hpp + // CardTable::card_shift = 9; + // CardTable::card_size = 512; + // CardTable::card_size_in_words = 64; + // CardTable::clean_card_val() + // CardTable::dirty_card_val() + + const size_t LogCardValsPerIntPtr; // the number of card values (entries) in an intptr_t + const size_t LogCardSizeInWords; // the size of a card in heap word units + + ShenandoahHeap* _heap; + ShenandoahCardTable* _card_table; + size_t _card_shift; + size_t _total_card_count; + HeapWord* _whole_heap_base; // Points to first HeapWord of data contained within heap memory + CardValue* _byte_map; // Points to first entry within the card table + CardValue* _byte_map_base; // Points to byte_map minus the bias computed from address of heap memory + +public: + + // count is the number of cards represented by the card table. + ShenandoahDirectCardMarkRememberedSet(ShenandoahCardTable* card_table, size_t total_card_count); + + // Card index is zero-based relative to _byte_map. + size_t last_valid_index() const; + size_t total_cards() const; + size_t card_index_for_addr(HeapWord* p) const; + HeapWord* addr_for_card_index(size_t card_index) const; + inline const CardValue* get_card_table_byte_map(bool use_write_table) const { + return use_write_table ? _card_table->write_byte_map() : _card_table->read_byte_map(); + } + + inline bool is_card_dirty(size_t card_index) const; + inline bool is_write_card_dirty(size_t card_index) const; + inline void mark_card_as_dirty(size_t card_index); + inline void mark_range_as_dirty(size_t card_index, size_t num_cards); + inline void mark_card_as_clean(size_t card_index); + inline void mark_range_as_clean(size_t card_index, size_t num_cards); + inline bool is_card_dirty(HeapWord* p) const; + inline bool is_write_card_dirty(HeapWord* p) const; + inline void mark_card_as_dirty(HeapWord* p); + inline void mark_range_as_dirty(HeapWord* p, size_t num_heap_words); + inline void mark_card_as_clean(HeapWord* p); + inline void mark_range_as_clean(HeapWord* p, size_t num_heap_words); + + // Merge any dirty values from write table into the read table, while leaving + // the write table unchanged. + void merge_write_table(HeapWord* start, size_t word_count); + + // Destructively copy the write table to the read table, and clean the write table. + void reset_remset(HeapWord* start, size_t word_count); +}; + +// A ShenandoahCardCluster represents the minimal unit of work +// performed by independent parallel GC threads during scanning of +// remembered sets. +// +// The GC threads that perform card-table remembered set scanning may +// overwrite card-table entries to mark them as clean in the case that +// the associated memory no longer holds references to young-gen +// memory. Rather than access the card-table entries directly, all GC +// thread access to card-table information is made by way of the +// ShenandoahCardCluster data abstraction. This abstraction +// effectively manages access to multiple possible underlying +// remembered set implementations, including a traditional card-table +// approach and a SATB-based approach. +// +// The API services represent a compromise between efficiency and +// convenience. +// +// Multiple GC threads that scan the remembered set +// in parallel. The desire is to divide the complete scanning effort +// into multiple clusters of work that can be independently processed +// by individual threads without need for synchronizing efforts +// between the work performed by each task. The term "cluster" of +// work is similar to the term "stripe" as used in the implementation +// of Parallel GC. +// +// Complexity arises when an object to be scanned crosses the boundary +// between adjacent cluster regions. Here is the protocol that we currently +// follow: +// +// 1. The thread responsible for scanning the cards in a cluster modifies +// the associated card-table entries. Only cards that are dirty are +// processed, except as described below for the case of objects that +// straddle more than one card. +// 2. Object Arrays are precisely dirtied, so only the portion of the obj-array +// that overlaps the range of dirty cards in its cluster are scanned +// by each worker thread. This holds for portions of obj-arrays that extend +// over clusters processed by different workers, with each worked responsible +// for scanning the portion of the obj-array overlapping the dirty cards in +// its cluster. +// 3. Non-array objects are precisely dirtied by the interpreter and the compilers +// For such objects that extend over multiple cards, or even multiple clusters, +// the entire object is scanned by the worker that processes the (dirty) card on +// which the object's header lies. (However, GC workers should precisely dirty the +// cards with inter-regional/inter-generational pointers in the body of this object, +// thus making subsequent scans potentially less expensive.) Such larger non-array +// objects are relatively rare. +// +// A possible criticism: +// C. The representation of pointer location descriptive information +// within Klass representations is not designed for efficient +// "random access". An alternative approach to this design would +// be to scan very large objects multiple times, once for each +// cluster that is spanned by the object's range. This reduces +// unnecessary overscan, but it introduces different sorts of +// overhead effort: +// i) For each spanned cluster, we have to look up the start of +// the crossing object. +// ii) Each time we scan the very large object, we have to +// sequentially walk through its pointer location +// descriptors, skipping over all of the pointers that +// precede the start of the range of addresses that we +// consider relevant. + + +// Because old-gen heap memory is not necessarily contiguous, and +// because cards are not necessarily maintained for young-gen memory, +// consecutive card numbers do not necessarily correspond to consecutive +// address ranges. For the traditional direct-card-marking +// implementation of this interface, consecutive card numbers are +// likely to correspond to contiguous regions of memory, but this +// should not be assumed. Instead, rely only upon the following: +// +// 1. All card numbers for cards pertaining to the same +// ShenandoahHeapRegion are consecutively numbered. +// 2. In the case that neighboring ShenandoahHeapRegions both +// represent old-gen memory, the card regions that span the +// boundary between these neighboring heap regions will be +// consecutively numbered. +// 3. (A corollary) In the case that an old-gen object straddles the +// boundary between two heap regions, the card regions that +// correspond to the span of this object will be consecutively +// numbered. +// +// ShenandoahCardCluster abstracts access to the remembered set +// and also keeps track of crossing map information to allow efficient +// resolution of object start addresses. +// +// ShenandoahCardCluster supports all of the services of +// DirectCardMarkRememberedSet, plus it supports register_object() and lookup_object(). +// Note that we only need to register the start addresses of the object that +// overlays the first address of a card; we need to do this for every card. +// In other words, register_object() checks if the object crosses a card boundary, +// and updates the offset value for each card that the object crosses into. +// For objects that don't straddle cards, nothing needs to be done. +// +class ShenandoahCardCluster: public CHeapObj { + +private: + ShenandoahDirectCardMarkRememberedSet* _rs; + +public: + static const size_t CardsPerCluster = 64; + +private: + typedef struct cross_map { uint8_t first; uint8_t last; } xmap; + typedef union crossing_info { uint16_t short_word; xmap offsets; } crossing_info; + + // ObjectStartsInCardRegion bit is set within a crossing_info.offsets.start iff at least one object starts within + // a particular card region. We pack this bit into start byte under assumption that start byte is accessed less + // frequently than last byte. This is true when number of clean cards is greater than number of dirty cards. + static const uint8_t ObjectStartsInCardRegion = 0x80; + static const uint8_t FirstStartBits = 0x7f; + + // Check that we have enough bits to store the largest possible offset into a card for an object start. + // The value for maximum card size is based on the constraints for GCCardSizeInBytes in gc_globals.hpp. + static const int MaxCardSize = NOT_LP64(512) LP64_ONLY(1024); + STATIC_ASSERT((MaxCardSize / HeapWordSize) - 1 <= FirstStartBits); + + crossing_info* _object_starts; + +public: + // If we're setting first_start, assume the card has an object. + inline void set_first_start(size_t card_index, uint8_t value) { + _object_starts[card_index].offsets.first = ObjectStartsInCardRegion | value; + } + + inline void set_last_start(size_t card_index, uint8_t value) { + _object_starts[card_index].offsets.last = value; + } + + inline void set_starts_object_bit(size_t card_index) { + _object_starts[card_index].offsets.first |= ObjectStartsInCardRegion; + } + + inline void clear_starts_object_bit(size_t card_index) { + _object_starts[card_index].offsets.first &= ~ObjectStartsInCardRegion; + } + + // Returns true iff an object is known to start within the card memory associated with card card_index. + inline bool starts_object(size_t card_index) const { + return (_object_starts[card_index].offsets.first & ObjectStartsInCardRegion) != 0; + } + + inline void clear_objects_in_range(HeapWord* addr, size_t num_words) { + size_t card_index = _rs->card_index_for_addr(addr); + size_t last_card_index = _rs->card_index_for_addr(addr + num_words - 1); + while (card_index <= last_card_index) + _object_starts[card_index++].short_word = 0; + } + + ShenandoahCardCluster(ShenandoahDirectCardMarkRememberedSet* rs) { + _rs = rs; + _object_starts = NEW_C_HEAP_ARRAY(crossing_info, rs->total_cards(), mtGC); + for (size_t i = 0; i < rs->total_cards(); i++) { + _object_starts[i].short_word = 0; + } + } + + ~ShenandoahCardCluster() { + FREE_C_HEAP_ARRAY(crossing_info, _object_starts); + _object_starts = nullptr; + } + + // There is one entry within the object_starts array for each card entry. + // + // Suppose multiple garbage objects are coalesced during GC sweep + // into a single larger "free segment". As each two objects are + // coalesced together, the start information pertaining to the second + // object must be removed from the objects_starts array. If the + // second object had been the first object within card memory, + // the new first object is the object that follows that object if + // that starts within the same card memory, or NoObject if the + // following object starts within the following cluster. If the + // second object had been the last object in the card memory, + // replace this entry with the newly coalesced object if it starts + // within the same card memory, or with NoObject if it starts in a + // preceding card's memory. + // + // Suppose a large free segment is divided into a smaller free + // segment and a new object. The second part of the newly divided + // memory must be registered as a new object, overwriting at most + // one first_start and one last_start entry. Note that one of the + // newly divided two objects might be a new GCLAB. + // + // Suppose postprocessing of a GCLAB finds that the original GCLAB + // has been divided into N objects. Each of the N newly allocated + // objects will be registered, overwriting at most one first_start + // and one last_start entries. + // + // No object registration operations are linear in the length of + // the registered objects. + // + // Consider further the following observations regarding object + // registration costs: + // + // 1. The cost is paid once for each old-gen object (Except when + // an object is demoted and repromoted, in which case we would + // pay the cost again). + // 2. The cost can be deferred so that there is no urgency during + // mutator copy-on-first-access promotion. Background GC + // threads will update the object_starts array by post- + // processing the contents of retired PLAB buffers. + // 3. The bet is that these costs are paid relatively rarely + // because: + // a) Most objects die young and objects that die in young-gen + // memory never need to be registered with the object_starts + // array. + // b) Most objects that are promoted into old-gen memory live + // there without further relocation for a relatively long + // time, so we get a lot of benefit from each investment + // in registering an object. + +public: + + // The starting locations of objects contained within old-gen memory + // are registered as part of the remembered set implementation. This + // information is required when scanning dirty card regions that are + // spanned by objects beginning within preceding card regions. It + // is necessary to find the first and last objects that begin within + // this card region. Starting addresses of objects are required to + // find the object headers, and object headers provide information + // about which fields within the object hold addresses. + // + // The old-gen memory allocator invokes register_object() for any + // object that is allocated within old-gen memory. This identifies + // the starting addresses of objects that span boundaries between + // card regions. + // + // It is not necessary to invoke register_object at the very instant + // an object is allocated. It is only necessary to invoke it + // prior to the next start of a garbage collection concurrent mark + // or concurrent update-references phase. An "ideal" time to register + // objects is during post-processing of a GCLAB after the GCLAB is + // retired due to depletion of its memory. + // + // register_object() does not perform synchronization. In the case + // that multiple threads are registering objects whose starting + // addresses are within the same cluster, races between these + // threads may result in corruption of the object-start data + // structures. Parallel GC threads should avoid registering objects + // residing within the same cluster by adhering to the following + // coordination protocols: + // + // 1. Align thread-local GCLAB buffers with some TBD multiple of + // card clusters. The card cluster size is 32 KB. If the + // desired GCLAB size is 128 KB, align the buffer on a multiple + // of 4 card clusters. + // 2. Post-process the contents of GCLAB buffers to register the + // objects allocated therein. Allow one GC thread at a + // time to do the post-processing of each GCLAB. + // 3. Since only one GC thread at a time is registering objects + // belonging to a particular allocation buffer, no locking + // is performed when registering these objects. + // 4. Any remnant of unallocated memory within an expended GC + // allocation buffer is not returned to the old-gen allocation + // pool until after the GC allocation buffer has been post + // processed. Before any remnant memory is returned to the + // old-gen allocation pool, the GC thread that scanned this GC + // allocation buffer performs a write-commit memory barrier. + // 5. Background GC threads that perform tenuring of young-gen + // objects without a GCLAB use a CAS lock before registering + // each tenured object. The CAS lock assures both mutual + // exclusion and memory coherency/visibility. Note that an + // object tenured by a background GC thread will not overlap + // with any of the clusters that are receiving tenured objects + // by way of GCLAB buffers. Multiple independent GC threads may + // attempt to tenure objects into a shared cluster. This is why + // sychronization may be necessary. Consider the following + // scenarios: + // + // a) If two objects are tenured into the same card region, each + // registration may attempt to modify the first-start or + // last-start information associated with that card region. + // Furthermore, because the representations of first-start + // and last-start information within the object_starts array + // entry uses different bits of a shared uint_16 to represent + // each, it is necessary to lock the entire card entry + // before modifying either the first-start or last-start + // information within the entry. + // b) Suppose GC thread X promotes a tenured object into + // card region A and this tenured object spans into + // neighboring card region B. Suppose GC thread Y (not equal + // to X) promotes a tenured object into cluster B. GC thread X + // will update the object_starts information for card A. No + // synchronization is required. + // c) In summary, when background GC threads register objects + // newly tenured into old-gen memory, they must acquire a + // mutual exclusion lock on the card that holds the starting + // address of the newly tenured object. This can be achieved + // by using a CAS instruction to assure that the previous + // values of first-offset and last-offset have not been + // changed since the same thread inquired as to their most + // current values. + // + // One way to minimize the need for synchronization between + // background tenuring GC threads is for each tenuring GC thread + // to promote young-gen objects into distinct dedicated cluster + // ranges. + // 6. The object_starts information is only required during the + // starting of concurrent marking and concurrent evacuation + // phases of GC. Before we start either of these GC phases, the + // JVM enters a safe point and all GC threads perform + // commit-write barriers to assure that access to the + // object_starts information is coherent. + + + // Notes on synchronization of register_object(): + // + // 1. For efficiency, there is no locking in the implementation of register_object() + // 2. Thus, it is required that users of this service assure that concurrent/parallel invocations of + // register_object() do pertain to the same card's memory range. See discussion below to understand + // the risks. + // 3. When allocating from a TLAB or GCLAB, the mutual exclusion can be guaranteed by assuring that each + // LAB's start and end are aligned on card memory boundaries. + // 4. Use the same lock that guarantees exclusivity when performing free-list allocation within heap regions. + // + // Register the newly allocated object while we're holding the global lock since there's no synchronization + // built in to the implementation of register_object(). There are potential races when multiple independent + // threads are allocating objects, some of which might span the same card region. For example, consider + // a card table's memory region within which three objects are being allocated by three different threads: + // + // objects being "concurrently" allocated: + // [-----a------][-----b-----][--------------c------------------] + // [---- card table memory range --------------] + // + // Before any objects are allocated, this card's memory range holds no objects. Note that: + // allocation of object a wants to set the has-object, first-start, and last-start attributes of the preceding card region. + // allocation of object b wants to set the has-object, first-start, and last-start attributes of this card region. + // allocation of object c also wants to set the has-object, first-start, and last-start attributes of this card region. + // + // The thread allocating b and the thread allocating c can "race" in various ways, resulting in confusion, such as last-start + // representing object b while first-start represents object c. This is why we need to require all register_object() + // invocations associated with objects that are allocated from "free lists" to provide their own mutual exclusion locking + // mechanism. + + // Reset the starts_object() information to false for all cards in the range between from and to. + void reset_object_range(HeapWord* from, HeapWord* to); + + // register_object() requires that the caller hold the heap lock + // before calling it. + void register_object(HeapWord* address); + + // register_object_without_lock() does not require that the caller hold + // the heap lock before calling it, under the assumption that the + // caller has assured no other thread will endeavor to concurrently + // register objects that start within the same card's memory region + // as address. + void register_object_without_lock(HeapWord* address); + + // During the reference updates phase of GC, we walk through each old-gen memory region that was + // not part of the collection set and we invalidate all unmarked objects. As part of this effort, + // we coalesce neighboring dead objects in order to make future remembered set scanning more + // efficient (since future remembered set scanning of any card region containing consecutive + // dead objects can skip over all of them at once by reading only a single dead object header + // instead of having to read the header of each of the coalesced dead objects. + // + // At some future time, we may implement a further optimization: satisfy future allocation requests + // by carving new objects out of the range of memory that represents the coalesced dead objects. + // + // Suppose we want to combine several dead objects into a single coalesced object. How does this + // impact our representation of crossing map information? + // 1. If the newly coalesced range is contained entirely within a card range, that card's last + // start entry either remains the same or it is changed to the start of the coalesced region. + // 2. For the card that holds the start of the coalesced object, it will not impact the first start + // but it may impact the last start. + // 3. For following cards spanned entirely by the newly coalesced object, it will change starts_object + // to false (and make first-start and last-start "undefined"). + // 4. For a following card that is spanned patially by the newly coalesced object, it may change + // first-start value, but it will not change the last-start value. + // + // The range of addresses represented by the arguments to coalesce_objects() must represent a range + // of memory that was previously occupied exactly by one or more previously registered objects. For + // convenience, it is legal to invoke coalesce_objects() with arguments that span a single previously + // registered object. + // + // The role of coalesce_objects is to change the crossing map information associated with all of the coalesced + // objects. + void coalesce_objects(HeapWord* address, size_t length_in_words); + + // The typical use case is going to look something like this: + // for each heapregion that comprises old-gen memory + // for each card number that corresponds to this heap region + // scan the objects contained therein if the card is dirty + // To avoid excessive lookups in a sparse array, the API queries + // the card number pertaining to a particular address and then uses the + // card number for subsequent information lookups and stores. + + // If starts_object(card_index), this returns the word offset within this card + // memory at which the first object begins. If !starts_object(card_index), the + // result is a don't care value -- asserts in a debug build. + size_t get_first_start(size_t card_index) const; + + // If starts_object(card_index), this returns the word offset within this card + // memory at which the last object begins. If !starts_object(card_index), the + // result is a don't care value. + size_t get_last_start(size_t card_index) const; + + + // Given a card_index, return the starting address of the first block in the heap + // that straddles into the card. If the card is co-initial with an object, then + // this would return the starting address of the heap that this card covers. + // Expects to be called for a card affiliated with the old generation in + // generational mode. + HeapWord* block_start(size_t card_index) const; +}; + +// ShenandoahScanRemembered is a concrete class representing the +// ability to scan the old-gen remembered set for references to +// objects residing in young-gen memory. +// +// Scanning normally begins with an invocation of numRegions and ends +// after all clusters of all regions have been scanned. +// +// Throughout the scanning effort, the number of regions does not +// change. +// +// Even though the regions that comprise old-gen memory are not +// necessarily contiguous, the abstraction represented by this class +// identifies each of the old-gen regions with an integer value +// in the range from 0 to (numRegions() - 1) inclusive. +// + +class ShenandoahScanRemembered: public CHeapObj { + +private: + ShenandoahDirectCardMarkRememberedSet* _rs; + ShenandoahCardCluster* _scc; + + // Global card stats (cumulative) + HdrSeq _card_stats_scan_rs[MAX_CARD_STAT_TYPE]; + HdrSeq _card_stats_update_refs[MAX_CARD_STAT_TYPE]; + // Per worker card stats (multiplexed by phase) + HdrSeq** _card_stats; + + // The types of card metrics that we gather + const char* _card_stats_name[MAX_CARD_STAT_TYPE] = { + "dirty_run", "clean_run", + "dirty_cards", "clean_cards", + "max_dirty_run", "max_clean_run", + "dirty_scan_objs", + "alternations" + }; + + // The statistics are collected and logged separately for + // card-scans for initial marking, and for updating refs. + const char* _card_stat_log_type[MAX_CARD_STAT_LOG_TYPE] = { + "Scan Remembered Set", "Update Refs" + }; + + int _card_stats_log_counter[2] = {0, 0}; + +public: + ShenandoahScanRemembered(ShenandoahDirectCardMarkRememberedSet* rs) { + _rs = rs; + _scc = new ShenandoahCardCluster(rs); + + // We allocate ParallelGCThreads worth even though we usually only + // use up to ConcGCThreads, because degenerate collections may employ + // ParallelGCThreads for remembered set scanning. + if (ShenandoahEnableCardStats) { + _card_stats = NEW_C_HEAP_ARRAY(HdrSeq*, ParallelGCThreads, mtGC); + for (uint i = 0; i < ParallelGCThreads; i++) { + _card_stats[i] = new HdrSeq[MAX_CARD_STAT_TYPE]; + } + } else { + _card_stats = nullptr; + } + } + + ~ShenandoahScanRemembered() { + delete _scc; + if (ShenandoahEnableCardStats) { + for (uint i = 0; i < ParallelGCThreads; i++) { + delete _card_stats[i]; + } + FREE_C_HEAP_ARRAY(HdrSeq*, _card_stats); + _card_stats = nullptr; + } + assert(_card_stats == nullptr, "Error"); + } + + HdrSeq* card_stats(uint worker_id) { + assert(worker_id < ParallelGCThreads, "Error"); + assert(ShenandoahEnableCardStats == (_card_stats != nullptr), "Error"); + return ShenandoahEnableCardStats ? _card_stats[worker_id] : nullptr; + } + + HdrSeq* card_stats_for_phase(CardStatLogType t) { + switch (t) { + case CARD_STAT_SCAN_RS: + return _card_stats_scan_rs; + case CARD_STAT_UPDATE_REFS: + return _card_stats_update_refs; + default: + guarantee(false, "No such CardStatLogType"); + } + return nullptr; // Quiet compiler + } + + // Card index is zero-based relative to first spanned card region. + size_t card_index_for_addr(HeapWord* p); + HeapWord* addr_for_card_index(size_t card_index); + bool is_card_dirty(size_t card_index); + bool is_write_card_dirty(size_t card_index); + bool is_card_dirty(HeapWord* p); + bool is_write_card_dirty(HeapWord* p); + void mark_card_as_dirty(HeapWord* p); + void mark_range_as_dirty(HeapWord* p, size_t num_heap_words); + void mark_card_as_clean(HeapWord* p); + void mark_range_as_clean(HeapWord* p, size_t num_heap_words); + + void reset_remset(HeapWord* start, size_t word_count) { _rs->reset_remset(start, word_count); } + + void merge_write_table(HeapWord* start, size_t word_count) { _rs->merge_write_table(start, word_count); } + + size_t cluster_for_addr(HeapWord* addr); + HeapWord* addr_for_cluster(size_t cluster_no); + + void reset_object_range(HeapWord* from, HeapWord* to); + void register_object(HeapWord* addr); + void register_object_without_lock(HeapWord* addr); + void coalesce_objects(HeapWord* addr, size_t length_in_words); + + HeapWord* first_object_in_card(size_t card_index) { + if (_scc->starts_object(card_index)) { + return addr_for_card_index(card_index) + _scc->get_first_start(card_index); + } else { + return nullptr; + } + } + + // Return true iff this object is "properly" registered. + bool verify_registration(HeapWord* address, ShenandoahMarkingContext* ctx); + + // clear the cards to clean, and clear the object_starts info to no objects + void mark_range_as_empty(HeapWord* addr, size_t length_in_words); + + // process_clusters() scans a portion of the remembered set + // for references from old gen into young. Several worker threads + // scan different portions of the remembered set by making parallel invocations + // of process_clusters() with each invocation scanning different + // "clusters" of the remembered set. + // + // An invocation of process_clusters() examines all of the + // intergenerational references spanned by `count` clusters starting + // with `first_cluster`. The `oops` argument is a worker-thread-local + // OopClosure that is applied to all "valid" references in the remembered set. + // + // A side-effect of executing process_clusters() is to update the remembered + // set entries (e.g. marking dirty cards clean if they no longer + // hold references to young-gen memory). + // + // An implementation of process_clusters() may choose to efficiently + // address more typical scenarios in the structure of remembered sets. E.g. + // in the generational setting, one might expect remembered sets to be very sparse + // (low mutation rates in the old generation leading to sparse dirty cards, + // each with very few intergenerational pointers). Specific implementations + // may choose to degrade gracefully as the sparsity assumption fails to hold, + // such as when there are sudden spikes in (premature) promotion or in the + // case of an underprovisioned, poorly-tuned, or poorly-shaped heap. + // + // At the start of a concurrent young generation marking cycle, we invoke process_clusters + // with ClosureType ShenandoahInitMarkRootsClosure. + // + // At the start of a concurrent evacuation phase, we invoke process_clusters with + // ClosureType ShenandoahEvacuateUpdateRootsClosure. + + template + void process_clusters(size_t first_cluster, size_t count, HeapWord* end_of_range, ClosureType* oops, + bool use_write_table, uint worker_id); + + template + void process_humongous_clusters(ShenandoahHeapRegion* r, size_t first_cluster, size_t count, + HeapWord* end_of_range, ClosureType* oops, bool use_write_table); + + template + void process_region_slice(ShenandoahHeapRegion* region, size_t offset, size_t clusters, HeapWord* end_of_range, + ClosureType* cl, bool use_write_table, uint worker_id); + + // To Do: + // Create subclasses of ShenandoahInitMarkRootsClosure and + // ShenandoahEvacuateUpdateRootsClosure and any other closures + // that need to participate in remembered set scanning. Within the + // subclasses, add a (probably templated) instance variable that + // refers to the associated ShenandoahCardCluster object. Use this + // ShenandoahCardCluster instance to "enhance" the do_oops + // processing so that we can: + // + // 1. Avoid processing references that correspond to clean card + // regions, and + // 2. Set card status to CLEAN when the associated card region no + // longer holds inter-generatioanal references. + // + // To enable efficient implementation of these behaviors, we + // probably also want to add a few fields into the + // ShenandoahCardCluster object that allow us to precompute and + // remember the addresses at which card status is going to change + // from dirty to clean and clean to dirty. The do_oops + // implementations will want to update this value each time they + // cross one of these boundaries. + void roots_do(OopIterateClosure* cl); + + // Log stats related to card/RS stats for given phase t + void log_card_stats(uint nworkers, CardStatLogType t) PRODUCT_RETURN; +private: + // Log stats for given worker id related into given summary card/RS stats + void log_worker_card_stats(uint worker_id, HdrSeq* sum_stats) PRODUCT_RETURN; + + // Log given stats + void log_card_stats(HdrSeq* stats) PRODUCT_RETURN; + + // Merge the stats from worked_id into the given summary stats, and clear the worker_id's stats. + void merge_worker_card_stats_cumulative(HdrSeq* worker_stats, HdrSeq* sum_stats) PRODUCT_RETURN; +}; + + +// A ShenandoahRegionChunk represents a contiguous interval of a ShenandoahHeapRegion, typically representing +// work to be done by a worker thread. +struct ShenandoahRegionChunk { + ShenandoahHeapRegion* _r; // The region of which this represents a chunk + size_t _chunk_offset; // HeapWordSize offset + size_t _chunk_size; // HeapWordSize qty +}; + +// ShenandoahRegionChunkIterator divides the total remembered set scanning effort into ShenandoahRegionChunks +// that are assigned one at a time to worker threads. (Here, we use the terms `assignments` and `chunks` +// interchangeably.) Note that the effort required to scan a range of memory is not necessarily a linear +// function of the size of the range. Some memory ranges hold only a small number of live objects. +// Some ranges hold primarily primitive (non-pointer) data. We start with larger chunk sizes because larger chunks +// reduce coordination overhead. We expect that the GC worker threads that receive more difficult assignments +// will work longer on those chunks. Meanwhile, other worker threads will repeatedly accept and complete multiple +// easier chunks. As the total amount of work remaining to be completed decreases, we decrease the size of chunks +// given to individual threads. This reduces the likelihood of significant imbalance between worker thread assignments +// when there is less meaningful work to be performed by the remaining worker threads while they wait for +// worker threads with difficult assignments to finish, reducing the overall duration of the phase. + +class ShenandoahRegionChunkIterator : public StackObj { +private: + // The largest chunk size is 4 MiB, measured in words. Otherwise, remembered set scanning may become too unbalanced. + // If the largest chunk size is too small, there is too much overhead sifting out assignments to individual worker threads. + static const size_t _maximum_chunk_size_words = (4 * 1024 * 1024) / HeapWordSize; + + static const size_t _clusters_in_smallest_chunk = 4; + + // smallest_chunk_size is 4 clusters. Each cluster spans 128 KiB. + // This is computed from CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster; + static size_t smallest_chunk_size_words() { + return _clusters_in_smallest_chunk * CardTable::card_size_in_words() * + ShenandoahCardCluster::CardsPerCluster; + } + + // The total remembered set scanning effort is divided into chunks of work that are assigned to individual worker tasks. + // The chunks of assigned work are divided into groups, where the size of the typical group (_regular_group_size) is half the + // total number of regions. The first group may be larger than + // _regular_group_size in the case that the first group's chunk + // size is less than the region size. The last group may be larger + // than _regular_group_size because no group is allowed to + // have smaller assignments than _smallest_chunk_size, which is 128 KB. + + // Under normal circumstances, no configuration needs more than _maximum_groups (default value of 16). + // The first group "effectively" processes chunks of size 1 MiB (or smaller for smaller region sizes). + // The last group processes chunks of size 128 KiB. There are four groups total. + + // group[0] is 4 MiB chunk size (_maximum_chunk_size_words) + // group[1] is 2 MiB chunk size + // group[2] is 1 MiB chunk size + // group[3] is 512 KiB chunk size + // group[4] is 256 KiB chunk size + // group[5] is 128 Kib shunk size (_smallest_chunk_size_words = 4 * 64 * 64 + static const size_t _maximum_groups = 6; + + const ShenandoahHeap* _heap; + + const size_t _regular_group_size; // Number of chunks in each group + const size_t _first_group_chunk_size_b4_rebalance; + const size_t _num_groups; // Number of groups in this configuration + const size_t _total_chunks; + + shenandoah_padding(0); + volatile size_t _index; + shenandoah_padding(1); + + size_t _region_index[_maximum_groups]; // The region index for the first region spanned by this group + size_t _group_offset[_maximum_groups]; // The offset at which group begins within first region spanned by this group + size_t _group_chunk_size[_maximum_groups]; // The size of each chunk within this group + size_t _group_entries[_maximum_groups]; // Total chunks spanned by this group and the ones before it. + + // No implicit copying: iterators should be passed by reference to capture the state + NONCOPYABLE(ShenandoahRegionChunkIterator); + + // Makes use of _heap. + size_t calc_regular_group_size(); + + // Makes use of _regular_group_size, which must be initialized before call. + size_t calc_first_group_chunk_size_b4_rebalance(); + + // Makes use of _regular_group_size and _first_group_chunk_size_b4_rebalance, both of which must be initialized before call. + size_t calc_num_groups(); + + // Makes use of _regular_group_size, _first_group_chunk_size_b4_rebalance, which must be initialized before call. + size_t calc_total_chunks(); + +public: + ShenandoahRegionChunkIterator(size_t worker_count); + ShenandoahRegionChunkIterator(ShenandoahHeap* heap, size_t worker_count); + + // Reset iterator to default state + void reset(); + + // Fills in assignment with next chunk of work and returns true iff there is more work. + // Otherwise, returns false. This is multi-thread-safe. + inline bool next(struct ShenandoahRegionChunk* assignment); + + // This is *not* MT safe. However, in the absence of multithreaded access, it + // can be used to determine if there is more work to do. + inline bool has_next() const; +}; + + +class ShenandoahScanRememberedTask : public WorkerTask { + private: + ShenandoahObjToScanQueueSet* _queue_set; + ShenandoahObjToScanQueueSet* _old_queue_set; + ShenandoahReferenceProcessor* _rp; + ShenandoahRegionChunkIterator* _work_list; + bool _is_concurrent; + + public: + ShenandoahScanRememberedTask(ShenandoahObjToScanQueueSet* queue_set, + ShenandoahObjToScanQueueSet* old_queue_set, + ShenandoahReferenceProcessor* rp, + ShenandoahRegionChunkIterator* work_list, + bool is_concurrent); + + void work(uint worker_id); + void do_work(uint worker_id); +}; + +// After Full GC is done, reconstruct the remembered set by iterating over OLD regions, +// registering all objects between bottom() and top(), and dirtying the cards containing +// cross-generational pointers. +class ShenandoahReconstructRememberedSetTask : public WorkerTask { +private: + ShenandoahRegionIterator* _regions; + +public: + explicit ShenandoahReconstructRememberedSetTask(ShenandoahRegionIterator* regions); + + void work(uint worker_id) override; +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBERED_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp new file mode 100644 index 00000000000..ec00adc4040 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp @@ -0,0 +1,405 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP + +#include "memory/iterator.hpp" +#include "oops/oop.hpp" +#include "oops/objArrayOop.hpp" +#include "gc/shared/collectorCounters.hpp" +#include "gc/shenandoah/shenandoahCardStats.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "logging/log.hpp" + +// Process all objects starting within count clusters beginning with first_cluster and for which the start address is +// less than end_of_range. For any non-array object whose header lies on a dirty card, scan the entire object, +// even if its end reaches beyond end_of_range. Object arrays, on the other hand, are precisely dirtied and +// only the portions of the array on dirty cards need to be scanned. +// +// Do not CANCEL within process_clusters. It is assumed that if a worker thread accepts responsibility for processing +// a chunk of work, it will finish the work it starts. Otherwise, the chunk of work will be lost in the transition to +// degenerated execution, leading to dangling references. +template +void ShenandoahScanRemembered::process_clusters(size_t first_cluster, size_t count, HeapWord* end_of_range, + ClosureType* cl, bool use_write_table, uint worker_id) { + + assert(ShenandoahHeap::heap()->old_generation()->is_parsable(), "Old generation regions must be parsable for remembered set scan"); + // If old-gen evacuation is active, then MarkingContext for old-gen heap regions is valid. We use the MarkingContext + // bits to determine which objects within a DIRTY card need to be scanned. This is necessary because old-gen heap + // regions that are in the candidate collection set have not been coalesced and filled. Thus, these heap regions + // may contain zombie objects. Zombie objects are known to be dead, but have not yet been "collected". Scanning + // zombie objects is unsafe because the Klass pointer is not reliable, objects referenced from a zombie may have been + // collected (if dead), or relocated (if live), or if dead but not yet collected, we don't want to "revive" them + // by marking them (when marking) or evacuating them (when updating references). + + // start and end addresses of range of objects to be scanned, clipped to end_of_range + const size_t start_card_index = first_cluster * ShenandoahCardCluster::CardsPerCluster; + const HeapWord* start_addr = _rs->addr_for_card_index(start_card_index); + // clip at end_of_range (exclusive) + HeapWord* end_addr = MIN2(end_of_range, (HeapWord*)start_addr + (count * ShenandoahCardCluster::CardsPerCluster + * CardTable::card_size_in_words())); + assert(start_addr < end_addr, "Empty region?"); + + const size_t whole_cards = (end_addr - start_addr + CardTable::card_size_in_words() - 1)/CardTable::card_size_in_words(); + const size_t end_card_index = start_card_index + whole_cards - 1; + log_debug(gc, remset)("Worker %u: cluster = " SIZE_FORMAT " count = " SIZE_FORMAT " eor = " INTPTR_FORMAT + " start_addr = " INTPTR_FORMAT " end_addr = " INTPTR_FORMAT " cards = " SIZE_FORMAT, + worker_id, first_cluster, count, p2i(end_of_range), p2i(start_addr), p2i(end_addr), whole_cards); + + // use_write_table states whether we are using the card table that is being + // marked by the mutators. If false, we are using a snapshot of the card table + // that is not subject to modifications. Even when this arg is true, and + // the card table is being actively marked, SATB marking ensures that we need not + // worry about cards marked after the processing here has passed them. + const CardValue* const ctbm = _rs->get_card_table_byte_map(use_write_table); + + // If old gen evacuation is active, ctx will hold the completed marking of + // old generation objects. We'll only scan objects that are marked live by + // the old generation marking. These include objects allocated since the + // start of old generation marking (being those above TAMS). + const ShenandoahHeap* heap = ShenandoahHeap::heap(); + const ShenandoahMarkingContext* ctx = heap->old_generation()->is_mark_complete() ? + heap->marking_context() : nullptr; + + // The region we will scan is the half-open interval [start_addr, end_addr), + // and lies entirely within a single region. + const ShenandoahHeapRegion* region = ShenandoahHeap::heap()->heap_region_containing(start_addr); + assert(region->contains(end_addr - 1), "Slice shouldn't cross regions"); + + // This code may have implicit assumptions of examining only old gen regions. + assert(region->is_old(), "We only expect to be processing old regions"); + assert(!region->is_humongous(), "Humongous regions can be processed more efficiently;" + "see process_humongous_clusters()"); + // tams and ctx below are for old generation marking. As such, young gen roots must + // consider everything above tams, since it doesn't represent a TAMS for young gen's + // SATB marking. + const HeapWord* tams = (ctx == nullptr ? region->bottom() : ctx->top_at_mark_start(region)); + + NOT_PRODUCT(ShenandoahCardStats stats(whole_cards, card_stats(worker_id));) + + // In the case of imprecise marking, we remember the lowest address + // scanned in a range of dirty cards, as we work our way left from the + // highest end_addr. This serves as another upper bound on the address we will + // scan as we move left over each contiguous range of dirty cards. + HeapWord* upper_bound = nullptr; + + // Starting at the right end of the address range, walk backwards accumulating + // a maximal dirty range of cards, then process those cards. + ssize_t cur_index = (ssize_t) end_card_index; + assert(cur_index >= 0, "Overflow"); + assert(((ssize_t)start_card_index) >= 0, "Overflow"); + while (cur_index >= (ssize_t)start_card_index) { + + // We'll continue the search starting with the card for the upper bound + // address identified by the last dirty range that we processed, if any, + // skipping any cards at higher addresses. + if (upper_bound != nullptr) { + ssize_t right_index = _rs->card_index_for_addr(upper_bound); + assert(right_index >= 0, "Overflow"); + cur_index = MIN2(cur_index, right_index); + assert(upper_bound < end_addr, "Program logic"); + end_addr = upper_bound; // lower end_addr + upper_bound = nullptr; // and clear upper_bound + if (end_addr <= start_addr) { + assert(right_index <= (ssize_t)start_card_index, "Program logic"); + // We are done with our cluster + return; + } + } + + if (ctbm[cur_index] == CardTable::dirty_card_val()) { + // ==== BEGIN DIRTY card range processing ==== + + const size_t dirty_r = cur_index; // record right end of dirty range (inclusive) + while (--cur_index >= (ssize_t)start_card_index && ctbm[cur_index] == CardTable::dirty_card_val()) { + // walk back over contiguous dirty cards to find left end of dirty range (inclusive) + } + // [dirty_l, dirty_r] is a "maximal" closed interval range of dirty card indices: + // it may not be maximal if we are using the write_table, because of concurrent + // mutations dirtying the card-table. It may also not be maximal if an upper bound + // was established by the scan of the previous chunk. + const size_t dirty_l = cur_index + 1; // record left end of dirty range (inclusive) + // Check that we identified a boundary on our left + assert(ctbm[dirty_l] == CardTable::dirty_card_val(), "First card in range should be dirty"); + assert(dirty_l == start_card_index || use_write_table + || ctbm[dirty_l - 1] == CardTable::clean_card_val(), + "Interval isn't maximal on the left"); + assert(dirty_r >= dirty_l, "Error"); + assert(ctbm[dirty_r] == CardTable::dirty_card_val(), "Last card in range should be dirty"); + // Record alternations, dirty run length, and dirty card count + NOT_PRODUCT(stats.record_dirty_run(dirty_r - dirty_l + 1);) + + // Find first object that starts this range: + // [left, right) is a maximal right-open interval of dirty cards + HeapWord* left = _rs->addr_for_card_index(dirty_l); // inclusive + HeapWord* right = _rs->addr_for_card_index(dirty_r + 1); // exclusive + // Clip right to end_addr established above (still exclusive) + right = MIN2(right, end_addr); + assert(right <= region->top() && end_addr <= region->top(), "Busted bounds"); + const MemRegion mr(left, right); + + // NOTE: We'll not call block_start() repeatedly + // on a very large object if its head card is dirty. If not, + // (i.e. the head card is clean) we'll call it each time we + // process a new dirty range on the object. This is always + // the case for large object arrays, which are typically more + // common. + HeapWord* p = _scc->block_start(dirty_l); + oop obj = cast_to_oop(p); + + // PREFIX: The object that straddles into this range of dirty cards + // from the left may be subject to special treatment unless + // it is an object array. + if (p < left && !obj->is_objArray()) { + // The mutator (both compiler and interpreter, but not JNI?) + // typically dirty imprecisely (i.e. only the head of an object), + // but GC closures typically dirty the object precisely. (It would + // be nice to have everything be precise for maximum efficiency.) + // + // To handle this, we check the head card of the object here and, + // if dirty, (arrange to) scan the object in its entirety. If we + // find the head card clean, we'll scan only the portion of the + // object lying in the dirty card range below, assuming this was + // the result of precise marking by GC closures. + + // index of the "head card" for p + const size_t hc_index = _rs->card_index_for_addr(p); + if (ctbm[hc_index] == CardTable::dirty_card_val()) { + // Scan or skip the object, depending on location of its + // head card, and remember that we'll have processed all + // the objects back up to p, which is thus an upper bound + // for the next iteration of a dirty card loop. + upper_bound = p; // remember upper bound for next chunk + if (p < start_addr) { + // if object starts in a previous slice, it'll be handled + // in its entirety by the thread processing that slice; we can + // skip over it and avoid an unnecessary extra scan. + assert(obj == cast_to_oop(p), "Inconsistency detected"); + p += obj->size(); + } else { + // the object starts in our slice, we scan it in its entirety + assert(obj == cast_to_oop(p), "Inconsistency detected"); + if (ctx == nullptr || ctx->is_marked(obj)) { + // Scan the object in its entirety + p += obj->oop_iterate_size(cl); + } else { + assert(p < tams, "Error 1 in ctx/marking/tams logic"); + // Skip over any intermediate dead objects + p = ctx->get_next_marked_addr(p, tams); + assert(p <= tams, "Error 2 in ctx/marking/tams logic"); + } + } + assert(p > left, "Should have processed into interior of dirty range"); + } + } + + size_t i = 0; + HeapWord* last_p = nullptr; + + // BODY: Deal with (other) objects in this dirty card range + while (p < right) { + obj = cast_to_oop(p); + // walk right scanning eligible objects + if (ctx == nullptr || ctx->is_marked(obj)) { + // we need to remember the last object ptr we scanned, in case we need to + // complete a partial suffix scan after mr, see below + last_p = p; + // apply the closure to the oops in the portion of + // the object within mr. + p += obj->oop_iterate_size(cl, mr); + NOT_PRODUCT(i++); + } else { + // forget the last object pointer we remembered + last_p = nullptr; + assert(p < tams, "Tams and above are implicitly marked in ctx"); + // object under tams isn't marked: skip to next live object + p = ctx->get_next_marked_addr(p, tams); + assert(p <= tams, "Error 3 in ctx/marking/tams logic"); + } + } + + // SUFFIX: Fix up a possible incomplete scan at right end of window + // by scanning the portion of a non-objArray that wasn't done. + if (p > right && last_p != nullptr) { + assert(last_p < right, "Error"); + // check if last_p suffix needs scanning + const oop last_obj = cast_to_oop(last_p); + if (!last_obj->is_objArray()) { + // scan the remaining suffix of the object + const MemRegion last_mr(right, p); + assert(p == last_p + last_obj->size(), "Would miss portion of last_obj"); + last_obj->oop_iterate(cl, last_mr); + log_develop_debug(gc, remset)("Fixed up non-objArray suffix scan in [" INTPTR_FORMAT ", " INTPTR_FORMAT ")", + p2i(last_mr.start()), p2i(last_mr.end())); + } else { + log_develop_debug(gc, remset)("Skipped suffix scan of objArray in [" INTPTR_FORMAT ", " INTPTR_FORMAT ")", + p2i(right), p2i(p)); + } + } + NOT_PRODUCT(stats.record_scan_obj_cnt(i);) + + // ==== END DIRTY card range processing ==== + } else { + // ==== BEGIN CLEAN card range processing ==== + + // If we are using the write table (during update refs, e.g.), a mutator may dirty + // a card at any time. This is fine for the algorithm below because it is only + // counting contiguous runs of clean cards (and only for non-product builds). + assert(use_write_table || ctbm[cur_index] == CardTable::clean_card_val(), "Error"); + + // walk back over contiguous clean cards + size_t i = 0; + while (--cur_index >= (ssize_t)start_card_index && ctbm[cur_index] == CardTable::clean_card_val()) { + NOT_PRODUCT(i++); + } + // Record alternations, clean run length, and clean card count + NOT_PRODUCT(stats.record_clean_run(i);) + + // ==== END CLEAN card range processing ==== + } + } +} + +// Given that this range of clusters is known to span a humongous object spanned by region r, scan the +// portion of the humongous object that corresponds to the specified range. +template +inline void +ShenandoahScanRemembered::process_humongous_clusters(ShenandoahHeapRegion* r, size_t first_cluster, size_t count, + HeapWord *end_of_range, ClosureType *cl, bool use_write_table) { + ShenandoahHeapRegion* start_region = r->humongous_start_region(); + HeapWord* p = start_region->bottom(); + oop obj = cast_to_oop(p); + assert(r->is_humongous(), "Only process humongous regions here"); + assert(start_region->is_humongous_start(), "Should be start of humongous region"); + assert(p + obj->size() >= end_of_range, "Humongous object ends before range ends"); + + size_t first_card_index = first_cluster * ShenandoahCardCluster::CardsPerCluster; + HeapWord* first_cluster_addr = _rs->addr_for_card_index(first_card_index); + size_t spanned_words = count * ShenandoahCardCluster::CardsPerCluster * CardTable::card_size_in_words(); + start_region->oop_iterate_humongous_slice_dirty(cl, first_cluster_addr, spanned_words, use_write_table); +} + + +// This method takes a region & determines the end of the region that the worker can scan. +template +inline void +ShenandoahScanRemembered::process_region_slice(ShenandoahHeapRegion *region, size_t start_offset, size_t clusters, + HeapWord *end_of_range, ClosureType *cl, bool use_write_table, + uint worker_id) { + + // This is called only for young gen collection, when we scan old gen regions + assert(region->is_old(), "Expecting an old region"); + HeapWord *start_of_range = region->bottom() + start_offset; + size_t start_cluster_no = cluster_for_addr(start_of_range); + assert(addr_for_cluster(start_cluster_no) == start_of_range, "process_region_slice range must align on cluster boundary"); + + // region->end() represents the end of memory spanned by this region, but not all of this + // memory is eligible to be scanned because some of this memory has not yet been allocated. + // + // region->top() represents the end of allocated memory within this region. Any addresses + // beyond region->top() should not be scanned as that memory does not hold valid objects. + + if (use_write_table) { + // This is update-refs servicing. + if (end_of_range > region->get_update_watermark()) { + end_of_range = region->get_update_watermark(); + } + } else { + // This is concurrent mark servicing. Note that TAMS for this region is TAMS at start of old-gen + // collection. Here, we need to scan up to TAMS for most recently initiated young-gen collection. + // Since all LABs are retired at init mark, and since replacement LABs are allocated lazily, and since no + // promotions occur until evacuation phase, TAMS for most recent young-gen is same as top(). + if (end_of_range > region->top()) { + end_of_range = region->top(); + } + } + + log_debug(gc)("Remembered set scan processing Region " SIZE_FORMAT ", from " PTR_FORMAT " to " PTR_FORMAT ", using %s table", + region->index(), p2i(start_of_range), p2i(end_of_range), + use_write_table? "read/write (updating)": "read (marking)"); + + // Note that end_of_range may point to the middle of a cluster because we limit scanning to + // region->top() or region->get_update_watermark(). We avoid processing past end_of_range. + // Objects that start between start_of_range and end_of_range, including humongous objects, will + // be fully processed by process_clusters. In no case should we need to scan past end_of_range. + if (start_of_range < end_of_range) { + if (region->is_humongous()) { + ShenandoahHeapRegion* start_region = region->humongous_start_region(); + process_humongous_clusters(start_region, start_cluster_no, clusters, end_of_range, cl, use_write_table); + } else { + process_clusters(start_cluster_no, clusters, end_of_range, cl, use_write_table, worker_id); + } + } +} + +inline bool ShenandoahRegionChunkIterator::has_next() const { + return _index < _total_chunks; +} + +inline bool ShenandoahRegionChunkIterator::next(struct ShenandoahRegionChunk *assignment) { + if (_index >= _total_chunks) { + return false; + } + size_t new_index = Atomic::add(&_index, (size_t) 1, memory_order_relaxed); + if (new_index > _total_chunks) { + // First worker that hits new_index == _total_chunks continues, other + // contending workers return false. + return false; + } + // convert to zero-based indexing + new_index--; + assert(new_index < _total_chunks, "Error"); + + // Find the group number for the assigned chunk index + size_t group_no; + for (group_no = 0; new_index >= _group_entries[group_no]; group_no++) + ; + assert(group_no < _num_groups, "Cannot have group no greater or equal to _num_groups"); + + // All size computations measured in HeapWord + size_t region_size_words = ShenandoahHeapRegion::region_size_words(); + size_t group_region_index = _region_index[group_no]; + size_t group_region_offset = _group_offset[group_no]; + + size_t index_within_group = (group_no == 0)? new_index: new_index - _group_entries[group_no - 1]; + size_t group_chunk_size = _group_chunk_size[group_no]; + size_t offset_of_this_chunk = group_region_offset + index_within_group * group_chunk_size; + size_t regions_spanned_by_chunk_offset = offset_of_this_chunk / region_size_words; + size_t offset_within_region = offset_of_this_chunk % region_size_words; + + size_t region_index = group_region_index + regions_spanned_by_chunk_offset; + + assignment->_r = _heap->get_region(region_index); + assignment->_chunk_offset = offset_within_region; + assignment->_chunk_size = group_chunk_size; + return true; +} + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp index 80c1d3417b2..3d5a2b149a7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp @@ -121,14 +121,15 @@ typedef struct ShenandoahSharedBitmap { ShenandoahSharedValue mask_val = (ShenandoahSharedValue) mask; while (true) { ShenandoahSharedValue ov = Atomic::load_acquire(&value); - if ((ov & mask_val) != 0) { + // We require all bits of mask_val to be set + if ((ov & mask_val) == mask_val) { // already set return; } ShenandoahSharedValue nv = ov | mask_val; if (Atomic::cmpxchg(&value, ov, nv) == ov) { - // successfully set + // successfully set: if value returned from cmpxchg equals ov, then nv has overwritten value. return; } } @@ -156,10 +157,19 @@ typedef struct ShenandoahSharedBitmap { Atomic::release_store_fence(&value, (ShenandoahSharedValue)0); } + // Returns true iff any bit set in mask is set in this.value. bool is_set(uint mask) const { return !is_unset(mask); } + // Returns true iff all bits set in mask are set in this.value. + bool is_set_exactly(uint mask) const { + assert (mask < (sizeof(ShenandoahSharedValue) * CHAR_MAX), "sanity"); + uint uvalue = Atomic::load_acquire(&value); + return (uvalue & mask) == mask; + } + + // Returns true iff all bits set in mask are unset in this.value. bool is_unset(uint mask) const { assert (mask < (sizeof(ShenandoahSharedValue) * CHAR_MAX), "sanity"); return (Atomic::load_acquire(&value) & (ShenandoahSharedValue) mask) == 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp index 68b81bce417..fbf89644adf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2021, Red Hat, Inc. All rights reserved. * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -73,11 +74,11 @@ OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { assert(Thread::current()->is_Worker_thread(), "Unexpected thread passing in context: " PTR_FORMAT, p2i(context)); return reinterpret_cast(context); } else { - if (_heap->is_concurrent_mark_in_progress()) { - return &_keep_alive_cl; - } else if (_heap->is_concurrent_weak_root_in_progress()) { + if (_heap->is_concurrent_weak_root_in_progress()) { assert(_heap->is_evacuation_in_progress(), "Nothing to evacuate"); return &_evac_update_oop_cl; + } else if (_heap->is_concurrent_mark_in_progress()) { + return &_keep_alive_cl; } else { ShouldNotReachHere(); return nullptr; @@ -90,14 +91,7 @@ void ShenandoahStackWatermark::start_processing_impl(void* context) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); // Process the non-frame part of the thread - if (heap->is_concurrent_mark_in_progress()) { - // We need to reset all TLABs because they might be below the TAMS, and we need to mark - // the objects in them. Do not let mutators allocate any new objects in their current TLABs. - // It is also a good place to resize the TLAB sizes for future allocations. - retire_tlab(); - - _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); - } else if (heap->is_concurrent_weak_root_in_progress()) { + if (heap->is_concurrent_weak_root_in_progress()) { assert(heap->is_evacuation_in_progress(), "Should not be armed"); // Retire the TLABs, which will force threads to reacquire their TLABs. // This is needed for two reasons. Strong one: new allocations would be with new freeset, @@ -106,6 +100,13 @@ void ShenandoahStackWatermark::start_processing_impl(void* context) { // be needed for reference updates (would update the large filler instead). retire_tlab(); + _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); + } else if (heap->is_concurrent_mark_in_progress()) { + // We need to reset all TLABs because they might be below the TAMS, and we need to mark + // the objects in them. Do not let mutators allocate any new objects in their current TLABs. + // It is also a good place to resize the TLAB sizes for future allocations. + retire_tlab(); + _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); } else { ShouldNotReachHere(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp index b6c82e5af48..042143254bc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp @@ -25,9 +25,10 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_INLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_INLINE_HPP -#include "gc/shenandoah/shenandoahStringDedup.hpp" - #include "classfile/javaClasses.inline.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahStringDedup.hpp" +#include "oops/markWord.hpp" bool ShenandoahStringDedup::is_string_candidate(oop obj) { assert(Thread::current()->is_Worker_thread(), @@ -45,22 +46,10 @@ bool ShenandoahStringDedup::is_candidate(oop obj) { return false; } - const markWord mark = obj->mark(); - - // Having/had displaced header, too risky to deal with them, skip - if (mark == markWord::INFLATING() || mark.has_displaced_mark_helper()) { - return false; - } - - if (StringDedup::is_below_threshold_age(mark.age())) { - // Increase string age and enqueue it when it reaches age threshold - markWord new_mark = mark.incr_age(); - if (mark == obj->cas_set_mark(new_mark, mark)) { - return StringDedup::is_threshold_age(new_mark.age()) && - !dedup_requested(obj); - } - } - return false; + uint age = ShenandoahHeap::get_object_age(obj); + return (age <= markWord::max_age) && + StringDedup::is_below_threshold_age(age) && + !dedup_requested(obj); } #endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp new file mode 100644 index 00000000000..3901e0c7b7f --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, 2023, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "gc/shenandoah/shenandoahEvacTracker.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" + +ShenandoahThreadLocalData::ShenandoahThreadLocalData() : + _gc_state(0), + _oom_scope_nesting_level(0), + _oom_during_evac(false), + _satb_mark_queue(&ShenandoahBarrierSet::satb_mark_queue_set()), + _gclab(nullptr), + _gclab_size(0), + _paced_time(0), + _plab(nullptr), + _plab_desired_size(0), + _plab_actual_size(0), + _plab_promoted(0), + _plab_allows_promotion(true), + _plab_retries_enabled(true), + _evacuation_stats(nullptr) { + if (ShenandoahHeap::heap()->mode()->is_generational()) { + _evacuation_stats = new ShenandoahEvacuationStats(); + } +} + +ShenandoahThreadLocalData::~ShenandoahThreadLocalData() { + if (_gclab != nullptr) { + delete _gclab; + } + if (_plab != nullptr) { + ShenandoahGenerationalHeap::heap()->retire_plab(_plab); + delete _plab; + } + + delete _evacuation_stats; +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index 91f1cfe7f56..c226b1ad254 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -29,8 +30,12 @@ #include "gc/shared/gcThreadLocalData.hpp" #include "gc/shared/gc_globals.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" +#include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahEvacTracker.hpp" #include "gc/shenandoah/shenandoahSATBMarkQueueSet.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "runtime/javaThread.hpp" #include "utilities/debug.hpp" #include "utilities/sizes.hpp" @@ -41,26 +46,43 @@ class ShenandoahThreadLocalData { // Evacuation OOM state uint8_t _oom_scope_nesting_level; bool _oom_during_evac; + SATBMarkQueue _satb_mark_queue; + + // Thread-local allocation buffer for object evacuations. + // In generational mode, it is exclusive to the young generation. PLAB* _gclab; size_t _gclab_size; + double _paced_time; - ShenandoahThreadLocalData() : - _gc_state(0), - _oom_scope_nesting_level(0), - _oom_during_evac(false), - _satb_mark_queue(&ShenandoahBarrierSet::satb_mark_queue_set()), - _gclab(nullptr), - _gclab_size(0), - _paced_time(0) { - } + // Thread-local allocation buffer only used in generational mode. + // Used both by mutator threads and by GC worker threads + // for evacuations within the old generation and + // for promotions from the young generation into the old generation. + PLAB* _plab; - ~ShenandoahThreadLocalData() { - if (_gclab != nullptr) { - delete _gclab; - } - } + // Heuristics will grow the desired size of plabs. + size_t _plab_desired_size; + + // Once the plab has been allocated, and we know the actual size, we record it here. + size_t _plab_actual_size; + + // As the plab is used for promotions, this value is incremented. When the plab is + // retired, the difference between 'actual_size' and 'promoted' will be returned to + // the old generation's promotion reserve (i.e., it will be 'unexpended'). + size_t _plab_promoted; + + // If false, no more promotion by this thread during this evacuation phase. + bool _plab_allows_promotion; + + // If true, evacuations may attempt to allocate a smaller plab if the original size fails. + bool _plab_retries_enabled; + + ShenandoahEvacuationStats* _evacuation_stats; + + ShenandoahThreadLocalData(); + ~ShenandoahThreadLocalData(); static ShenandoahThreadLocalData* data(Thread* thread) { assert(UseShenandoahGC, "Sanity"); @@ -98,6 +120,11 @@ class ShenandoahThreadLocalData { assert(data(thread)->_gclab == nullptr, "Only initialize once"); data(thread)->_gclab = new PLAB(PLAB::min_size()); data(thread)->_gclab_size = 0; + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + data(thread)->_plab = new PLAB(align_up(PLAB::min_size(), CardTable::card_size_in_words())); + data(thread)->_plab_desired_size = 0; + } } static PLAB* gclab(Thread* thread) { @@ -112,6 +139,84 @@ class ShenandoahThreadLocalData { data(thread)->_gclab_size = v; } + static void begin_evacuation(Thread* thread, size_t bytes) { + data(thread)->_evacuation_stats->begin_evacuation(bytes); + } + + static void end_evacuation(Thread* thread, size_t bytes) { + data(thread)->_evacuation_stats->end_evacuation(bytes); + } + + static void record_age(Thread* thread, size_t bytes, uint age) { + data(thread)->_evacuation_stats->record_age(bytes, age); + } + + static ShenandoahEvacuationStats* evacuation_stats(Thread* thread) { + shenandoah_assert_generational(); + return data(thread)->_evacuation_stats; + } + + static PLAB* plab(Thread* thread) { + return data(thread)->_plab; + } + + static size_t plab_size(Thread* thread) { + return data(thread)->_plab_desired_size; + } + + static void set_plab_size(Thread* thread, size_t v) { + data(thread)->_plab_desired_size = v; + } + + static void enable_plab_retries(Thread* thread) { + data(thread)->_plab_retries_enabled = true; + } + + static void disable_plab_retries(Thread* thread) { + data(thread)->_plab_retries_enabled = false; + } + + static bool plab_retries_enabled(Thread* thread) { + return data(thread)->_plab_retries_enabled; + } + + static void enable_plab_promotions(Thread* thread) { + data(thread)->_plab_allows_promotion = true; + } + + static void disable_plab_promotions(Thread* thread) { + data(thread)->_plab_allows_promotion = false; + } + + static bool allow_plab_promotions(Thread* thread) { + return data(thread)->_plab_allows_promotion; + } + + static void reset_plab_promoted(Thread* thread) { + data(thread)->_plab_promoted = 0; + } + + static void add_to_plab_promoted(Thread* thread, size_t increment) { + data(thread)->_plab_promoted += increment; + } + + static void subtract_from_plab_promoted(Thread* thread, size_t increment) { + assert(data(thread)->_plab_promoted >= increment, "Cannot subtract more than remaining promoted"); + data(thread)->_plab_promoted -= increment; + } + + static size_t get_plab_promoted(Thread* thread) { + return data(thread)->_plab_promoted; + } + + static void set_plab_actual_size(Thread* thread, size_t value) { + data(thread)->_plab_actual_size = value; + } + + static size_t get_plab_actual_size(Thread* thread) { + return data(thread)->_plab_actual_size; + } + static void add_paced_time(Thread* thread, double v) { data(thread)->_paced_time += v; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp new file mode 100644 index 00000000000..0e6453c9b35 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com Inc. 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" +#include "gc/shenandoah/shenandoahEvacInfo.hpp" +#include "gc/shenandoah/shenandoahTrace.hpp" +#include "jfr/jfrEvents.hpp" + +void ShenandoahTracer::report_evacuation_info(ShenandoahEvacuationInformation* info) { + send_evacuation_info_event(info); +} + +void ShenandoahTracer::send_evacuation_info_event(ShenandoahEvacuationInformation* info) { + EventShenandoahEvacuationInformation e; + if (e.should_commit()) { + e.set_gcId(GCId::current()); + e.set_cSetRegions(info->collection_set_regions()); + e.set_cSetUsedBefore(info->collection_set_used_before()); + e.set_cSetUsedAfter(info->collection_set_used_after()); + e.set_collectedOld(info->collected_old()); + e.set_collectedPromoted(info->collected_promoted()); + e.set_collectedYoung(info->collected_young()); + e.set_regionsPromotedHumongous(info->regions_promoted_humongous()); + e.set_regionsPromotedRegular(info->regions_promoted_regular()); + e.set_regularPromotedGarbage(info->regular_promoted_garbage()); + e.set_regularPromotedFree(info->regular_promoted_free()); + e.set_regionsFreed(info->regions_freed()); + e.set_regionsImmediate(info->regions_immediate()); + e.set_immediateBytes(info->immediate_size()); + + e.commit(); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp new file mode 100644 index 00000000000..a5351f4ef28 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHTRACE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHTRACE_HPP + +#include "gc/shared/gcTrace.hpp" +#include "memory/allocation.hpp" + +class ShenandoahEvacuationInformation; + +class ShenandoahTracer : public GCTracer, public CHeapObj { +public: + ShenandoahTracer() : GCTracer(Shenandoah) {} + void report_evacuation_info(ShenandoahEvacuationInformation* info); + +private: + void send_evacuation_info_event(ShenandoahEvacuationInformation* info); +}; + +#endif diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index ca56b06e79d..518cb325efb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -32,25 +33,27 @@ #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "utilities/debug.hpp" ShenandoahPhaseTimings::Phase ShenandoahTimingsTracker::_current_phase = ShenandoahPhaseTimings::_invalid_phase; -ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause) : +ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation) : _heap(ShenandoahHeap::heap()), + _generation(generation), _timer(_heap->gc_timer()), _tracer(_heap->tracer()) { assert(!ShenandoahGCPhase::is_current_phase_valid(), "No current GC phase"); - _heap->shenandoah_policy()->record_collection_cause(cause); - _heap->set_gc_cause(cause); + _heap->on_cycle_start(cause, _generation); + _timer->register_gc_start(); _tracer->report_gc_start(cause, _timer->gc_start()); _heap->trace_heap_before_gc(_tracer); - _heap->heuristics()->record_cycle_start(); _trace_cycle.initialize(_heap->cycle_memory_manager(), cause, "end of GC cycle", /* allMemoryPoolsAffected */ true, @@ -65,13 +68,12 @@ ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause) : } ShenandoahGCSession::~ShenandoahGCSession() { - _heap->heuristics()->record_cycle_end(); + _heap->on_cycle_end(_generation); _timer->register_gc_end(); _heap->trace_heap_after_gc(_tracer); - _tracer->report_gc_reference_stats(_heap->ref_processor()->reference_process_stats()); + _tracer->report_gc_reference_stats(_generation->ref_processor()->reference_process_stats()); _tracer->report_gc_end(_timer->gc_end(), _timer->time_partitions()); assert(!ShenandoahGCPhase::is_current_phase_valid(), "No current GC phase"); - _heap->set_gc_cause(GCCause::_no_gc); } ShenandoahGCPauseMark::ShenandoahGCPauseMark(uint gc_id, const char* notification_message, SvcGCMarker::reason_type type) : diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index ffa9d764d3c..190822af9d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -41,16 +42,33 @@ #include "services/memoryService.hpp" class GCTimer; +class ShenandoahGeneration; + +#define SHENANDOAH_RETURN_EVENT_MESSAGE(generation_type, prefix, postfix) \ + switch (generation_type) { \ + case NON_GEN: \ + return prefix postfix; \ + case GLOBAL: \ + return prefix " (Global)" postfix; \ + case YOUNG: \ + return prefix " (Young)" postfix; \ + case OLD: \ + return prefix " (Old)" postfix; \ + default: \ + ShouldNotReachHere(); \ + return prefix " (Unknown)" postfix; \ + } \ class ShenandoahGCSession : public StackObj { private: ShenandoahHeap* const _heap; + ShenandoahGeneration* const _generation; GCTimer* const _timer; GCTracer* const _tracer; TraceMemoryManagerStats _trace_cycle; public: - ShenandoahGCSession(GCCause::Cause cause); + ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation); ~ShenandoahGCSession(); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp index 2dd6018ecd4..da32601eed7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp @@ -27,24 +27,43 @@ #include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahDegeneratedGC.hpp" #include "gc/shenandoah/shenandoahFullGC.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVMOperations.hpp" #include "interpreter/oopMapCache.hpp" +#include "logging/log.hpp" #include "memory/universe.hpp" bool VM_ShenandoahOperation::doit_prologue() { + log_active_generation("Prologue"); assert(!ShenandoahHeap::heap()->has_gc_state_changed(), "GC State can only be changed on a safepoint."); return true; } void VM_ShenandoahOperation::doit_epilogue() { + log_active_generation("Epilogue"); assert(!ShenandoahHeap::heap()->has_gc_state_changed(), "GC State was not synchronized to java threads."); // GC thread root traversal likely used OopMapCache a lot, which // might have created lots of old entries. Trigger the cleanup now. OopMapCache::try_trigger_cleanup(); } +void VM_ShenandoahOperation::log_active_generation(const char* prefix) { + ShenandoahGeneration* agen = ShenandoahHeap::heap()->active_generation(); + ShenandoahGeneration* ggen = ShenandoahHeap::heap()->gc_generation(); + log_debug(gc, heap)("%s: active_generation is %s, gc_generation is %s", prefix, + agen == nullptr ? "nullptr" : shenandoah_generation_name(agen->type()), + ggen == nullptr ? "nullptr" : shenandoah_generation_name(ggen->type())); +} + +void VM_ShenandoahOperation::set_active_generation() { + if (evaluate_at_safepoint()) { + assert(SafepointSynchronize::is_at_safepoint(), "Error??"); + ShenandoahHeap::heap()->set_active_generation(); + } +} + bool VM_ShenandoahReferenceOperation::doit_prologue() { VM_ShenandoahOperation::doit_prologue(); Heap_lock->lock(); @@ -61,42 +80,49 @@ void VM_ShenandoahReferenceOperation::doit_epilogue() { void VM_ShenandoahInitMark::doit() { ShenandoahGCPauseMark mark(_gc_id, "Init Mark", SvcGCMarker::CONCURRENT); + set_active_generation(); _gc->entry_init_mark(); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } void VM_ShenandoahFinalMarkStartEvac::doit() { ShenandoahGCPauseMark mark(_gc_id, "Final Mark", SvcGCMarker::CONCURRENT); + set_active_generation(); _gc->entry_final_mark(); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } void VM_ShenandoahFullGC::doit() { ShenandoahGCPauseMark mark(_gc_id, "Full GC", SvcGCMarker::FULL); + set_active_generation(); _full_gc->entry_full(_gc_cause); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } void VM_ShenandoahDegeneratedGC::doit() { ShenandoahGCPauseMark mark(_gc_id, "Degenerated GC", SvcGCMarker::CONCURRENT); + set_active_generation(); _gc->entry_degenerated(); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } void VM_ShenandoahInitUpdateRefs::doit() { ShenandoahGCPauseMark mark(_gc_id, "Init Update Refs", SvcGCMarker::CONCURRENT); + set_active_generation(); _gc->entry_init_updaterefs(); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } void VM_ShenandoahFinalUpdateRefs::doit() { ShenandoahGCPauseMark mark(_gc_id, "Final Update Refs", SvcGCMarker::CONCURRENT); + set_active_generation(); _gc->entry_final_updaterefs(); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } void VM_ShenandoahFinalRoots::doit() { ShenandoahGCPauseMark mark(_gc_id, "Final Roots", SvcGCMarker::CONCURRENT); + set_active_generation(); _gc->entry_final_roots(); ShenandoahHeap::heap()->propagate_gc_state_to_java_threads(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp index 1b78766935f..09971bb2630 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp @@ -38,16 +38,21 @@ class ShenandoahFullGC; // - VM_ShenandoahFinalMarkStartEvac: finish up concurrent marking, and start evacuation // - VM_ShenandoahInitUpdateRefs: initiate update references // - VM_ShenandoahFinalUpdateRefs: finish up update references +// - VM_ShenandoahFinalRoots: finish up roots on a non-evacuating cycle // - VM_ShenandoahReferenceOperation: // - VM_ShenandoahFullGC: do full GC // - VM_ShenandoahDegeneratedGC: do STW degenerated GC class VM_ShenandoahOperation : public VM_Operation { protected: - uint _gc_id; + uint _gc_id; + + void set_active_generation(); public: VM_ShenandoahOperation() : _gc_id(GCId::current()) {}; bool skip_thread_oop_barriers() const override { return true; } + + void log_active_generation(const char* prefix); bool doit_prologue() override; void doit_epilogue() override; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index b653a06b877..bde8638140b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -27,12 +28,16 @@ #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahForwarding.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahRootProcessor.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "memory/allocation.hpp" #include "memory/iterator.inline.hpp" #include "memory/resourceArea.hpp" @@ -62,6 +67,7 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { void* _interior_loc; oop _loc; ReferenceIterationMode _ref_mode; + ShenandoahGeneration* _generation; public: ShenandoahVerifyOopClosure(ShenandoahVerifierStack* stack, MarkBitMap* map, ShenandoahLivenessData* ld, @@ -73,8 +79,10 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { _map(map), _ld(ld), _interior_loc(nullptr), - _loc(nullptr) { + _loc(nullptr), + _generation(nullptr) { if (options._verify_marked == ShenandoahVerifier::_verify_marked_complete_except_references || + options._verify_marked == ShenandoahVerifier::_verify_marked_complete_satb_empty || options._verify_marked == ShenandoahVerifier::_verify_marked_disable) { // Unknown status for Reference.referent field. Do not touch it, it might be dead. // Normally, barriers would prevent us from seeing the dead referents, but verifier @@ -84,6 +92,12 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { // Otherwise do all fields. _ref_mode = DO_FIELDS; } + + if (_heap->mode()->is_generational()) { + _generation = _heap->gc_generation(); + assert(_generation != nullptr, "Expected active generation in this mode"); + shenandoah_assert_generations_reconciled(); + } } ReferenceIterationMode reference_iteration_mode() override { @@ -110,14 +124,22 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { // // For performance reasons, only fully verify non-marked field values. // We are here when the host object for *p is already marked. - - if (_map->par_mark(obj)) { + if (in_generation(obj) && _map->par_mark(obj)) { verify_oop_at(p, obj); _stack->push(ShenandoahVerifierTask(obj)); } } } + bool in_generation(oop obj) { + if (_generation == nullptr) { + return true; + } + + ShenandoahHeapRegion* region = _heap->heap_region_containing(obj); + return _generation->contains(region); + } + void verify_oop(oop obj) { // Perform consistency checks with gradually decreasing safety level. This guarantees // that failure report would not try to touch something that was not yet verified to be @@ -168,8 +190,10 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { Atomic::add(&_ld[obj_reg->index()], (uint) ShenandoahForwarding::size(obj), memory_order_relaxed); // fallthrough for fast failure for un-live regions: case ShenandoahVerifier::_verify_liveness_conservative: - check(ShenandoahAsserts::_safe_oop, obj, obj_reg->has_live(), + check(ShenandoahAsserts::_safe_oop, obj, obj_reg->has_live() || + (obj_reg->is_old() && _heap->gc_generation()->is_young()), "Object must belong to region with live data"); + shenandoah_assert_generations_reconciled(); break; default: assert(false, "Unhandled liveness verification"); @@ -235,7 +259,6 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { } // ------------ obj and fwd are safe at this point -------------- - switch (_options._verify_marked) { case ShenandoahVerifier::_verify_marked_disable: // skip @@ -249,6 +272,7 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { "Must be marked in complete bitmap"); break; case ShenandoahVerifier::_verify_marked_complete_except_references: + case ShenandoahVerifier::_verify_marked_complete_satb_empty: check(ShenandoahAsserts::_safe_all, obj, _heap->complete_marking_context()->is_marked(obj), "Must be marked in complete bitmap, except j.l.r.Reference referents"); break; @@ -335,25 +359,105 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { _loc = nullptr; } - virtual void do_oop(oop* p) override { do_oop_work(p); } - virtual void do_oop(narrowOop* p) override { do_oop_work(p); } + void do_oop(oop* p) override { do_oop_work(p); } + void do_oop(narrowOop* p) override { do_oop_work(p); } }; +// This closure computes the amounts of used, committed, and garbage memory and the number of regions contained within +// a subset (e.g. the young generation or old generation) of the total heap. class ShenandoahCalculateRegionStatsClosure : public ShenandoahHeapRegionClosure { private: - size_t _used, _committed, _garbage; + size_t _used, _committed, _garbage, _regions, _humongous_waste, _trashed_regions; public: - ShenandoahCalculateRegionStatsClosure() : _used(0), _committed(0), _garbage(0) {}; + ShenandoahCalculateRegionStatsClosure() : + _used(0), _committed(0), _garbage(0), _regions(0), _humongous_waste(0), _trashed_regions(0) {}; - void heap_region_do(ShenandoahHeapRegion* r) { + void heap_region_do(ShenandoahHeapRegion* r) override { _used += r->used(); _garbage += r->garbage(); _committed += r->is_committed() ? ShenandoahHeapRegion::region_size_bytes() : 0; + if (r->is_humongous()) { + _humongous_waste += r->free(); + } + if (r->is_trash()) { + _trashed_regions++; + } + _regions++; + log_debug(gc)("ShenandoahCalculateRegionStatsClosure: adding " SIZE_FORMAT " for %s Region " SIZE_FORMAT ", yielding: " SIZE_FORMAT, + r->used(), (r->is_humongous() ? "humongous" : "regular"), r->index(), _used); + } + + size_t used() const { return _used; } + size_t committed() const { return _committed; } + size_t garbage() const { return _garbage; } + size_t regions() const { return _regions; } + size_t waste() const { return _humongous_waste; } + + // span is the total memory affiliated with these stats (some of which is in use and other is available) + size_t span() const { return _regions * ShenandoahHeapRegion::region_size_bytes(); } + size_t non_trashed_span() const { return (_regions - _trashed_regions) * ShenandoahHeapRegion::region_size_bytes(); } +}; + +class ShenandoahGenerationStatsClosure : public ShenandoahHeapRegionClosure { + public: + ShenandoahCalculateRegionStatsClosure old; + ShenandoahCalculateRegionStatsClosure young; + ShenandoahCalculateRegionStatsClosure global; + + void heap_region_do(ShenandoahHeapRegion* r) override { + switch (r->affiliation()) { + case FREE: + return; + case YOUNG_GENERATION: + young.heap_region_do(r); + global.heap_region_do(r); + break; + case OLD_GENERATION: + old.heap_region_do(r); + global.heap_region_do(r); + break; + default: + ShouldNotReachHere(); + } } - size_t used() { return _used; } - size_t committed() { return _committed; } - size_t garbage() { return _garbage; } + static void log_usage(ShenandoahGeneration* generation, ShenandoahCalculateRegionStatsClosure& stats) { + log_debug(gc)("Safepoint verification: %s verified usage: " SIZE_FORMAT "%s, recorded usage: " SIZE_FORMAT "%s", + generation->name(), + byte_size_in_proper_unit(generation->used()), proper_unit_for_byte_size(generation->used()), + byte_size_in_proper_unit(stats.used()), proper_unit_for_byte_size(stats.used())); + } + + static void validate_usage(const bool adjust_for_padding, + const char* label, ShenandoahGeneration* generation, ShenandoahCalculateRegionStatsClosure& stats) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + size_t generation_used = generation->used(); + size_t generation_used_regions = generation->used_regions(); + if (adjust_for_padding && (generation->is_young() || generation->is_global())) { + size_t pad = heap->old_generation()->get_pad_for_promote_in_place(); + generation_used += pad; + } + + guarantee(stats.used() == generation_used, + "%s: generation (%s) used size must be consistent: generation-used: " PROPERFMT ", regions-used: " PROPERFMT, + label, generation->name(), PROPERFMTARGS(generation_used), PROPERFMTARGS(stats.used())); + + guarantee(stats.regions() == generation_used_regions, + "%s: generation (%s) used regions (" SIZE_FORMAT ") must equal regions that are in use (" SIZE_FORMAT ")", + label, generation->name(), generation->used_regions(), stats.regions()); + + size_t generation_capacity = generation->max_capacity(); + guarantee(stats.non_trashed_span() <= generation_capacity, + "%s: generation (%s) size spanned by regions (" SIZE_FORMAT ") * region size (" PROPERFMT + ") must not exceed current capacity (" PROPERFMT ")", + label, generation->name(), stats.regions(), PROPERFMTARGS(ShenandoahHeapRegion::region_size_bytes()), + PROPERFMTARGS(generation_capacity)); + + size_t humongous_waste = generation->get_humongous_waste(); + guarantee(stats.waste() == humongous_waste, + "%s: generation (%s) humongous waste must be consistent: generation: " PROPERFMT ", regions: " PROPERFMT, + label, generation->name(), PROPERFMTARGS(humongous_waste), PROPERFMTARGS(stats.waste())); + } }; class ShenandoahVerifyHeapRegionClosure : public ShenandoahHeapRegionClosure { @@ -385,7 +489,7 @@ class ShenandoahVerifyHeapRegionClosure : public ShenandoahHeapRegionClosure { } } - void heap_region_do(ShenandoahHeapRegion* r) { + void heap_region_do(ShenandoahHeapRegion* r) override { switch (_regions) { case ShenandoahVerifier::_verify_regions_disable: break; @@ -437,8 +541,11 @@ class ShenandoahVerifyHeapRegionClosure : public ShenandoahHeapRegionClosure { verify(r, r->get_gclab_allocs() <= r->capacity(), "GCLAB alloc count should not be larger than capacity"); - verify(r, r->get_shared_allocs() + r->get_tlab_allocs() + r->get_gclab_allocs() == r->used(), - "Accurate accounting: shared + TLAB + GCLAB = used"); + verify(r, r->get_plab_allocs() <= r->capacity(), + "PLAB alloc count should not be larger than capacity"); + + verify(r, r->get_shared_allocs() + r->get_tlab_allocs() + r->get_gclab_allocs() + r->get_plab_allocs() == r->used(), + "Accurate accounting: shared + TLAB + GCLAB + PLAB = used"); verify(r, !r->is_empty() || !r->has_live(), "Empty regions should not have live data"); @@ -470,11 +577,11 @@ class ShenandoahVerifierReachableTask : public WorkerTask { _bitmap(bitmap), _processed(0) {}; - size_t processed() { + size_t processed() const { return _processed; } - virtual void work(uint worker_id) { + void work(uint worker_id) override { ResourceMark rm; ShenandoahVerifierStack stack; @@ -511,6 +618,16 @@ class ShenandoahVerifierReachableTask : public WorkerTask { } }; +class ShenandoahVerifyNoIncompleteSatbBuffers : public ThreadClosure { +public: + void do_thread(Thread* thread) override { + SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); + if (!queue.is_empty()) { + fatal("All SATB buffers should have been flushed during mark"); + } + } +}; + class ShenandoahVerifierMarkedRegionTask : public WorkerTask { private: const char* _label; @@ -520,6 +637,7 @@ class ShenandoahVerifierMarkedRegionTask : public WorkerTask { ShenandoahLivenessData* _ld; volatile size_t _claimed; volatile size_t _processed; + ShenandoahGeneration* _generation; public: ShenandoahVerifierMarkedRegionTask(MarkBitMap* bitmap, @@ -533,13 +651,25 @@ class ShenandoahVerifierMarkedRegionTask : public WorkerTask { _bitmap(bitmap), _ld(ld), _claimed(0), - _processed(0) {}; + _processed(0), + _generation(nullptr) { + if (_heap->mode()->is_generational()) { + _generation = _heap->gc_generation(); + assert(_generation != nullptr, "Expected active generation in this mode."); + shenandoah_assert_generations_reconciled(); + } + }; size_t processed() { return Atomic::load(&_processed); } - virtual void work(uint worker_id) { + void work(uint worker_id) override { + if (_options._verify_marked == ShenandoahVerifier::_verify_marked_complete_satb_empty) { + ShenandoahVerifyNoIncompleteSatbBuffers verify_satb; + Threads::threads_do(&verify_satb); + } + ShenandoahVerifierStack stack; ShenandoahVerifyOopClosure cl(&stack, _bitmap, _ld, ShenandoahMessageBuffer("%s, Marked", _label), @@ -549,6 +679,10 @@ class ShenandoahVerifierMarkedRegionTask : public WorkerTask { size_t v = Atomic::fetch_then_add(&_claimed, 1u, memory_order_relaxed); if (v < _heap->num_regions()) { ShenandoahHeapRegion* r = _heap->get_region(v); + if (!in_generation(r)) { + continue; + } + if (!r->is_humongous() && !r->is_trash()) { work_regular(r, stack, cl); } else if (r->is_humongous_start()) { @@ -560,6 +694,10 @@ class ShenandoahVerifierMarkedRegionTask : public WorkerTask { } } + bool in_generation(ShenandoahHeapRegion* r) { + return _generation == nullptr || _generation->contains(r); + } + virtual void work_humongous(ShenandoahHeapRegion *r, ShenandoahVerifierStack& stack, ShenandoahVerifyOopClosure& cl) { size_t processed = 0; HeapWord* obj = r->bottom(); @@ -630,18 +768,30 @@ class VerifyThreadGCState : public ThreadClosure { public: VerifyThreadGCState(const char* label, char expected) : _label(label), _expected(expected) {} - void do_thread(Thread* t) { + void do_thread(Thread* t) override { char actual = ShenandoahThreadLocalData::gc_state(t); - if (actual != _expected) { + if (!verify_gc_state(actual, _expected)) { fatal("%s: Thread %s: expected gc-state %d, actual %d", _label, t->name(), _expected, actual); } } + + static bool verify_gc_state(char actual, char expected) { + // Old generation marking is allowed in all states. + if (ShenandoahHeap::heap()->mode()->is_generational()) { + return ((actual & ~(ShenandoahHeap::OLD_MARKING | ShenandoahHeap::MARKING)) == expected); + } else { + assert((actual & ShenandoahHeap::OLD_MARKING) == 0, "Should not mark old in non-generational mode"); + return (actual == expected); + } + } }; -void ShenandoahVerifier::verify_at_safepoint(const char *label, +void ShenandoahVerifier::verify_at_safepoint(const char* label, + VerifyRememberedSet remembered, VerifyForwarded forwarded, VerifyMarked marked, VerifyCollectionSet cset, VerifyLiveness liveness, VerifyRegions regions, + VerifySize sizeness, VerifyGCState gcstate) { guarantee(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "only when nothing else happens"); guarantee(ShenandoahVerify, "only when enabled, and bitmap is initialized in ShenandoahHeap::initialize"); @@ -665,6 +815,10 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, enabled = true; expected = ShenandoahHeap::HAS_FORWARDED; break; + case _verify_gcstate_updating: + enabled = true; + expected = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::UPDATEREFS; + break; case _verify_gcstate_stable: enabled = true; expected = ShenandoahHeap::STABLE; @@ -684,7 +838,13 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, if (enabled) { char actual = _heap->gc_state(); - if (actual != expected) { + + bool is_marking = (actual & ShenandoahHeap::MARKING); + bool is_marking_young_or_old = (actual & (ShenandoahHeap::YOUNG_MARKING | ShenandoahHeap::OLD_MARKING)); + assert(is_marking == is_marking_young_or_old, "MARKING iff (YOUNG_MARKING or OLD_MARKING), gc_state is: %x", actual); + + // Old generation marking is allowed in all states. + if (!VerifyThreadGCState::verify_gc_state(actual, expected)) { fatal("%s: Global gc-state: expected %d, actual %d", label, expected, actual); } @@ -702,13 +862,20 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, ShenandoahCalculateRegionStatsClosure cl; _heap->heap_region_iterate(&cl); - size_t heap_used = _heap->used(); - guarantee(cl.used() == heap_used, - "%s: heap used size must be consistent: heap-used = " SIZE_FORMAT "%s, regions-used = " SIZE_FORMAT "%s", - label, - byte_size_in_proper_unit(heap_used), proper_unit_for_byte_size(heap_used), - byte_size_in_proper_unit(cl.used()), proper_unit_for_byte_size(cl.used())); - + size_t heap_used; + if (_heap->mode()->is_generational() && (sizeness == _verify_size_adjusted_for_padding)) { + // Prior to evacuation, regular regions that are to be evacuated in place are padded to prevent further allocations + heap_used = _heap->used() + _heap->old_generation()->get_pad_for_promote_in_place(); + } else if (sizeness != _verify_size_disable) { + heap_used = _heap->used(); + } + if (sizeness != _verify_size_disable) { + guarantee(cl.used() == heap_used, + "%s: heap used size must be consistent: heap-used = " SIZE_FORMAT "%s, regions-used = " SIZE_FORMAT "%s", + label, + byte_size_in_proper_unit(heap_used), proper_unit_for_byte_size(heap_used), + byte_size_in_proper_unit(cl.used()), proper_unit_for_byte_size(cl.used())); + } size_t heap_committed = _heap->committed(); guarantee(cl.committed() == heap_committed, "%s: heap committed size must be consistent: heap-committed = " SIZE_FORMAT "%s, regions-committed = " SIZE_FORMAT "%s", @@ -717,12 +884,73 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, byte_size_in_proper_unit(cl.committed()), proper_unit_for_byte_size(cl.committed())); } + log_debug(gc)("Safepoint verification finished heap usage verification"); + + ShenandoahGeneration* generation; + if (_heap->mode()->is_generational()) { + generation = _heap->gc_generation(); + guarantee(generation != nullptr, "Need to know which generation to verify."); + shenandoah_assert_generations_reconciled(); + } else { + generation = nullptr; + } + + if (generation != nullptr) { + ShenandoahHeapLocker lock(_heap->lock()); + + switch (remembered) { + case _verify_remembered_disable: + break; + case _verify_remembered_before_marking: + log_debug(gc)("Safepoint verification of remembered set at mark"); + verify_rem_set_before_mark(); + break; + case _verify_remembered_before_updating_references: + log_debug(gc)("Safepoint verification of remembered set at update ref"); + verify_rem_set_before_update_ref(); + break; + case _verify_remembered_after_full_gc: + log_debug(gc)("Safepoint verification of remembered set after full gc"); + verify_rem_set_after_full_gc(); + break; + default: + fatal("Unhandled remembered set verification mode"); + } + + ShenandoahGenerationStatsClosure cl; + _heap->heap_region_iterate(&cl); + + if (LogTarget(Debug, gc)::is_enabled()) { + ShenandoahGenerationStatsClosure::log_usage(_heap->old_generation(), cl.old); + ShenandoahGenerationStatsClosure::log_usage(_heap->young_generation(), cl.young); + ShenandoahGenerationStatsClosure::log_usage(_heap->global_generation(), cl.global); + } + if (sizeness == _verify_size_adjusted_for_padding) { + ShenandoahGenerationStatsClosure::validate_usage(false, label, _heap->old_generation(), cl.old); + ShenandoahGenerationStatsClosure::validate_usage(true, label, _heap->young_generation(), cl.young); + ShenandoahGenerationStatsClosure::validate_usage(true, label, _heap->global_generation(), cl.global); + } else if (sizeness == _verify_size_exact) { + ShenandoahGenerationStatsClosure::validate_usage(false, label, _heap->old_generation(), cl.old); + ShenandoahGenerationStatsClosure::validate_usage(false, label, _heap->young_generation(), cl.young); + ShenandoahGenerationStatsClosure::validate_usage(false, label, _heap->global_generation(), cl.global); + } + // else: sizeness must equal _verify_size_disable + } + + log_debug(gc)("Safepoint verification finished remembered set verification"); + // Internal heap region checks if (ShenandoahVerifyLevel >= 1) { ShenandoahVerifyHeapRegionClosure cl(label, regions); - _heap->heap_region_iterate(&cl); + if (generation != nullptr) { + generation->heap_region_iterate(&cl); + } else { + _heap->heap_region_iterate(&cl); + } } + log_debug(gc)("Safepoint verification finished heap region closure verification"); + OrderAccess::fence(); if (UseTLAB) { @@ -747,6 +975,8 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, count_reachable = task.processed(); } + log_debug(gc)("Safepoint verification finished getting initial reachable set"); + // Step 3. Walk marked objects. Marked objects might be unreachable. This verifies what collector, // not the application, can see during the region scans. There is no reason to process the objects // that were already verified, e.g. those marked in verification bitmap. There is interaction with TAMS: @@ -755,7 +985,10 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, // version size_t count_marked = 0; - if (ShenandoahVerifyLevel >= 4 && (marked == _verify_marked_complete || marked == _verify_marked_complete_except_references)) { + if (ShenandoahVerifyLevel >= 4 && + (marked == _verify_marked_complete || + marked == _verify_marked_complete_except_references || + marked == _verify_marked_complete_satb_empty)) { guarantee(_heap->marking_context()->is_complete(), "Marking context should be complete"); ShenandoahVerifierMarkedRegionTask task(_verification_bit_map, ld, label, options); _heap->workers()->run_task(&task); @@ -764,12 +997,17 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, guarantee(ShenandoahVerifyLevel < 4 || marked == _verify_marked_incomplete || marked == _verify_marked_disable, "Should be"); } + log_debug(gc)("Safepoint verification finished walking marked objects"); + // Step 4. Verify accumulated liveness data, if needed. Only reliable if verification level includes // marked objects. if (ShenandoahVerifyLevel >= 4 && marked == _verify_marked_complete && liveness == _verify_liveness_complete) { for (size_t i = 0; i < _heap->num_regions(); i++) { ShenandoahHeapRegion* r = _heap->get_region(i); + if (generation != nullptr && !generation->contains(r)) { + continue; + } juint verf_live = 0; if (r->is_humongous()) { @@ -793,6 +1031,9 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, } } + log_debug(gc)("Safepoint verification finished accumulation of liveness data"); + + log_info(gc)("Verify %s, Level " INTX_FORMAT " (" SIZE_FORMAT " reachable, " SIZE_FORMAT " marked)", label, ShenandoahVerifyLevel, count_reachable, count_marked); @@ -802,11 +1043,13 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, void ShenandoahVerifier::verify_generic(VerifyOption vo) { verify_at_safepoint( "Generic Verification", + _verify_remembered_disable, // do not verify remembered set _verify_forwarded_allow, // conservatively allow forwarded _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations _verify_cset_disable, // cset may be inconsistent _verify_liveness_disable, // no reliable liveness data _verify_regions_disable, // no reliable region data + _verify_size_exact, // expect generation and heap sizes to match exactly _verify_gcstate_disable // no data about gcstate ); } @@ -814,11 +1057,14 @@ void ShenandoahVerifier::verify_generic(VerifyOption vo) { void ShenandoahVerifier::verify_before_concmark() { verify_at_safepoint( "Before Mark", + _verify_remembered_before_marking, + // verify read-only remembered set from bottom() to top() _verify_forwarded_none, // UR should have fixed up _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations _verify_cset_none, // UR should have fixed this _verify_liveness_disable, // no reliable liveness data _verify_regions_notrash, // no trash regions + _verify_size_exact, // expect generation and heap sizes to match exactly _verify_gcstate_stable // there are no forwarded objects ); } @@ -826,23 +1072,43 @@ void ShenandoahVerifier::verify_before_concmark() { void ShenandoahVerifier::verify_after_concmark() { verify_at_safepoint( "After Mark", - _verify_forwarded_none, // no forwarded references - _verify_marked_complete_except_references, // bitmaps as precise as we can get, except dangling j.l.r.Refs - _verify_cset_none, // no references to cset anymore - _verify_liveness_complete, // liveness data must be complete here - _verify_regions_disable, // trash regions not yet recycled - _verify_gcstate_stable_weakroots // heap is still stable, weakroots are in progress + _verify_remembered_disable, // do not verify remembered set + _verify_forwarded_none, // no forwarded references + _verify_marked_complete_satb_empty, // bitmaps as precise as we can get, except dangling j.l.r.Refs + _verify_cset_none, // no references to cset anymore + _verify_liveness_complete, // liveness data must be complete here + _verify_regions_disable, // trash regions not yet recycled + _verify_size_exact, // expect generation and heap sizes to match exactly + _verify_gcstate_stable_weakroots // heap is still stable, weakroots are in progress + ); +} + +void ShenandoahVerifier::verify_after_concmark_with_promotions() { + verify_at_safepoint( + "After Mark", + _verify_remembered_disable, // do not verify remembered set + _verify_forwarded_none, // no forwarded references + _verify_marked_complete_satb_empty, // bitmaps as precise as we can get, except dangling j.l.r.Refs + _verify_cset_none, // no references to cset anymore + _verify_liveness_complete, // liveness data must be complete here + _verify_regions_disable, // trash regions not yet recycled + _verify_size_adjusted_for_padding, // expect generation and heap sizes to match after adjustments + // for promote in place padding + _verify_gcstate_stable_weakroots // heap is still stable, weakroots are in progress ); } void ShenandoahVerifier::verify_before_evacuation() { verify_at_safepoint( "Before Evacuation", + _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // no forwarded references _verify_marked_complete_except_references, // walk over marked objects too _verify_cset_disable, // non-forwarded references to cset expected _verify_liveness_complete, // liveness data must be complete here _verify_regions_disable, // trash regions not yet recycled + _verify_size_adjusted_for_padding, // expect generation and heap sizes to match after adjustments + // for promote in place padding _verify_gcstate_stable_weakroots // heap is still stable, weakroots are in progress ); } @@ -850,23 +1116,28 @@ void ShenandoahVerifier::verify_before_evacuation() { void ShenandoahVerifier::verify_before_updaterefs() { verify_at_safepoint( "Before Updating References", + _verify_remembered_before_updating_references, // verify read-write remembered set _verify_forwarded_allow, // forwarded references allowed _verify_marked_complete, // bitmaps might be stale, but alloc-after-mark should be well _verify_cset_forwarded, // all cset refs are fully forwarded _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash, // trash regions have been recycled already - _verify_gcstate_forwarded // evacuation should have produced some forwarded objects + _verify_size_exact, // expect generation and heap sizes to match exactly + _verify_gcstate_updating // evacuation should have produced some forwarded objects ); } +// We have not yet cleanup (reclaimed) the collection set void ShenandoahVerifier::verify_after_updaterefs() { verify_at_safepoint( "After Updating References", + _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // no forwarded references _verify_marked_complete, // bitmaps might be stale, but alloc-after-mark should be well _verify_cset_none, // no cset references, all updated _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_nocset, // no cset regions, trash regions have appeared + _verify_size_exact, // expect generation and heap sizes to match exactly _verify_gcstate_stable // update refs had cleaned up forwarded objects ); } @@ -874,11 +1145,13 @@ void ShenandoahVerifier::verify_after_updaterefs() { void ShenandoahVerifier::verify_after_degenerated() { verify_at_safepoint( "After Degenerated GC", + _verify_remembered_disable, // do not verify remembered set _verify_forwarded_none, // all objects are non-forwarded _verify_marked_complete, // all objects are marked in complete bitmap _verify_cset_none, // no cset references _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash_nocset, // no trash, no cset + _verify_size_exact, // expect generation and heap sizes to match exactly _verify_gcstate_stable // degenerated refs had cleaned up forwarded objects ); } @@ -886,11 +1159,13 @@ void ShenandoahVerifier::verify_after_degenerated() { void ShenandoahVerifier::verify_before_fullgc() { verify_at_safepoint( "Before Full GC", + _verify_remembered_disable, // do not verify remembered set _verify_forwarded_allow, // can have forwarded objects _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations _verify_cset_disable, // cset might be foobared _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_disable, // no reliable region data here + _verify_size_disable, // if we degenerate during evacuation, usage not valid: padding and deferred accounting _verify_gcstate_disable // no reliable gcstate data ); } @@ -898,16 +1173,18 @@ void ShenandoahVerifier::verify_before_fullgc() { void ShenandoahVerifier::verify_after_fullgc() { verify_at_safepoint( "After Full GC", + _verify_remembered_after_full_gc, // verify read-write remembered set _verify_forwarded_none, // all objects are non-forwarded _verify_marked_complete, // all objects are marked in complete bitmap _verify_cset_none, // no cset references _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash_nocset, // no trash, no cset + _verify_size_exact, // expect generation and heap sizes to match exactly _verify_gcstate_stable // full gc cleaned up everything ); } -class ShenandoahVerifyNoForwared : public OopClosure { +class ShenandoahVerifyNoForwarded : public BasicOopIterateClosure { private: template void do_oop_work(T* p) { @@ -927,7 +1204,7 @@ class ShenandoahVerifyNoForwared : public OopClosure { void do_oop(oop* p) { do_oop_work(p); } }; -class ShenandoahVerifyInToSpaceClosure : public OopClosure { +class ShenandoahVerifyInToSpaceClosure : public BasicOopIterateClosure { private: template void do_oop_work(T* p) { @@ -936,7 +1213,7 @@ class ShenandoahVerifyInToSpaceClosure : public OopClosure { oop obj = CompressedOops::decode_not_null(o); ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (!heap->marking_context()->is_marked(obj)) { + if (!heap->marking_context()->is_marked_or_old(obj)) { ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr, "Verify Roots In To-Space", "Should be marked", __FILE__, __LINE__); } @@ -955,8 +1232,8 @@ class ShenandoahVerifyInToSpaceClosure : public OopClosure { } public: - void do_oop(narrowOop* p) { do_oop_work(p); } - void do_oop(oop* p) { do_oop_work(p); } + void do_oop(narrowOop* p) override { do_oop_work(p); } + void do_oop(oop* p) override { do_oop_work(p); } }; void ShenandoahVerifier::verify_roots_in_to_space() { @@ -965,6 +1242,171 @@ void ShenandoahVerifier::verify_roots_in_to_space() { } void ShenandoahVerifier::verify_roots_no_forwarded() { - ShenandoahVerifyNoForwared cl; + ShenandoahVerifyNoForwarded cl; ShenandoahRootVerifier::roots_do(&cl); } + +template +class ShenandoahVerifyRemSetClosure : public BasicOopIterateClosure { +protected: + ShenandoahGenerationalHeap* const _heap; + Scanner* const _scanner; + const char* _message; + +public: + // Argument distinguishes between initial mark or start of update refs verification. + explicit ShenandoahVerifyRemSetClosure(Scanner* scanner, const char* message) : + _heap(ShenandoahGenerationalHeap::heap()), + _scanner(scanner), + _message(message) {} + + template + inline void work(T* p) { + T o = RawAccess<>::oop_load(p); + if (!CompressedOops::is_null(o)) { + oop obj = CompressedOops::decode_not_null(o); + if (_heap->is_in_young(obj) && !_scanner->is_card_dirty((HeapWord*) p)) { + ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr, + _message, "clean card should be dirty", __FILE__, __LINE__); + } + } + } + + void do_oop(narrowOop* p) override { work(p); } + void do_oop(oop* p) override { work(p); } +}; + +ShenandoahMarkingContext* ShenandoahVerifier::get_marking_context_for_old() { + shenandoah_assert_generations_reconciled(); + if (_heap->old_generation()->is_mark_complete() || _heap->gc_generation()->is_global()) { + return _heap->complete_marking_context(); + } + return nullptr; +} + +template +void ShenandoahVerifier::help_verify_region_rem_set(Scanner* scanner, ShenandoahHeapRegion* r, ShenandoahMarkingContext* ctx, + HeapWord* registration_watermark, const char* message) { + ShenandoahVerifyRemSetClosure check_interesting_pointers(scanner, message); + HeapWord* from = r->bottom(); + HeapWord* obj_addr = from; + if (r->is_humongous_start()) { + oop obj = cast_to_oop(obj_addr); + if ((ctx == nullptr) || ctx->is_marked(obj)) { + // For humongous objects, the typical object is an array, so the following checks may be overkill + // For regular objects (not object arrays), if the card holding the start of the object is dirty, + // we do not need to verify that cards spanning interesting pointers within this object are dirty. + if (!scanner->is_card_dirty(obj_addr) || obj->is_objArray()) { + obj->oop_iterate(&check_interesting_pointers); + } + // else, object's start is marked dirty and obj is not an objArray, so any interesting pointers are covered + } + // else, this humongous object is not live so no need to verify its internal pointers + + if ((obj_addr < registration_watermark) && !scanner->verify_registration(obj_addr, ctx)) { + ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, obj_addr, nullptr, message, + "object not properly registered", __FILE__, __LINE__); + } + } else if (!r->is_humongous()) { + HeapWord* top = r->top(); + while (obj_addr < top) { + oop obj = cast_to_oop(obj_addr); + // ctx->is_marked() returns true if mark bit set or if obj above TAMS. + if ((ctx == nullptr) || ctx->is_marked(obj)) { + // For regular objects (not object arrays), if the card holding the start of the object is dirty, + // we do not need to verify that cards spanning interesting pointers within this object are dirty. + if (!scanner->is_card_dirty(obj_addr) || obj->is_objArray()) { + obj->oop_iterate(&check_interesting_pointers); + } + // else, object's start is marked dirty and obj is not an objArray, so any interesting pointers are covered + + if ((obj_addr < registration_watermark) && !scanner->verify_registration(obj_addr, ctx)) { + ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, obj_addr, nullptr, message, + "object not properly registered", __FILE__, __LINE__); + } + obj_addr += obj->size(); + } else { + // This object is not live so we don't verify dirty cards contained therein + HeapWord* tams = ctx->top_at_mark_start(r); + obj_addr = ctx->get_next_marked_addr(obj_addr, tams); + } + } + } +} + +class ShenandoahWriteTableScanner { +private: + ShenandoahScanRemembered* _scanner; +public: + explicit ShenandoahWriteTableScanner(ShenandoahScanRemembered* scanner) : _scanner(scanner) {} + + bool is_card_dirty(HeapWord* obj_addr) { + return _scanner->is_write_card_dirty(obj_addr); + } + + bool verify_registration(HeapWord* obj_addr, ShenandoahMarkingContext* ctx) { + return _scanner->verify_registration(obj_addr, ctx); + } +}; + +// Assure that the remember set has a dirty card everywhere there is an interesting pointer. +// This examines the read_card_table between bottom() and top() since all PLABS are retired +// before the safepoint for init_mark. Actually, we retire them before update-references and don't +// restore them until the start of evacuation. +void ShenandoahVerifier::verify_rem_set_before_mark() { + shenandoah_assert_safepoint(); + shenandoah_assert_generational(); + + ShenandoahMarkingContext* ctx = get_marking_context_for_old(); + ShenandoahOldGeneration* old_generation = _heap->old_generation(); + + log_debug(gc)("Verifying remembered set at %s mark", old_generation->is_doing_mixed_evacuations() ? "mixed" : "young"); + + ShenandoahScanRemembered* scanner = old_generation->card_scan(); + for (size_t i = 0, n = _heap->num_regions(); i < n; ++i) { + ShenandoahHeapRegion* r = _heap->get_region(i); + if (r->is_old() && r->is_active()) { + help_verify_region_rem_set(scanner, r, ctx, r->end(), "Verify init-mark remembered set violation"); + } + } +} + +void ShenandoahVerifier::verify_rem_set_after_full_gc() { + shenandoah_assert_safepoint(); + shenandoah_assert_generational(); + + ShenandoahWriteTableScanner scanner(ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()); + for (size_t i = 0, n = _heap->num_regions(); i < n; ++i) { + ShenandoahHeapRegion* r = _heap->get_region(i); + if (r->is_old() && !r->is_cset()) { + help_verify_region_rem_set(&scanner, r, nullptr, r->top(), "Remembered set violation at end of Full GC"); + } + } +} + +// Assure that the remember set has a dirty card everywhere there is an interesting pointer. Even though +// the update-references scan of remembered set only examines cards up to update_watermark, the remembered +// set should be valid through top. This examines the write_card_table between bottom() and top() because +// all PLABS are retired immediately before the start of update refs. +void ShenandoahVerifier::verify_rem_set_before_update_ref() { + shenandoah_assert_safepoint(); + shenandoah_assert_generational(); + + ShenandoahMarkingContext* ctx = get_marking_context_for_old(); + ShenandoahWriteTableScanner scanner(_heap->old_generation()->card_scan()); + for (size_t i = 0, n = _heap->num_regions(); i < n; ++i) { + ShenandoahHeapRegion* r = _heap->get_region(i); + if (r->is_old() && !r->is_cset()) { + help_verify_region_rem_set(&scanner, r, ctx, r->get_update_watermark(), "Remembered set violation at init-update-references"); + } + } +} + +void ShenandoahVerifier::verify_before_rebuilding_free_set() { + ShenandoahGenerationStatsClosure cl; + _heap->heap_region_iterate(&cl); + + ShenandoahGenerationStatsClosure::validate_usage(false, "Before free set rebuild", _heap->old_generation(), cl.old); + ShenandoahGenerationStatsClosure::validate_usage(false, "Before free set rebuild", _heap->young_generation(), cl.young); + ShenandoahGenerationStatsClosure::validate_usage(false, "Before free set rebuild", _heap->global_generation(), cl.global); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp index 1617678d928..e49b7b43376 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -32,6 +33,7 @@ #include "utilities/stack.hpp" class ShenandoahHeap; +class ShenandoahMarkingContext; #ifdef _WINDOWS #pragma warning( disable : 4522 ) @@ -57,6 +59,24 @@ class ShenandoahVerifier : public CHeapObj { ShenandoahHeap* _heap; MarkBitMap* _verification_bit_map; public: + typedef enum { + // Disable remembered set verification. + _verify_remembered_disable, + + // Old objects should be registered and RS cards within *read-only* RS are dirty for all + // inter-generational pointers. + _verify_remembered_before_marking, + + // Old objects should be registered and RS cards within *read-write* RS are dirty for all + // inter-generational pointers. + _verify_remembered_before_updating_references, + + // Old objects should be registered and RS cards within *read-write* RS are dirty for all + // inter-generational pointers. Differs from previous verification modes by using top instead + // of update watermark and not using the marking context. + _verify_remembered_after_full_gc + } VerifyRememberedSet; + typedef enum { // Disable marked objects verification. _verify_marked_disable, @@ -69,7 +89,12 @@ class ShenandoahVerifier : public CHeapObj { // Objects should be marked in "complete" bitmap, except j.l.r.Reference referents, which // may be dangling after marking but before conc-weakrefs-processing. - _verify_marked_complete_except_references + _verify_marked_complete_except_references, + + // Objects should be marked in "complete" bitmap, except j.l.r.Reference referents, which + // may be dangling after marking but before conc-weakrefs-processing. All SATB buffers must + // be empty. + _verify_marked_complete_satb_empty, } VerifyMarked; typedef enum { @@ -122,6 +147,17 @@ class ShenandoahVerifier : public CHeapObj { _verify_regions_notrash_nocset } VerifyRegions; + typedef enum { + // Disable size verification + _verify_size_disable, + + // Enforce exact consistency + _verify_size_exact, + + // Expect promote-in-place adjustments: padding inserted to temporarily prevent further allocation in regular regions + _verify_size_adjusted_for_padding + } VerifySize; + typedef enum { // Disable gc-state verification _verify_gcstate_disable, @@ -133,7 +169,10 @@ class ShenandoahVerifier : public CHeapObj { _verify_gcstate_stable_weakroots, // Nothing is in progress, some objects are forwarded - _verify_gcstate_forwarded + _verify_gcstate_forwarded, + + // Evacuation is done, some objects are forwarded, updating is in progress + _verify_gcstate_updating } VerifyGCState; struct VerifyOptions { @@ -157,12 +196,14 @@ class ShenandoahVerifier : public CHeapObj { }; private: - void verify_at_safepoint(const char *label, + void verify_at_safepoint(const char* label, + VerifyRememberedSet remembered, VerifyForwarded forwarded, VerifyMarked marked, VerifyCollectionSet cset, VerifyLiveness liveness, VerifyRegions regions, + VerifySize sizeness, VerifyGCState gcstate); public: @@ -171,6 +212,7 @@ class ShenandoahVerifier : public CHeapObj { void verify_before_concmark(); void verify_after_concmark(); + void verify_after_concmark_with_promotions(); void verify_before_evacuation(); void verify_before_updaterefs(); void verify_after_updaterefs(); @@ -181,8 +223,20 @@ class ShenandoahVerifier : public CHeapObj { // Roots should only contain to-space oops void verify_roots_in_to_space(); - void verify_roots_no_forwarded(); + + // Check that generation usages are accurate before rebuilding free set + void verify_before_rebuilding_free_set(); +private: + template + void help_verify_region_rem_set(Scanner* scanner, ShenandoahHeapRegion* r, ShenandoahMarkingContext* ctx, + HeapWord* update_watermark, const char* message); + + void verify_rem_set_before_mark(); + void verify_rem_set_before_update_ref(); + void verify_rem_set_after_full_gc(); + + ShenandoahMarkingContext* get_marking_context_for_old(); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHVERIFIER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp index 3d0945ff951..288cf231e2d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.cpp @@ -35,6 +35,10 @@ uint ShenandoahWorkerPolicy::calc_workers_for_conc_marking() { return ConcGCThreads; } +uint ShenandoahWorkerPolicy::calc_workers_for_rs_scanning() { + return ConcGCThreads; +} + uint ShenandoahWorkerPolicy::calc_workers_for_final_marking() { return ParallelGCThreads; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp index c1e07d41d53..e07fc885e28 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkerPolicy.hpp @@ -35,6 +35,9 @@ class ShenandoahWorkerPolicy : AllStatic { // Calculate the number of workers for concurrent marking static uint calc_workers_for_conc_marking(); + // Calculate the number of workers for remembered set scanning + static uint calc_workers_for_rs_scanning(); + // Calculate the number of workers for final marking static uint calc_workers_for_final_marking(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp new file mode 100644 index 00000000000..59c33a015c2 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp @@ -0,0 +1,119 @@ +/* + * Copyright Amazon.com Inc. 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" + +#include "gc/shenandoah/shenandoahAgeCensus.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" + +ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity) : + ShenandoahGeneration(YOUNG, max_queues, max_capacity, soft_max_capacity), + _old_gen_task_queues(nullptr) { +} + +void ShenandoahYoungGeneration::set_concurrent_mark_in_progress(bool in_progress) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + heap->set_concurrent_young_mark_in_progress(in_progress); + if (is_bootstrap_cycle() && in_progress && !heap->is_prepare_for_old_mark_in_progress()) { + // This is not a bug. When the bootstrapping marking phase is complete, + // the old generation marking is still in progress, unless it's not. + // In the case that old-gen preparation for mixed evacuation has been + // preempted, we do not want to set concurrent old mark to be in progress. + heap->set_concurrent_old_mark_in_progress(in_progress); + } +} + +bool ShenandoahYoungGeneration::contains(ShenandoahAffiliation affiliation) const { + return affiliation == YOUNG_GENERATION; +} + +bool ShenandoahYoungGeneration::contains(ShenandoahHeapRegion* region) const { + return region->is_young(); +} + +void ShenandoahYoungGeneration::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) { + // Just iterate over the young generation here. + ShenandoahIncludeRegionClosure young_regions_cl(cl); + ShenandoahHeap::heap()->parallel_heap_region_iterate(&young_regions_cl); +} + +void ShenandoahYoungGeneration::heap_region_iterate(ShenandoahHeapRegionClosure* cl) { + ShenandoahIncludeRegionClosure young_regions_cl(cl); + ShenandoahHeap::heap()->heap_region_iterate(&young_regions_cl); +} + +void ShenandoahYoungGeneration::parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) { + // Iterate over everything that is not old. + ShenandoahExcludeRegionClosure exclude_cl(cl); + ShenandoahHeap::heap()->parallel_heap_region_iterate(&exclude_cl); +} + +bool ShenandoahYoungGeneration::is_concurrent_mark_in_progress() { + return ShenandoahHeap::heap()->is_concurrent_young_mark_in_progress(); +} + +void ShenandoahYoungGeneration::reserve_task_queues(uint workers) { + ShenandoahGeneration::reserve_task_queues(workers); + if (is_bootstrap_cycle()) { + _old_gen_task_queues->reserve(workers); + } +} + +bool ShenandoahYoungGeneration::contains(oop obj) const { + return ShenandoahHeap::heap()->is_in_young(obj); +} + +ShenandoahHeuristics* ShenandoahYoungGeneration::initialize_heuristics(ShenandoahMode* gc_mode) { + _young_heuristics = new ShenandoahYoungHeuristics(this); + _heuristics = _young_heuristics; + _heuristics->set_guaranteed_gc_interval(ShenandoahGuaranteedYoungGCInterval); + confirm_heuristics_mode(); + return _heuristics; +} + +size_t ShenandoahYoungGeneration::available() const { + // The collector reserve may eat into what the mutator is allowed to use. Make sure we are looking + // at what is available to the mutator when reporting how much memory is available. + size_t available = this->ShenandoahGeneration::available(); + return MIN2(available, ShenandoahHeap::heap()->free_set()->available()); +} + +size_t ShenandoahYoungGeneration::soft_available() const { + size_t available = this->ShenandoahGeneration::soft_available(); + return MIN2(available, ShenandoahHeap::heap()->free_set()->available()); +} + +void ShenandoahYoungGeneration::prepare_gc() { + + ShenandoahGeneration::prepare_gc(); + + assert(type() == YOUNG, "Error?"); + // Clear any stale/partial local census data before the start of a + // new marking cycle + ShenandoahGenerationalHeap::heap()->age_census()->reset_local(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp new file mode 100644 index 00000000000..1237e28c06e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp @@ -0,0 +1,85 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP + +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" + +class ShenandoahYoungGeneration : public ShenandoahGeneration { +private: + ShenandoahObjToScanQueueSet* _old_gen_task_queues; + ShenandoahYoungHeuristics* _young_heuristics; + +public: + ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t max_soft_capacity); + + ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override; + + const char* name() const override { + return "Young"; + } + + ShenandoahYoungHeuristics* heuristics() const override { + return _young_heuristics; + } + + void set_concurrent_mark_in_progress(bool in_progress) override; + bool is_concurrent_mark_in_progress() override; + + void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + + void parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) override; + + void heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + + bool contains(ShenandoahAffiliation affiliation) const override; + bool contains(ShenandoahHeapRegion* region) const override; + bool contains(oop obj) const override; + + void reserve_task_queues(uint workers) override; + void set_old_gen_task_queues(ShenandoahObjToScanQueueSet* old_gen_queues) { + _old_gen_task_queues = old_gen_queues; + } + ShenandoahObjToScanQueueSet* old_gen_task_queues() const override { + return _old_gen_task_queues; + } + + // Returns true if the young generation is configured to enqueue old + // oops for the old generation mark queues. + bool is_bootstrap_cycle() { + return _old_gen_task_queues != nullptr; + } + + size_t available() const override; + + // Do not override available_with_reserve() because that needs to see memory reserved for Collector + + size_t soft_available() const override; + + void prepare_gc() override; +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index c66e5839d58..4239fc37a26 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -33,6 +34,86 @@ range, \ constraint) \ \ + product(uintx, ShenandoahGenerationalHumongousReserve, 0, EXPERIMENTAL, \ + "(Generational mode only) What percent of the heap should be " \ + "reserved for humongous objects if possible. Old-generation " \ + "collections will endeavor to evacuate old-gen regions within " \ + "this reserved area even if these regions do not contain high " \ + "percentage of garbage. Setting a larger value will cause " \ + "more frequent old-gen collections. A smaller value will " \ + "increase the likelihood that humongous object allocations " \ + "fail, resulting in stop-the-world full GCs.") \ + range(0,100) \ + \ + product(double, ShenandoahMinOldGenGrowthPercent, 12.5, EXPERIMENTAL, \ + "(Generational mode only) If the usage within old generation " \ + "has grown by at least this percent of its live memory size " \ + "at completion of the most recent old-generation marking " \ + "effort, heuristics may trigger the start of a new old-gen " \ + "collection.") \ + range(0.0,100.0) \ + \ + product(uintx, ShenandoahIgnoreOldGrowthBelowPercentage,10, EXPERIMENTAL, \ + "(Generational mode only) If the total usage of the old " \ + "generation is smaller than this percent, we do not trigger " \ + "old gen collections even if old has grown, except when " \ + "ShenandoahGenerationalDoNotIgnoreGrowthAfterYoungCycles " \ + "consecutive cycles have been completed following the " \ + "preceding old-gen collection.") \ + range(0,100) \ + \ + product(uintx, ShenandoahDoNotIgnoreGrowthAfterYoungCycles, \ + 50, EXPERIMENTAL, \ + "(Generational mode only) Even if the usage of old generation " \ + "is below ShenandoahIgnoreOldGrowthBelowPercentage, " \ + "trigger an old-generation mark if old has grown and this " \ + "many consecutive young-gen collections have been " \ + "completed following the preceding old-gen collection.") \ + \ + product(bool, ShenandoahGenerationalCensusAtEvac, false, EXPERIMENTAL, \ + "(Generational mode only) Object age census at evacuation, " \ + "rather than during marking.") \ + \ + product(bool, ShenandoahGenerationalAdaptiveTenuring, true, EXPERIMENTAL, \ + "(Generational mode only) Dynamically adapt tenuring age.") \ + \ + product(bool, ShenandoahGenerationalCensusIgnoreOlderCohorts, true, \ + EXPERIMENTAL,\ + "(Generational mode only) Ignore mortality rates older than the " \ + "oldest cohort under the tenuring age for the last cycle." ) \ + \ + product(uintx, ShenandoahGenerationalMinTenuringAge, 1, EXPERIMENTAL, \ + "(Generational mode only) Floor for adaptive tenuring age. " \ + "Setting floor and ceiling to the same value fixes the tenuring " \ + "age; setting both to 1 simulates a poor approximation to " \ + "AlwaysTenure, and setting both to 16 simulates NeverTenure.") \ + range(1,16) \ + \ + product(uintx, ShenandoahGenerationalMaxTenuringAge, 15, EXPERIMENTAL, \ + "(Generational mode only) Ceiling for adaptive tenuring age. " \ + "Setting floor and ceiling to the same value fixes the tenuring " \ + "age; setting both to 1 simulates a poor approximation to " \ + "AlwaysTenure, and setting both to 16 simulates NeverTenure.") \ + range(1,16) \ + \ + product(double, ShenandoahGenerationalTenuringMortalityRateThreshold, \ + 0.1, EXPERIMENTAL, \ + "(Generational mode only) Cohort mortality rates below this " \ + "value will be treated as indicative of longevity, leading to " \ + "tenuring. A lower value delays tenuring, a higher value hastens "\ + "it. Used only when ShenandoahGenerationalhenAdaptiveTenuring is "\ + "enabled.") \ + range(0.001,0.999) \ + \ + product(size_t, ShenandoahGenerationalTenuringCohortPopulationThreshold, \ + 4*K, EXPERIMENTAL, \ + "(Generational mode only) Cohorts whose population is lower than "\ + "this value in the previous census are ignored wrt tenuring " \ + "decisions. Effectively this makes then tenurable as soon as all "\ + "older cohorts are. Set this value to the largest cohort " \ + "population volume that you are comfortable ignoring when making "\ + "tenuring decisions.") \ + \ product(size_t, ShenandoahRegionSize, 0, EXPERIMENTAL, \ "Static heap region size. Set zero to enable automatic sizing.") \ \ @@ -54,7 +135,8 @@ "barriers are in in use. Possible values are:" \ " satb - snapshot-at-the-beginning concurrent GC (three pass mark-evac-update);" \ " iu - incremental-update concurrent GC (three pass mark-evac-update);" \ - " passive - stop the world GC only (either degenerated or full)") \ + " passive - stop the world GC only (either degenerated or full);" \ + " generational - generational concurrent GC") \ \ product(ccstr, ShenandoahGCHeuristics, "adaptive", \ "GC heuristics to use. This fine-tunes the GC mode selected, " \ @@ -68,6 +150,16 @@ " compact - run GC more frequently and with deeper targets to " \ "free up more memory.") \ \ + product(uintx, ShenandoahExpeditePromotionsThreshold, 5, EXPERIMENTAL, \ + "When Shenandoah expects to promote at least this percentage " \ + "of the young generation, trigger a young collection to " \ + "expedite these promotions.") \ + range(0,100) \ + \ + product(uintx, ShenandoahExpediteMixedThreshold, 10, EXPERIMENTAL, \ + "When there are this many old regions waiting to be collected, " \ + "trigger a mixed collection immediately.") \ + \ product(uintx, ShenandoahGarbageThreshold, 25, EXPERIMENTAL, \ "How much garbage a region has to contain before it would be " \ "taken for collection. This a guideline only, as GC heuristics " \ @@ -76,17 +168,35 @@ "collector accepts. In percents of heap region size.") \ range(0,100) \ \ + product(uintx, ShenandoahOldGarbageThreshold, 15, EXPERIMENTAL, \ + "How much garbage an old region has to contain before it would " \ + "be taken for collection.") \ + range(0,100) \ + \ + product(uintx, ShenandoahIgnoreGarbageThreshold, 5, EXPERIMENTAL, \ + "When less than this amount of garbage (as a percentage of " \ + "region size) exists within a region, the region will not be " \ + "added to the collection set, even when the heuristic has " \ + "chosen to aggressively add regions with less than " \ + "ShenandoahGarbageThreshold amount of garbage into the " \ + "collection set.") \ + range(0,100) \ + \ product(uintx, ShenandoahInitFreeThreshold, 70, EXPERIMENTAL, \ - "How much heap should be free before some heuristics trigger the "\ - "initial (learning) cycles. Affects cycle frequency on startup " \ - "and after drastic state changes, e.g. after degenerated/full " \ - "GC cycles. In percents of (soft) max heap size.") \ + "When less than this amount of memory is free within the" \ + "heap or generation, trigger a learning cycle if we are " \ + "in learning mode. Learning mode happens during initialization " \ + "and following a drastic state change, such as following a " \ + "degenerated or Full GC cycle. In percents of soft max " \ + "heap size.") \ range(0,100) \ \ product(uintx, ShenandoahMinFreeThreshold, 10, EXPERIMENTAL, \ - "How much heap should be free before most heuristics trigger the "\ - "collection, even without other triggers. Provides the safety " \ - "margin for many heuristics. In percents of (soft) max heap size.")\ + "Percentage of free heap memory (or young generation, in " \ + "generational mode) below which most heuristics trigger " \ + "collection independent of other triggers. Provides a safety " \ + "margin for many heuristics. In percents of (soft) max heap " \ + "size.") \ range(0,100) \ \ product(uintx, ShenandoahAllocationThreshold, 0, EXPERIMENTAL, \ @@ -149,6 +259,16 @@ "time from active application. Time is in milliseconds. " \ "Setting this to 0 disables the feature.") \ \ + product(uintx, ShenandoahGuaranteedOldGCInterval, 10*60*1000, EXPERIMENTAL, \ + "Run a collection of the old generation at least this often. " \ + "Heuristics may trigger collections more frequently. Time is in " \ + "milliseconds. Setting this to 0 disables the feature.") \ + \ + product(uintx, ShenandoahGuaranteedYoungGCInterval, 5*60*1000, EXPERIMENTAL, \ + "Run a collection of the young generation at least this often. " \ + "Heuristics may trigger collections more frequently. Time is in " \ + "milliseconds. Setting this to 0 disables the feature.") \ + \ product(bool, ShenandoahAlwaysClearSoftRefs, false, EXPERIMENTAL, \ "Unconditionally clear soft references, instead of using any " \ "other cleanup policy. This minimizes footprint at expense of" \ @@ -208,17 +328,42 @@ " 4 = previous level, plus all marked objects") \ \ product(uintx, ShenandoahEvacReserve, 5, EXPERIMENTAL, \ - "How much of heap to reserve for evacuations. Larger values make "\ - "GC evacuate more live objects on every cycle, while leaving " \ - "less headroom for application to allocate in. In percents of " \ - "total heap size.") \ + "How much of (young-generation) heap to reserve for " \ + "(young-generation) evacuations. Larger values allow GC to " \ + "evacuate more live objects on every cycle, while leaving " \ + "less headroom for application to allocate while GC is " \ + "evacuating and updating references. This parameter is " \ + "consulted at the end of marking, before selecting the " \ + "collection set. If available memory at this time is smaller " \ + "than the indicated reserve, the bound on collection set size is "\ + "adjusted downward. The size of a generational mixed " \ + "evacuation collection set (comprised of both young and old " \ + "regions) is also bounded by this parameter. In percents of " \ + "total (young-generation) heap size.") \ range(1,100) \ \ product(double, ShenandoahEvacWaste, 1.2, EXPERIMENTAL, \ "How much waste evacuations produce within the reserved space. " \ "Larger values make evacuations more resilient against " \ "evacuation conflicts, at expense of evacuating less on each " \ - "GC cycle.") \ + "GC cycle. Smaller values increase the risk of evacuation " \ + "failures, which will trigger stop-the-world Full GC passes.") \ + range(1.0,100.0) \ + \ + product(double, ShenandoahOldEvacWaste, 1.4, EXPERIMENTAL, \ + "How much waste evacuations produce within the reserved space. " \ + "Larger values make evacuations more resilient against " \ + "evacuation conflicts, at expense of evacuating less on each " \ + "GC cycle. Smaller values increase the risk of evacuation " \ + "failures, which will trigger stop-the-world Full GC passes.") \ + range(1.0,100.0) \ + \ + product(double, ShenandoahPromoEvacWaste, 1.2, EXPERIMENTAL, \ + "How much waste promotions produce within the reserved space. " \ + "Larger values make evacuations more resilient against " \ + "evacuation conflicts, at expense of promoting less on each " \ + "GC cycle. Smaller values increase the risk of evacuation " \ + "failures, which will trigger stop-the-world Full GC passes.") \ range(1.0,100.0) \ \ product(bool, ShenandoahEvacReserveOverflow, true, EXPERIMENTAL, \ @@ -227,6 +372,41 @@ "reserve/waste is incorrect, at the risk that application " \ "runs out of memory too early.") \ \ + product(uintx, ShenandoahOldEvacRatioPercent, 75, EXPERIMENTAL, \ + "The maximum proportion of evacuation from old-gen memory, " \ + "expressed as a percentage. The default value 75 denotes that no" \ + "more than 75% of the collection set evacuation workload may be " \ + "towards evacuation of old-gen heap regions. This limits both the"\ + "promotion of aged regions and the compaction of existing old " \ + "regions. A value of 75 denotes that the total evacuation work" \ + "may increase to up to four times the young gen evacuation work." \ + "A larger value allows quicker promotion and allows" \ + "a smaller number of mixed evacuations to process " \ + "the entire list of old-gen collection candidates at the cost " \ + "of an increased disruption of the normal cadence of young-gen " \ + "collections. A value of 100 allows a mixed evacuation to " \ + "focus entirely on old-gen memory, allowing no young-gen " \ + "regions to be collected, likely resulting in subsequent " \ + "allocation failures because the allocation pool is not " \ + "replenished. A value of 0 allows a mixed evacuation to" \ + "focus entirely on young-gen memory, allowing no old-gen " \ + "regions to be collected, likely resulting in subsequent " \ + "promotion failures and triggering of stop-the-world full GC " \ + "events.") \ + range(0,100) \ + \ + product(uintx, ShenandoahMinYoungPercentage, 20, EXPERIMENTAL, \ + "The minimum percentage of the heap to use for the young " \ + "generation. Heuristics will not adjust the young generation " \ + "to be less than this.") \ + range(0, 100) \ + \ + product(uintx, ShenandoahMaxYoungPercentage, 100, EXPERIMENTAL, \ + "The maximum percentage of the heap to use for the young " \ + "generation. Heuristics will not adjust the young generation " \ + "to be more than this.") \ + range(0, 100) \ + \ product(bool, ShenandoahPacing, true, EXPERIMENTAL, \ "Pace application allocations to give GC chance to start " \ "and complete before allocation failure is reached.") \ @@ -301,6 +481,15 @@ product(bool, ShenandoahAllocFailureALot, false, DIAGNOSTIC, \ "Testing: make lots of artificial allocation failures.") \ \ + product(uintx, ShenandoahCoalesceChance, 0, DIAGNOSTIC, \ + "Testing: Abandon remaining mixed collections with this " \ + "likelihood. Following each mixed collection, abandon all " \ + "remaining mixed collection candidate regions with likelihood " \ + "ShenandoahCoalesceChance. Abandoning a mixed collection will " \ + "cause the old regions to be made parsable, rather than being " \ + "evacuated.") \ + range(0, 100) \ + \ product(intx, ShenandoahMarkScanPrefetch, 32, EXPERIMENTAL, \ "How many objects to prefetch ahead when traversing mark bitmaps."\ "Set to 0 to disable prefetching.") \ @@ -327,6 +516,10 @@ product(bool, ShenandoahSATBBarrier, true, DIAGNOSTIC, \ "Turn on/off SATB barriers in Shenandoah") \ \ + product(bool, ShenandoahCardBarrier, false, DIAGNOSTIC, \ + "Turn on/off card-marking post-write barrier in Shenandoah: " \ + " true when ShenandoahGCMode is generational, false otherwise") \ + \ product(bool, ShenandoahCASBarrier, true, DIAGNOSTIC, \ "Turn on/off CAS barriers in Shenandoah") \ \ @@ -342,7 +535,34 @@ develop(bool, ShenandoahVerifyOptoBarriers, trueInDebug, \ "Verify no missing barriers in C2.") \ \ - -// end of GC_SHENANDOAH_FLAGS + product(uintx, ShenandoahOldCompactionReserve, 8, EXPERIMENTAL, \ + "During generational GC, prevent promotions from filling " \ + "this number of heap regions. These regions are reserved " \ + "for the purpose of supporting compaction of old-gen " \ + "memory. Otherwise, old-gen memory cannot be compacted.") \ + range(0, 128) \ + \ + product(bool, ShenandoahAllowOldMarkingPreemption, true, DIAGNOSTIC, \ + "Allow young generation collections to suspend concurrent" \ + " marking in the old generation.") \ + \ + product(uintx, ShenandoahAgingCyclePeriod, 1, EXPERIMENTAL, \ + "With generational mode, increment the age of objects and" \ + "regions each time this many young-gen GC cycles are completed.") \ + \ + develop(bool, ShenandoahEnableCardStats, false, \ + "Enable statistics collection related to clean & dirty cards") \ + \ + develop(int, ShenandoahCardStatsLogInterval, 50, \ + "Log cumulative card stats every so many remembered set or " \ + "update refs scans") \ + \ + product(uintx, ShenandoahMinimumOldTimeMs, 100, EXPERIMENTAL, \ + "Minimum amount of time in milliseconds to run old collections " \ + "before a young collection is allowed to run. This is intended " \ + "to prevent starvation of the old collector. Setting this to " \ + "0 will allow back to back young collections to run during old " \ + "collections.") \ + // end of GC_SHENANDOAH_FLAGS #endif // SHARE_GC_SHENANDOAH_SHENANDOAH_GLOBALS_HPP diff --git a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp index 9bc3af5ba9e..376df7f7a0f 100644 --- a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp +++ b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp @@ -25,6 +25,8 @@ #define SHARE_GC_SHENANDOAH_VMSTRUCTS_SHENANDOAH_HPP #include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" @@ -32,8 +34,9 @@ nonstatic_field(ShenandoahHeap, _num_regions, size_t) \ nonstatic_field(ShenandoahHeap, _regions, ShenandoahHeapRegion**) \ nonstatic_field(ShenandoahHeap, _log_min_obj_alignment_in_bytes, int) \ - volatile_nonstatic_field(ShenandoahHeap, _used, size_t) \ + nonstatic_field(ShenandoahHeap, _global_generation, ShenandoahGeneration*) \ volatile_nonstatic_field(ShenandoahHeap, _committed, size_t) \ + volatile_nonstatic_field(ShenandoahGeneration, _used, size_t) \ static_field(ShenandoahHeapRegion, RegionSizeBytes, size_t) \ static_field(ShenandoahHeapRegion, RegionSizeBytesShift, size_t) \ nonstatic_field(ShenandoahHeapRegion, _state, ShenandoahHeapRegion::RegionState) \ @@ -58,9 +61,12 @@ declare_toplevel_type, \ declare_integer_type) \ declare_type(ShenandoahHeap, CollectedHeap) \ + declare_type(ShenandoahGenerationalHeap, ShenandoahHeap) \ declare_toplevel_type(ShenandoahHeapRegion) \ declare_toplevel_type(ShenandoahHeap*) \ declare_toplevel_type(ShenandoahHeapRegion*) \ declare_toplevel_type(ShenandoahHeapRegion::RegionState) \ + declare_toplevel_type(ShenandoahGeneration) \ + declare_toplevel_type(ShenandoahGeneration*) \ #endif // SHARE_GC_SHENANDOAH_VMSTRUCTS_SHENANDOAH_HPP diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index f8b792f3094..3b5e24ca9a2 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -1208,6 +1208,23 @@ + + + + + + + + + + + + + + + + + diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 93c1acea1db..af5f3cf79ca 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -438,7 +438,8 @@ WB_ENTRY(jboolean, WB_isObjectInOldGen(JNIEnv* env, jobject o, jobject obj)) #endif #if INCLUDE_SHENANDOAHGC if (UseShenandoahGC) { - return Universe::heap()->is_in(p); + ShenandoahHeap* sh = ShenandoahHeap::heap(); + return sh->mode()->is_generational() ? sh->is_in_old(p) : sh->is_in(p); } #endif #if INCLUDE_SERIALGC diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGeneration.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGeneration.java new file mode 100644 index 00000000000..bcd59523ae0 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGeneration.java @@ -0,0 +1,59 @@ +/* + * * Copyright Amazon.com Inc. 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. + * + */ + +package sun.jvm.hotspot.gc.shenandoah; + +import sun.jvm.hotspot.utilities.Observable; +import sun.jvm.hotspot.utilities.Observer; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.VMObject; +import sun.jvm.hotspot.types.CIntegerField; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; + +public class ShenandoahGeneration extends VMObject { + private static CIntegerField used; + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("ShenandoahGeneration"); + used = type.getCIntegerField("_used"); + } + + public ShenandoahGeneration(Address addr) { + super(addr); + } + + public long used() { + return used.getValue(addr); + } +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGenerationalHeap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGenerationalHeap.java new file mode 100644 index 00000000000..4b24c4ecedd --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahGenerationalHeap.java @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +package sun.jvm.hotspot.gc.shenandoah; + +import sun.jvm.hotspot.debugger.Address; + +public class ShenandoahGenerationalHeap extends ShenandoahHeap { + public ShenandoahGenerationalHeap(Address addr) { + super(addr); + } +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeap.java index ca12562ac3e..3109fe22102 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeap.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeap.java @@ -43,7 +43,7 @@ public class ShenandoahHeap extends CollectedHeap { private static CIntegerField numRegions; - private static CIntegerField used; + private static AddressField globalGeneration; private static CIntegerField committed; private static AddressField regions; private static CIntegerField logMinObjAlignmentInBytes; @@ -60,7 +60,7 @@ public void update(Observable o, Object data) { private static synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("ShenandoahHeap"); numRegions = type.getCIntegerField("_num_regions"); - used = type.getCIntegerField("_used"); + globalGeneration = type.getAddressField("_global_generation"); committed = type.getCIntegerField("_committed"); regions = type.getAddressField("_regions"); logMinObjAlignmentInBytes = type.getCIntegerField("_log_min_obj_alignment_in_bytes"); @@ -89,7 +89,9 @@ public long capacity() { @Override public long used() { - return used.getValue(addr); + Address globalGenerationAddress = globalGeneration.getValue(addr); + ShenandoahGeneration global = VMObjectFactory.newObject(ShenandoahGeneration.class, globalGenerationAddress); + return global.used(); } public long committed() { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java index 01fd0f7430a..4bed0ffc638 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java @@ -36,6 +36,7 @@ import sun.jvm.hotspot.gc.serial.SerialHeap; import sun.jvm.hotspot.gc.shared.CollectedHeap; import sun.jvm.hotspot.gc.shenandoah.ShenandoahHeap; +import sun.jvm.hotspot.gc.shenandoah.ShenandoahGenerationalHeap; import sun.jvm.hotspot.gc.z.ZCollectedHeap; import sun.jvm.hotspot.oops.Oop; import sun.jvm.hotspot.runtime.BasicType; @@ -88,6 +89,7 @@ private static synchronized void initialize(TypeDataBase db) { addHeapTypeIfInDB(db, EpsilonHeap.class); addHeapTypeIfInDB(db, ZCollectedHeap.class); addHeapTypeIfInDB(db, ShenandoahHeap.class); + addHeapTypeIfInDB(db, ShenandoahGenerationalHeap.class); UniverseExt.initialize(heapConstructor); } diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 79aee3f3349..57016a9bdd0 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -516,6 +516,10 @@ false + + false + + true false diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 6691638e5d4..1df3af7475f 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -516,6 +516,10 @@ false + + false + + true true diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp index 17a4ed642a5..f1470ea9338 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp @@ -1,6 +1,6 @@ /* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2022, 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 @@ -34,45 +34,130 @@ class ShenandoahNumberSeqTest: public ::testing::Test { protected: - HdrSeq seq; + const double err = 0.5; + + HdrSeq seq1; + HdrSeq seq2; + HdrSeq seq3; + + void print() { + if (seq1.num() > 0) { + print(seq1, "seq1"); + } + if (seq2.num() > 0) { + print(seq2, "seq2"); + } + if (seq3.num() > 0) { + print(seq3, "seq3"); + } + } + + void print(HdrSeq& seq, const char* msg) { + std::cout << "["; + for (int i = 0; i <= 100; i += 10) { + std::cout << "\t p" << i << ":" << seq.percentile(i); + } + std::cout << "\t] : " << msg << "\n"; + } }; class BasicShenandoahNumberSeqTest: public ShenandoahNumberSeqTest { - protected: - const double err = 0.5; + public: BasicShenandoahNumberSeqTest() { - seq.add(0); - seq.add(1); - seq.add(10); + seq1.add(0); + seq1.add(1); + seq1.add(10); for (int i = 0; i < 7; i++) { - seq.add(100); + seq1.add(100); + } + ShenandoahNumberSeqTest::print(); + } +}; + +class ShenandoahNumberSeqMergeTest: public ShenandoahNumberSeqTest { + public: + ShenandoahNumberSeqMergeTest() { + for (int i = 0; i < 80; i++) { + seq1.add(1); + seq3.add(1); } - std::cout << " p0 = " << seq.percentile(0); - std::cout << " p10 = " << seq.percentile(10); - std::cout << " p20 = " << seq.percentile(20); - std::cout << " p30 = " << seq.percentile(30); - std::cout << " p50 = " << seq.percentile(50); - std::cout << " p80 = " << seq.percentile(80); - std::cout << " p90 = " << seq.percentile(90); - std::cout << " p100 = " << seq.percentile(100); + + for (int i = 0; i < 20; i++) { + seq2.add(100); + seq3.add(100); + } + ShenandoahNumberSeqTest::print(); } }; TEST_VM_F(BasicShenandoahNumberSeqTest, maximum_test) { - EXPECT_EQ(seq.maximum(), 100); + EXPECT_EQ(seq1.maximum(), 100); } TEST_VM_F(BasicShenandoahNumberSeqTest, minimum_test) { - EXPECT_EQ(0, seq.percentile(0)); + EXPECT_EQ(0, seq1.percentile(0)); } TEST_VM_F(BasicShenandoahNumberSeqTest, percentile_test) { - EXPECT_NEAR(0, seq.percentile(10), err); - EXPECT_NEAR(1, seq.percentile(20), err); - EXPECT_NEAR(10, seq.percentile(30), err); - EXPECT_NEAR(100, seq.percentile(40), err); - EXPECT_NEAR(100, seq.percentile(50), err); - EXPECT_NEAR(100, seq.percentile(75), err); - EXPECT_NEAR(100, seq.percentile(90), err); - EXPECT_NEAR(100, seq.percentile(100), err); + EXPECT_NEAR(0, seq1.percentile(10), err); + EXPECT_NEAR(1, seq1.percentile(20), err); + EXPECT_NEAR(10, seq1.percentile(30), err); + EXPECT_NEAR(100, seq1.percentile(40), err); + EXPECT_NEAR(100, seq1.percentile(50), err); + EXPECT_NEAR(100, seq1.percentile(75), err); + EXPECT_NEAR(100, seq1.percentile(90), err); + EXPECT_NEAR(100, seq1.percentile(100), err); +} + +TEST_VM_F(BasicShenandoahNumberSeqTest, clear_test) { + HdrSeq test; + test.add(1); + + EXPECT_NE(test.num(), 0); + EXPECT_NE(test.sum(), 0); + EXPECT_NE(test.maximum(), 0); + EXPECT_NE(test.avg(), 0); + EXPECT_EQ(test.sd(), 0); + EXPECT_NE(test.davg(), 0); + EXPECT_EQ(test.dvariance(), 0); + for (int i = 0; i <= 100; i += 10) { + EXPECT_NE(test.percentile(i), 0); + } + + test.clear(); + + EXPECT_EQ(test.num(), 0); + EXPECT_EQ(test.sum(), 0); + EXPECT_EQ(test.maximum(), 0); + EXPECT_EQ(test.avg(), 0); + EXPECT_EQ(test.sd(), 0); + EXPECT_EQ(test.davg(), 0); + EXPECT_EQ(test.dvariance(), 0); + for (int i = 0; i <= 100; i += 10) { + EXPECT_EQ(test.percentile(i), 0); + } +} + +TEST_VM_F(ShenandoahNumberSeqMergeTest, merge_test) { + EXPECT_EQ(seq1.num(), 80); + EXPECT_EQ(seq2.num(), 20); + EXPECT_EQ(seq3.num(), 100); + + HdrSeq merged; + merged.add(seq1); + merged.add(seq2); + + EXPECT_EQ(merged.num(), seq3.num()); + + EXPECT_EQ(merged.maximum(), seq3.maximum()); + EXPECT_EQ(merged.percentile(0), seq3.percentile(0)); + for (int i = 0; i <= 100; i += 10) { + EXPECT_NEAR(merged.percentile(i), seq3.percentile(i), err); + } + EXPECT_NEAR(merged.avg(), seq3.avg(), err); + EXPECT_NEAR(merged.sd(), seq3.sd(), err); + + // These are not implemented + EXPECT_TRUE(isnan(merged.davg())); + EXPECT_TRUE(isnan(merged.dvariance())); } diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp new file mode 100644 index 00000000000..e5c09a3645c --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp @@ -0,0 +1,200 @@ +/* + * Copyright Amazon.com Inc. 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" +#include "unittest.hpp" + +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" + +#define SKIP_IF_NOT_SHENANDOAH() \ + if (!(UseShenandoahGC && ShenandoahHeap::heap()->mode()->is_generational())) { \ + tty->print_cr("skipped (run with -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational)"); \ + return; \ + } + + +class ShenandoahOldGenerationTest : public ::testing::Test { +protected: + static const size_t INITIAL_PLAB_SIZE; + static const size_t INITIAL_PLAB_PROMOTED; + + ShenandoahOldGeneration* old; + + ShenandoahOldGenerationTest() + : old(nullptr) + { + } + + void SetUp() override { + SKIP_IF_NOT_SHENANDOAH(); + + ShenandoahHeap::heap()->lock()->lock(false); + + old = new ShenandoahOldGeneration(8, 1024 * 1024, 1024); + old->set_promoted_reserve(512 * HeapWordSize); + old->expend_promoted(256 * HeapWordSize); + old->set_evacuation_reserve(512 * HeapWordSize); + + Thread* thread = Thread::current(); + ShenandoahThreadLocalData::reset_plab_promoted(thread); + ShenandoahThreadLocalData::disable_plab_promotions(thread); + ShenandoahThreadLocalData::set_plab_actual_size(thread, INITIAL_PLAB_SIZE); + ShenandoahThreadLocalData::add_to_plab_promoted(thread, INITIAL_PLAB_PROMOTED); + } + + void TearDown() override { + if (UseShenandoahGC) { + ShenandoahHeap::heap()->lock()->unlock(); + delete old; + } + } + + static bool promotions_enabled() { + return ShenandoahThreadLocalData::allow_plab_promotions(Thread::current()); + } + + static size_t plab_size() { + return ShenandoahThreadLocalData::get_plab_actual_size(Thread::current()); + } + + static size_t plab_promoted() { + return ShenandoahThreadLocalData::get_plab_promoted(Thread::current()); + } +}; + +const size_t ShenandoahOldGenerationTest::INITIAL_PLAB_SIZE = 42; +const size_t ShenandoahOldGenerationTest::INITIAL_PLAB_PROMOTED = 128; + +TEST_VM_F(ShenandoahOldGenerationTest, test_can_promote) { + SKIP_IF_NOT_SHENANDOAH(); + EXPECT_TRUE(old->can_promote(128 * HeapWordSize)) << "Should have room to promote"; + EXPECT_FALSE(old->can_promote(384 * HeapWordSize)) << "Should not have room to promote"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_plab_for_promotion) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128); + EXPECT_TRUE(old->can_allocate(req)) << "Should have room to promote"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_plab_for_evacuation) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(384, 384); + EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotions"; + EXPECT_TRUE(old->can_allocate(req)) << "Should have room to evacuate"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_cannot_allocate_plab) { + SKIP_IF_NOT_SHENANDOAH(); + // Simulate having exhausted the evacuation reserve when request is too big to be promoted + old->set_evacuation_reserve(0); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(384, 384); + EXPECT_FALSE(old->can_allocate(req)) << "No room for promotions or evacuations"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_for_shared_evacuation) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(768, ShenandoahAffiliation::OLD_GENERATION, false); + EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotion"; + EXPECT_TRUE(old->can_allocate(req)) << "Should have room to evacuate shared (even though evacuation reserve is smaller than request)"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_cannot_allocate_for_shared_promotion) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(768, ShenandoahAffiliation::OLD_GENERATION, true); + EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotion"; + EXPECT_FALSE(old->can_allocate(req)) << "No room to promote, should fall back to evacuation in young gen"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128); + + // simulate the allocation + req.set_actual_size(128); + + size_t actual_size = req.actual_size() * HeapWordSize; + EXPECT_TRUE(old->can_promote(actual_size)) << "Should have room for promotion"; + + size_t expended_before = old->get_promoted_expended(); + old->configure_plab_for_current_thread(req); + size_t expended_after = old->get_promoted_expended(); + EXPECT_EQ(expended_before + actual_size, expended_after) << "Should expend promotion reserve"; + EXPECT_EQ(plab_promoted(), 0UL) << "Nothing promoted yet"; + EXPECT_EQ(plab_size(), actual_size) << "New plab should be able to hold this much promotion"; + EXPECT_TRUE(promotions_enabled()) << "Plab should be available for promotions"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserve) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128); + + // simulate an allocation that exceeds the promotion reserve after allocation + req.set_actual_size(384); + EXPECT_FALSE(old->can_promote(req.actual_size() * HeapWordSize)) << "Should have room for promotion"; + + size_t expended_before = old->get_promoted_expended(); + old->configure_plab_for_current_thread(req); + size_t expended_after = old->get_promoted_expended(); + + EXPECT_EQ(expended_before, expended_after) << "Did not promote, should not expend promotion"; + EXPECT_EQ(plab_promoted(), 0UL) << "Cannot promote in new plab"; + EXPECT_EQ(plab_size(), 0UL) << "Should not have space for promotions"; + EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations"; +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_shared_expends_promoted_but_does_not_change_plab) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, true); + req.set_actual_size(128); + size_t actual_size = req.actual_size() * HeapWordSize; + + size_t expended_before = old->get_promoted_expended(); + old->configure_plab_for_current_thread(req); + size_t expended_after = old->get_promoted_expended(); + + EXPECT_EQ(expended_before + actual_size, expended_after) << "Shared promotion still expends promotion"; + EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Shared promotion should not count in plab"; + EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Shared promotion should not change size of plab"; + EXPECT_FALSE(promotions_enabled()); +} + +TEST_VM_F(ShenandoahOldGenerationTest, test_shared_evacuation_has_no_side_effects) { + SKIP_IF_NOT_SHENANDOAH(); + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, false); + req.set_actual_size(128); + + size_t expended_before = old->get_promoted_expended(); + old->configure_plab_for_current_thread(req); + size_t expended_after = old->get_promoted_expended(); + + EXPECT_EQ(expended_before, expended_after) << "Not a promotion, should not expend promotion reserve"; + EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Not a plab, should not have touched plab"; + EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Not a plab, should not have touched plab"; + EXPECT_FALSE(promotions_enabled()); +} + +#undef SKIP_IF_NOT_SHENANDOAH diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp new file mode 100644 index 00000000000..d2bb9108fa9 --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldHeuristic.cpp @@ -0,0 +1,366 @@ +/* + * Copyright Amazon.com Inc. 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" +#include "unittest.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" +#include + +// These tests will all be skipped (unless Shenandoah becomes the default +// collector). To execute these tests, you must enable Shenandoah, which +// is done with: +// +// % make exploded-test TEST="gtest:ShenandoahOld*" CONF=release TEST_OPTS="JAVA_OPTIONS=-XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational" +// +// Please note that these 'unit' tests are really integration tests and rely +// on the JVM being initialized. These tests manipulate the state of the +// collector in ways that are not compatible with a normal collection run. +// If these tests take longer than the minimum time between gc intervals - +// or, more likely, if you have them paused in a debugger longer than this +// interval - you can expect trouble. These tests will also not run in a build +// with asserts enabled because they use APIs that expect to run on a safepoint. +#ifdef ASSERT +#define SKIP_IF_NOT_SHENANDOAH() \ + tty->print_cr("skipped (debug build)" ); \ + return; +#else +#define SKIP_IF_NOT_SHENANDOAH() \ + if (!UseShenandoahGC) { \ + tty->print_cr("skipped"); \ + return; \ + } +#endif + +class ShenandoahResetRegions : public ShenandoahHeapRegionClosure { + public: + virtual void heap_region_do(ShenandoahHeapRegion* region) override { + if (!region->is_empty()) { + region->make_trash(); + region->make_empty(); + } + region->set_affiliation(FREE); + region->clear_live_data(); + region->set_top(region->bottom()); + } +}; + +class ShenandoahOldHeuristicTest : public ::testing::Test { + protected: + ShenandoahHeap* _heap; + ShenandoahOldHeuristics* _heuristics; + ShenandoahCollectionSet* _collection_set; + + ShenandoahOldHeuristicTest() + : _heap(nullptr), + _heuristics(nullptr), + _collection_set(nullptr) { + SKIP_IF_NOT_SHENANDOAH(); + _heap = ShenandoahHeap::heap(); + _heuristics = _heap->old_generation()->heuristics(); + _collection_set = _heap->collection_set(); + _heap->lock()->lock(false); + ShenandoahResetRegions reset; + _heap->heap_region_iterate(&reset); + _heap->old_generation()->set_capacity(ShenandoahHeapRegion::region_size_bytes() * 10); + _heap->old_generation()->set_evacuation_reserve(ShenandoahHeapRegion::region_size_bytes() * 4); + _heuristics->abandon_collection_candidates(); + _collection_set->clear(); + } + + ~ShenandoahOldHeuristicTest() override { + SKIP_IF_NOT_SHENANDOAH(); + _heap->lock()->unlock(); + } + + ShenandoahOldGeneration::State old_generation_state() { + return _heap->old_generation()->state(); + } + + size_t make_garbage(size_t region_idx, size_t garbage_bytes) { + ShenandoahHeapRegion* region = _heap->get_region(region_idx); + region->set_affiliation(OLD_GENERATION); + region->make_regular_allocation(OLD_GENERATION); + size_t live_bytes = ShenandoahHeapRegion::region_size_bytes() - garbage_bytes; + region->increase_live_data_alloc_words(live_bytes / HeapWordSize); + region->set_top(region->end()); + return region->garbage(); + } + + size_t create_too_much_garbage_for_one_mixed_evacuation() { + size_t garbage_target = _heap->old_generation()->max_capacity() / 2; + size_t garbage_total = 0; + size_t region_idx = 0; + while (garbage_total < garbage_target && region_idx < _heap->num_regions()) { + garbage_total += make_garbage_above_collection_threshold(region_idx++); + } + return garbage_total; + } + + void make_pinned(size_t region_idx) { + ShenandoahHeapRegion* region = _heap->get_region(region_idx); + region->record_pin(); + region->make_pinned(); + } + + void make_unpinned(size_t region_idx) { + ShenandoahHeapRegion* region = _heap->get_region(region_idx); + region->record_unpin(); + region->make_unpinned(); + } + + size_t make_garbage_below_collection_threshold(size_t region_idx) { + return make_garbage(region_idx, collection_threshold() - 100); + } + + size_t make_garbage_above_collection_threshold(size_t region_idx) { + return make_garbage(region_idx, collection_threshold() + 100); + } + + size_t collection_threshold() const { + return ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold / 100; + } + + bool collection_set_is(size_t r1) { return _collection_set_is(1, r1); } + bool collection_set_is(size_t r1, size_t r2) { return _collection_set_is(2, r1, r2); } + bool collection_set_is(size_t r1, size_t r2, size_t r3) { return _collection_set_is(3, r1, r2, r3); } + + bool _collection_set_is(size_t count, ...) { + va_list args; + va_start(args, count); + EXPECT_EQ(count, _collection_set->count()); + bool result = true; + for (size_t i = 0; i < count; ++i) { + size_t index = va_arg(args, size_t); + if (!_collection_set->is_in(index)) { + result = false; + break; + } + } + va_end(args); + return result; + } +}; + +TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_regions) { + SKIP_IF_NOT_SHENANDOAH(); + + _heuristics->prepare_for_old_collections(); + EXPECT_EQ(0U, _heuristics->coalesce_and_fill_candidates_count()); + EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index()); + EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, select_no_old_region_above_threshold) { + SKIP_IF_NOT_SHENANDOAH(); + + // In this case, we have zero regions to add to the collection set, + // but we will have one region that must still be made parseable. + make_garbage_below_collection_threshold(10); + _heuristics->prepare_for_old_collections(); + EXPECT_EQ(1U, _heuristics->coalesce_and_fill_candidates_count()); + EXPECT_EQ(0U, _heuristics->last_old_collection_candidate_index()); + EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, select_one_old_region_above_threshold) { + SKIP_IF_NOT_SHENANDOAH(); + + make_garbage_above_collection_threshold(10); + _heuristics->prepare_for_old_collections(); + EXPECT_EQ(1U, _heuristics->coalesce_and_fill_candidates_count()); + EXPECT_EQ(1U, _heuristics->last_old_collection_candidate_index()); + EXPECT_EQ(1U, _heuristics->unprocessed_old_collection_candidates()); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, prime_one_old_region) { + SKIP_IF_NOT_SHENANDOAH(); + + size_t garbage = make_garbage_above_collection_threshold(10); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(10UL)); + EXPECT_EQ(garbage, _collection_set->get_old_garbage()); + EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, prime_many_old_regions) { + SKIP_IF_NOT_SHENANDOAH(); + + size_t g1 = make_garbage_above_collection_threshold(100); + size_t g2 = make_garbage_above_collection_threshold(101); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(100UL, 101UL)); + EXPECT_EQ(g1 + g2, _collection_set->get_old_garbage()); + EXPECT_EQ(0U, _heuristics->unprocessed_old_collection_candidates()); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, require_multiple_mixed_evacuations) { + SKIP_IF_NOT_SHENANDOAH(); + + size_t garbage = create_too_much_garbage_for_one_mixed_evacuation(); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_LT(_collection_set->get_old_garbage(), garbage); + EXPECT_GT(_heuristics->unprocessed_old_collection_candidates(), 0UL); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, skip_pinned_regions) { + SKIP_IF_NOT_SHENANDOAH(); + + // Create three old regions with enough garbage to be collected. + size_t g1 = make_garbage_above_collection_threshold(0); + size_t g2 = make_garbage_above_collection_threshold(1); + size_t g3 = make_garbage_above_collection_threshold(2); + + // A region can be pinned when we chose collection set candidates. + make_pinned(1); + _heuristics->prepare_for_old_collections(); + + // We only exclude pinned regions when we actually add regions to the collection set. + ASSERT_EQ(3UL, _heuristics->unprocessed_old_collection_candidates()); + + // Here the region is still pinned, so it cannot be added to the collection set. + _heuristics->prime_collection_set(_collection_set); + + // The two unpinned regions should be added to the collection set and the pinned + // region should be retained at the front of the list of candidates as it would be + // likely to become unpinned by the next mixed collection cycle. + EXPECT_TRUE(collection_set_is(0UL, 2UL)); + EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); + + // Simulate another mixed collection after making region 1 unpinned. This time, + // the now unpinned region should be added to the collection set. + make_unpinned(1); + _collection_set->clear(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_EQ(_collection_set->get_old_garbage(), g2); + EXPECT_TRUE(collection_set_is(1UL)); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_first) { + SKIP_IF_NOT_SHENANDOAH(); + + // Create three old regions with enough garbage to be collected. + size_t g1 = make_garbage_above_collection_threshold(0); + size_t g2 = make_garbage_above_collection_threshold(1); + size_t g3 = make_garbage_above_collection_threshold(2); + + make_pinned(0); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(1UL, 2UL)); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); + + make_unpinned(0); + _collection_set->clear(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(0UL)); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, pinned_region_is_last) { + SKIP_IF_NOT_SHENANDOAH(); + + // Create three old regions with enough garbage to be collected. + size_t g1 = make_garbage_above_collection_threshold(0); + size_t g2 = make_garbage_above_collection_threshold(1); + size_t g3 = make_garbage_above_collection_threshold(2); + + make_pinned(2); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(0UL, 1UL)); + EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g2); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 1UL); + + make_unpinned(2); + _collection_set->clear(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(2UL)); + EXPECT_EQ(_collection_set->get_old_garbage(), g3); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, unpinned_region_is_middle) { + SKIP_IF_NOT_SHENANDOAH(); + + // Create three old regions with enough garbage to be collected. + size_t g1 = make_garbage_above_collection_threshold(0); + size_t g2 = make_garbage_above_collection_threshold(1); + size_t g3 = make_garbage_above_collection_threshold(2); + + make_pinned(0); + make_pinned(2); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(1UL)); + EXPECT_EQ(_collection_set->get_old_garbage(), g2); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 2UL); + + make_unpinned(0); + make_unpinned(2); + _collection_set->clear(); + _heuristics->prime_collection_set(_collection_set); + + EXPECT_TRUE(collection_set_is(0UL, 2UL)); + EXPECT_EQ(_collection_set->get_old_garbage(), g1 + g3); + EXPECT_EQ(_heuristics->unprocessed_old_collection_candidates(), 0UL); +} + +TEST_VM_F(ShenandoahOldHeuristicTest, all_candidates_are_pinned) { + SKIP_IF_NOT_SHENANDOAH(); + + size_t g1 = make_garbage_above_collection_threshold(0); + size_t g2 = make_garbage_above_collection_threshold(1); + size_t g3 = make_garbage_above_collection_threshold(2); + + make_pinned(0); + make_pinned(1); + make_pinned(2); + _heuristics->prepare_for_old_collections(); + _heuristics->prime_collection_set(_collection_set); + + // In the case when all candidates are pinned, we want to abandon + // this set of mixed collection candidates so that another old collection + // can run. This is meant to defend against "bad" JNI code that permanently + // leaves an old region in the pinned state. + EXPECT_EQ(_collection_set->count(), 0UL); + EXPECT_EQ(old_generation_state(), ShenandoahOldGeneration::FILLING); +} +#undef SKIP_IF_NOT_SHENANDOAH diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index d3de1f871e8..e09004aa58b 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -95,6 +95,7 @@ gc/TestAlwaysPreTouchBehavior.java#G1 8334513 generic-all gc/TestAlwaysPreTouchBehavior.java#Z 8334513 generic-all gc/TestAlwaysPreTouchBehavior.java#Epsilon 8334513 generic-all gc/stress/gclocker/TestExcessGCLockerCollections.java 8229120 generic-all +gc/shenandoah/oom/TestAllocOutOfMemory.java 8344312 linux-ppc64le ############################################################################# diff --git a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java index 6498a394911..8d5fd780811 100644 --- a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java +++ b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -94,6 +95,23 @@ * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive * TestAllocHumongousFragment +*/ + +/* + * @test id=generational + * @summary Make sure Shenandoah can recover from humongous allocation fragmentation + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestAllocHumongousFragment + * + * @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestAllocHumongousFragment */ /* diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java index b10e90b4055..457af294f6f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -99,6 +100,23 @@ * TestAllocIntArrays */ +/* + * @test id=generational + * @summary Acceptance tests: collector can withstand allocation + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestAllocIntArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestAllocIntArrays + */ + /* * @test id=static * @summary Acceptance tests: collector can withstand allocation @@ -147,9 +165,14 @@ public class TestAllocIntArrays { public static void main(String[] args) throws Exception { final int min = 0; final int max = 384 * 1024; + // Each allocated int array is assumed to consume 16 bytes for alignment and header, plus + // an average of 4 * the average number of elements in the array. long count = TARGET_MB * 1024 * 1024 / (16 + 4 * (min + (max - min) / 2)); Random r = Utils.getRandomInstance(); + // Repeatedly, allocate an array of int having between 0 and 384K elements, until we have + // allocated approximately TARGET_MB. The largest allocated array consumes 384K*4 + 16, which is 1.5 M, + // which is well below the heap size of 1g. for (long c = 0; c < count; c++) { sink = new int[min + r.nextInt(max - min)]; } diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java index fa9bb7f0177..1df8f7453f7 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -99,6 +100,35 @@ * TestAllocObjectArrays */ +/* + * @test id=generational + * @summary Acceptance tests: collector can withstand allocation + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahOOMDuringEvacALot + * -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahAllocFailureALot + * -XX:+ShenandoahVerify + * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestAllocObjectArrays + */ + /* * @test id=static * @summary Acceptance tests: collector can withstand allocation @@ -134,6 +164,11 @@ * -XX:+UseShenandoahGC * -XX:-UseTLAB -XX:+ShenandoahVerify * TestAllocObjectArrays + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:-UseTLAB -XX:+ShenandoahVerify + * TestAllocObjectArrays */ import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java index 26dd98ed403..18f0e104ce0 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -93,6 +94,21 @@ * TestAllocObjects */ +/* + * @test id=generational + * @summary Acceptance tests: collector can withstand allocation + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestAllocObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestAllocObjects + */ + /* * @test id=static * @summary Acceptance tests: collector can withstand allocation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyCheckCast.java b/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyCheckCast.java index ecfe5f5836e..c370d03a55e 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyCheckCast.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyCheckCast.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,11 +24,18 @@ */ /* - * @test + * @test id=default * @requires vm.gc.Shenandoah * * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:TieredStopAtLevel=0 -Xmx16m TestArrayCopyCheckCast */ + +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:TieredStopAtLevel=0 -Xmx16m TestArrayCopyCheckCast -XX:ShenandoahGCMode=generational + */ public class TestArrayCopyCheckCast { static class Foo {} diff --git a/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyStress.java b/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyStress.java index 6a00b0f51de..fd6e14145e4 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestArrayCopyStress.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,13 +27,22 @@ import jdk.test.lib.Utils; /* - * @test + * @test id=default * @key randomness * @requires vm.gc.Shenandoah * @library /test/lib * * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:TieredStopAtLevel=0 -Xmx16m TestArrayCopyStress */ + +/* + * @test id=generational + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:TieredStopAtLevel=0 -Xmx16m TestArrayCopyStress + */ public class TestArrayCopyStress { private static final int ARRAY_SIZE = 1000; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java b/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java index b47a818694f..a3dded8d09a 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -62,6 +63,17 @@ * TestDynamicSoftMaxHeapSize */ +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -Dtarget=10000 + * TestDynamicSoftMaxHeapSize + */ + /* * @test id=static * @requires vm.gc.Shenandoah diff --git a/test/hotspot/jtreg/gc/shenandoah/TestEvilSyncBug.java b/test/hotspot/jtreg/gc/shenandoah/TestEvilSyncBug.java index 8765105b5c0..891c0334847 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestEvilSyncBug.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestEvilSyncBug.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,13 +24,23 @@ */ /* - * @test + * @test id=default * @summary Tests for crash/assert when attaching init thread during shutdown * @requires vm.gc.Shenandoah * @library /test/lib * @modules java.base/jdk.internal.misc * java.management - * @run driver/timeout=480 TestEvilSyncBug + * @run driver/timeout=480 TestEvilSyncBug -XX:ShenandoahGCHeuristics=aggressive + */ + +/* + * @test id=generational + * @summary Tests for crash/assert when attaching init thread during shutdown + * @requires vm.gc.Shenandoah + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver/timeout=480 TestEvilSyncBug -XX:ShenandoahGCMode=generational */ import java.util.*; @@ -46,9 +57,10 @@ public class TestEvilSyncBug { static Thread[] hooks = new MyHook[10000]; public static void main(String[] args) throws Exception { - if (args.length > 0) { + if ("test".equals(args[0])) { test(); } else { + String options = args[0]; // Use 1/4 of available processors to avoid over-saturation. int numJobs = Math.max(1, Runtime.getRuntime().availableProcessors() / 4); ExecutorService pool = Executors.newFixedThreadPool(numJobs); @@ -61,7 +73,7 @@ public static void main(String[] args) throws Exception { "-XX:+UnlockExperimentalVMOptions", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCHeuristics=aggressive", + options, "TestEvilSyncBug", "test"); output.shouldHaveExitValue(0); return null; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java b/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java index 1e8aaa32dd6..e66c4e7dc87 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -76,6 +77,24 @@ * TestGCThreadGroups */ +/** + * @test id=generational + * @summary Test Shenandoah GC uses concurrent/parallel threads correctly + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC + * -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 + * -Dtarget=1000 -XX:ShenandoahGCMode=generational + * TestGCThreadGroups + * + * @run main/othervm -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC + * -XX:-UseDynamicNumberOfGCThreads + * -Dtarget=1000 -XX:ShenandoahGCMode=generational + * TestGCThreadGroups + */ + public class TestGCThreadGroups { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation, around 1K cycles to handle diff --git a/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java b/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java index a57d9c7c49a..ffa7027a3c8 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -63,6 +64,11 @@ * TestHeapUncommit * * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 + * -XX:+UseShenandoahGC + * -XX:-UseTLAB -XX:+ShenandoahVerify + * TestHeapUncommit + * + * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive * TestHeapUncommit * @@ -77,11 +83,28 @@ * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive * TestHeapUncommit + */ + +/* + * @test id=generational + * @summary Acceptance tests: collector can withstand allocation + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestHeapUncommit * * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational * -XX:-UseTLAB -XX:+ShenandoahVerify * TestHeapUncommit + * + * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestHeapUncommit */ /* @@ -98,11 +121,11 @@ * * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 -XX:+UseLargePages * -XX:+UseShenandoahGC + * -XX:-UseTLAB -XX:+ShenandoahVerify * TestHeapUncommit * * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 -XX:+UseLargePages * -XX:+UseShenandoahGC - * -XX:-UseTLAB -XX:+ShenandoahVerify * TestHeapUncommit */ diff --git a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java index 6d8b1cb3875..1b607bf96ca 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -72,6 +73,18 @@ * TestJcmdHeapDump */ +/* + * @test id=generational + * @library /test/lib + * @modules jdk.attach/com.sun.tools.attach + * @requires vm.gc.Shenandoah + * + * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -Dtarget=10000 + * TestJcmdHeapDump + */ + /* * @test id=static * @library /test/lib diff --git a/test/hotspot/jtreg/gc/shenandoah/TestLargeObjectAlignment.java b/test/hotspot/jtreg/gc/shenandoah/TestLargeObjectAlignment.java index 893014804e2..9e12a910896 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestLargeObjectAlignment.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestLargeObjectAlignment.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,7 +24,7 @@ */ /* - * @test + * @test id=default * @summary Shenandoah crashes with -XX:ObjectAlignmentInBytes=16 * @key randomness * @requires vm.gc.Shenandoah @@ -36,6 +37,20 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ObjectAlignmentInBytes=16 -XX:TieredStopAtLevel=4 TestLargeObjectAlignment */ +/* + * @test id=generational + * @summary Shenandoah crashes with -XX:ObjectAlignmentInBytes=16 + * @key randomness + * @requires vm.gc.Shenandoah + * @requires vm.bits == "64" + * @library /test/lib + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ObjectAlignmentInBytes=16 -Xint TestLargeObjectAlignment + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ObjectAlignmentInBytes=16 -XX:-TieredCompilation TestLargeObjectAlignment + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ObjectAlignmentInBytes=16 -XX:TieredStopAtLevel=1 TestLargeObjectAlignment + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ObjectAlignmentInBytes=16 -XX:TieredStopAtLevel=4 TestLargeObjectAlignment + */ + import java.util.ArrayList; import java.util.List; import java.util.Random; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java index 9d7738c25ce..569406fa95c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -71,6 +72,16 @@ * TestLotsOfCycles */ +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * + * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -Dtarget=10000 + * TestLotsOfCycles + */ + /* * @test id=static * @requires vm.gc.Shenandoah diff --git a/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java index b30f7fd9ac4..0aad385d4e3 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -55,8 +56,9 @@ public static void main(String[] args) throws Exception { } String[][][] modeHeuristics = new String[][][] { - {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, - {{"passive"}, {"passive"}} + {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, + {{"generational"}, {"adaptive"}}, + {{"passive"}, {"passive"}} }; for (String[][] mh : modeHeuristics) { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java b/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java index 2849b58aa94..58f102298ff 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -45,11 +46,28 @@ public static void testWith(String msg, boolean periodic, String... args) throws OutputAnalyzer output = ProcessTools.executeLimitedTestJava(cmds); output.shouldHaveExitValue(0); - if (periodic && !output.getOutput().contains("Trigger: Time since last GC")) { - throw new AssertionError(msg + ": Should have periodic GC in logs"); + if (periodic) { + output.shouldContain("Trigger: Time since last GC"); } - if (!periodic && output.getOutput().contains("Trigger: Time since last GC")) { - throw new AssertionError(msg + ": Should not have periodic GC in logs"); + if (!periodic) { + output.shouldNotContain("Trigger: Time since last GC"); + } + } + + public static void testGenerational(boolean periodic, String... args) throws Exception { + String[] cmds = Arrays.copyOf(args, args.length + 2); + cmds[args.length] = TestPeriodicGC.class.getName(); + cmds[args.length + 1] = "test"; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(cmds); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + if (periodic) { + output.shouldContain("Trigger (Young): Time since last GC"); + output.shouldContain("Trigger (Old): Time since last GC"); + } else { + output.shouldNotContain("Trigger (Young): Time since last GC"); + output.shouldNotContain("Trigger (Old): Time since last GC"); } } @@ -126,6 +144,26 @@ public static void main(String[] args) throws Exception { "-XX:ShenandoahGCMode=passive", "-XX:ShenandoahGuaranteedGCInterval=1000" ); + + testGenerational(true, + "-Xlog:gc", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", + "-XX:ShenandoahGCMode=generational", + "-XX:ShenandoahGuaranteedYoungGCInterval=1000", + "-XX:ShenandoahGuaranteedOldGCInterval=1500" + ); + + testGenerational(false, + "-Xlog:gc", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", + "-XX:ShenandoahGCMode=generational", + "-XX:ShenandoahGuaranteedYoungGCInterval=0", + "-XX:ShenandoahGuaranteedOldGCInterval=0" + ); } } diff --git a/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java b/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java index 3763a7eea87..9ad81d2d7a7 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -48,6 +48,31 @@ * gc.shenandoah.TestReferenceRefersToShenandoah */ +/* @test id=generational + * @requires vm.gc.Shenandoah + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * gc.shenandoah.TestReferenceRefersToShenandoah + */ + +/* @test id=generational-100 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @modules java.base + * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ShenandoahGarbageThreshold=100 -Xmx100m + * gc.shenandoah.TestReferenceRefersToShenandoah + */ + import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -99,7 +124,11 @@ private static void gcUntilOld(Object o) throws Exception { if (!WB.isObjectInOldGen(o)) { WB.fullGC(); if (!WB.isObjectInOldGen(o)) { - fail("object not promoted by full gc"); + // This is just a warning, because failing would + // be overspecifying for generational shenandoah, + // which need not necessarily promote objects upon + // a full GC. + warn("object not promoted by full gc"); } } } @@ -126,6 +155,10 @@ private static void fail(String msg) throws Exception { throw new RuntimeException(msg); } + private static void warn(String msg) { + System.out.println("Warning: " + msg); + } + private static void expectCleared(Reference ref, String which) throws Exception { expectNotValue(ref, testObjectNone, which); diff --git a/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java b/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java index b7ae0c1c31a..be3d0931aa2 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -36,6 +36,19 @@ * gc.shenandoah.TestReferenceShortcutCycle */ +/* @test id=generational-100 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @modules java.base + * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ShenandoahGarbageThreshold=100 -Xmx100m + * gc.shenandoah.TestReferenceShortcutCycle + */ + import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java b/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java index 0bf7672e7d5..4f0734ad57d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -41,6 +42,21 @@ * TestRefprocSanity */ +/* + * @test id=generational + * @summary Test that null references/referents work fine + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestRefprocSanity + * + * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestRefprocSanity + */ + import java.lang.ref.*; public class TestRefprocSanity { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java b/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java index dfa09a2f5ab..442f0ab8b2d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -46,6 +47,15 @@ * TestRegionSampling */ +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahRegionSampling + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestRegionSampling + */ + /* * @test id=static * @requires vm.gc.Shenandoah diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRegionSamplingLogging.java b/test/hotspot/jtreg/gc/shenandoah/TestRegionSamplingLogging.java new file mode 100644 index 00000000000..0017328b517 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/TestRegionSamplingLogging.java @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com Inc. 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 id=default-rotation + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+ShenandoahRegionSampling -XX:+ShenandoahRegionSampling + * -Xlog:gc+region=trace:region-snapshots-%p.log::filesize=100,filecount=3 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive + * TestRegionSamplingLogging + */ + +/* + * @test id=generational-rotation + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+ShenandoahRegionSampling -XX:+ShenandoahRegionSampling + * -Xlog:gc+region=trace:region-snapshots-%p.log::filesize=100,filecount=3 + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestRegionSamplingLogging + */ +import java.io.File; +import java.util.Arrays; + +public class TestRegionSamplingLogging { + + static final long TARGET_MB = Long.getLong("target", 2_000); // 2 Gb allocation + + static volatile Object sink; + + public static void main(String[] args) throws Exception { + long count = TARGET_MB * 1024 * 1024 / 16; + for (long c = 0; c < count; c++) { + sink = new Object(); + } + + File directory = new File("."); + File[] files = directory.listFiles((dir, name) -> name.startsWith("region-snapshots") && name.endsWith(".log")); + System.out.println(Arrays.toString(files)); + if (files == null || files.length == 0) { + throw new IllegalStateException("Did not find expected snapshot log file."); + } + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java b/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java index 4e3089d0862..f42b14c0d36 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -98,6 +99,26 @@ * TestResizeTLAB */ +/* + * @test id=generational + * @key randomness + * @summary Test that Shenandoah is able to work with(out) resizeable TLABs + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:+ResizeTLAB + * TestResizeTLAB + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:-ResizeTLAB + * TestResizeTLAB + */ + /* * @test id=static * @key randomness diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java index 7be388b7945..d25c8dd0f5e 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -83,6 +84,31 @@ * TestRetainObjects */ +/* + * @test id=generational + * @summary Acceptance tests: collector can deal with retained objects + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify + * TestRetainObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestRetainObjects + */ + /* * @test id=static * @summary Acceptance tests: collector can deal with retained objects diff --git a/test/hotspot/jtreg/gc/shenandoah/TestParallelRefprocSanity.java b/test/hotspot/jtreg/gc/shenandoah/TestShenandoahRegionLogging.java similarity index 53% rename from test/hotspot/jtreg/gc/shenandoah/TestParallelRefprocSanity.java rename to test/hotspot/jtreg/gc/shenandoah/TestShenandoahRegionLogging.java index 474831a4771..81e66c9d0cb 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestParallelRefprocSanity.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestShenandoahRegionLogging.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,28 +23,27 @@ */ /* - * @test - * @summary Test that reference processing works with both parallel and non-parallel variants. + * @test id=rotation * @requires vm.gc.Shenandoah * - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xmx1g -Xms1g TestParallelRefprocSanity - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xmx1g -Xms1g -XX:-ParallelRefProcEnabled TestParallelRefprocSanity - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xmx1g -Xms1g -XX:+ParallelRefProcEnabled TestParallelRefprocSanity + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+ShenandoahRegionSampling + * -Xlog:gc+region=trace:region-snapshots-%p.log::filesize=100,filecount=3 + * -XX:+UseShenandoahGC + * TestShenandoahRegionLogging */ +import java.io.File; -import java.lang.ref.*; - -public class TestParallelRefprocSanity { - - static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation +public class TestShenandoahRegionLogging { + public static void main(String[] args) throws Exception { + System.gc(); - static volatile Object sink; + File directory = new File("."); + File[] files = directory.listFiles((dir, name) -> name.startsWith("region-snapshots")); - public static void main(String[] args) throws Exception { - long count = TARGET_MB * 1024 * 1024 / 32; - for (long c = 0; c < count; c++) { - sink = new WeakReference(new Object()); + // Expect one or more log files when region logging is enabled + if (files.length == 0) { + throw new Error("Expected at least one log file for region sampling data."); } } - } diff --git a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java index 2eaead55a6a..37359b038b3 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -90,6 +91,28 @@ * */ +/* + * @test id=generational + * @summary Acceptance tests: collector can deal with retained objects + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify + * TestSieveObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify + * TestSieveObjects + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestSieveObjects + */ + /* * @test id=static * @summary Acceptance tests: collector can deal with retained objects diff --git a/test/hotspot/jtreg/gc/shenandoah/TestSmallHeap.java b/test/hotspot/jtreg/gc/shenandoah/TestSmallHeap.java index 56f416790f5..6642e5ec10f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestSmallHeap.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestSmallHeap.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,7 +24,7 @@ */ /* - * @test + * @test id=default * @requires vm.gc.Shenandoah * * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestSmallHeap @@ -34,6 +35,17 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xmx4m TestSmallHeap */ +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational TestSmallHeap + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx64m TestSmallHeap + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx32m TestSmallHeap + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx16m TestSmallHeap + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx8m TestSmallHeap + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx4m TestSmallHeap + */ public class TestSmallHeap { public static void main(String[] args) throws Exception { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java b/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java index 9fdb822d48c..8432eff8045 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -65,6 +66,20 @@ * TestStringDedup */ +/* + * @test id=generational + * @summary Test Shenandoah string deduplication implementation + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * @modules java.base/java.lang:open + * java.management + * + * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:StringDeduplicationAgeThreshold=3 + * TestStringDedup + */ + import java.lang.reflect.*; import java.util.*; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java b/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java index 2d928e848ce..1c3c68f7f4f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -42,6 +43,22 @@ * TestStringDedupStress */ +/* + * @test id=generational + * @summary Test Shenandoah string deduplication implementation + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * @modules java.base/java.lang:open + * java.management + * + * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahDegeneratedGC + * -DtargetStrings=3000000 + * TestStringDedupStress + */ + /* * @test id=default * @summary Test Shenandoah string deduplication implementation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java b/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java index 62c9e16f777..0aeafc09bc0 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -75,6 +76,22 @@ * TestStringInternCleanup */ +/* + * @test id=generational + * @summary Check that Shenandoah cleans up interned strings + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestStringInternCleanup + * + * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * TestStringInternCleanup + */ + + public class TestStringInternCleanup { static final int COUNT = 1_000_000; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java b/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java index 82bc74daddc..8af77a07d0b 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -56,6 +57,11 @@ * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact * -XX:+ShenandoahVerify * TestVerifyJCStress + * + * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestVerifyJCStress */ import java.util.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestVerifyLevels.java b/test/hotspot/jtreg/gc/shenandoah/TestVerifyLevels.java index 880a965010f..11321eaa67f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestVerifyLevels.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestVerifyLevels.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,7 +24,7 @@ */ /* - * @test + * @test id=default * @requires vm.gc.Shenandoah * * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=0 TestVerifyLevels @@ -33,6 +34,16 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=4 TestVerifyLevels */ +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=0 TestVerifyLevels + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=1 TestVerifyLevels + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=2 TestVerifyLevels + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=3 TestVerifyLevels + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UnlockDiagnosticVMOptions -Xmx128m -XX:+ShenandoahVerify -XX:ShenandoahVerifyLevel=4 TestVerifyLevels + */ public class TestVerifyLevels { static final long TARGET_MB = Long.getLong("target", 1_000); // 1 Gb allocation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestWithLogLevel.java b/test/hotspot/jtreg/gc/shenandoah/TestWithLogLevel.java index b9214aeb53c..9f0fa13d182 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestWithLogLevel.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestWithLogLevel.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,7 +24,7 @@ */ /* - * @test + * @test id=default * @summary Test Shenandoah with different log levels * @requires vm.gc.Shenandoah * @@ -34,6 +35,17 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms256M -Xmx1G -Xlog:gc*=trace TestWithLogLevel */ +/* + * @test id=generational + * @summary Test Shenandoah with different log levels + * @requires vm.gc.Shenandoah + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms256M -Xmx1G -Xlog:gc*=error TestWithLogLevel + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms256M -Xmx1G -Xlog:gc*=warning TestWithLogLevel + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms256M -Xmx1G -Xlog:gc*=info TestWithLogLevel + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms256M -Xmx1G -Xlog:gc*=debug TestWithLogLevel + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms256M -Xmx1G -Xlog:gc*=trace TestWithLogLevel + */ import java.util.*; public class TestWithLogLevel { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java b/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java index 6dcdf259c6c..4fc44445ed1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,7 +27,8 @@ * @test * @requires vm.gc.Shenandoah * - * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestWrongArrayMember + * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestWrongArrayMember + * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational TestWrongArrayMember */ public class TestWrongArrayMember { diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestClone.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestClone.java index cb8582ee420..2b1340890e1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestClone.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestClone.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -205,7 +206,131 @@ * TestClone */ +/* + * @test id=generational + * @summary Test clone barriers work correctly + * @requires vm.gc.Shenandoah + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -Xint + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:-TieredCompilation + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:TieredStopAtLevel=1 + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:TieredStopAtLevel=4 + * TestClone + */ + +/* + * @test id=generational-verify + * @summary Test clone barriers work correctly + * @requires vm.gc.Shenandoah + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -Xint + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:-TieredCompilation + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:TieredStopAtLevel=1 + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:TieredStopAtLevel=4 + * TestClone + */ + + /* + * @test id=generational-no-coops + * @summary Test clone barriers work correctly + * @requires vm.gc.Shenandoah + * @requires vm.bits == "64" + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -Xint + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:-TieredCompilation + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:TieredStopAtLevel=1 + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:TieredStopAtLevel=4 + * TestClone + */ + /* + * @test id=generational-no-coops-verify + * @summary Test clone barriers work correctly + * @requires vm.gc.Shenandoah + * @requires vm.bits == "64" + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -Xint + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:-TieredCompilation + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:TieredStopAtLevel=1 + * TestClone + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g + * -XX:-UseCompressedOops + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * -XX:TieredStopAtLevel=4 + * TestClone + */ public class TestClone { public static void main(String[] args) throws Exception { diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestReferenceCAS.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestReferenceCAS.java index ecfe46377b4..277da9eeb63 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestReferenceCAS.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestReferenceCAS.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -53,6 +54,32 @@ * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCHeuristics=aggressive -XX:+UseShenandoahGC -XX:-UseCompressedOops -XX:TieredStopAtLevel=4 TestReferenceCAS */ +/* + * @test id=generational + * @summary Shenandoah reference CAS test + * @requires vm.gc.Shenandoah + * @modules java.base/jdk.internal.misc:+open + * + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational TestReferenceCAS + * @run main/othervm -Diters=100 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xint TestReferenceCAS + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:-TieredCompilation TestReferenceCAS + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:TieredStopAtLevel=1 TestReferenceCAS + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:TieredStopAtLevel=4 TestReferenceCAS + */ + +/* + * @test id=generational-no-coops + * @summary Shenandoah reference CAS test + * @requires vm.gc.Shenandoah + * @requires vm.bits == "64" + * @modules java.base/jdk.internal.misc:+open + * + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:-UseCompressedOops TestReferenceCAS + * @run main/othervm -Diters=100 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:-UseCompressedOops -Xint TestReferenceCAS + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:-UseCompressedOops -XX:-TieredCompilation TestReferenceCAS + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:-UseCompressedOops -XX:TieredStopAtLevel=1 TestReferenceCAS + * @run main/othervm -Diters=20000 -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:-UseCompressedOops -XX:TieredStopAtLevel=4 TestReferenceCAS + */ import java.lang.reflect.Field; public class TestReferenceCAS { diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestConcurrentEvac.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestConcurrentEvac.java new file mode 100644 index 00000000000..763c5906f3f --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestConcurrentEvac.java @@ -0,0 +1,185 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +package gc.shenandoah.generational; + +import jdk.test.whitebox.WhiteBox; +import java.util.Random; +import java.util.HashMap; + +/* + * To avoid the risk of false regressions identified by this test, the heap + * size is set artificially high. Though this test is known to run reliably + * in 66 MB heap, the heap size for this test run is currently set to 256 MB. + */ + +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * @summary Confirm that card marking and remembered set scanning do not crash. + * @library /testlibrary /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -Xms256m -Xmx256m + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:NewRatio=1 -XX:+UnlockExperimentalVMOptions + * -XX:ShenandoahGuaranteedGCInterval=3000 + * -XX:-UseDynamicNumberOfGCThreads -XX:-ShenandoahPacing + * gc.shenandoah.generational.TestConcurrentEvac + */ + +public class TestConcurrentEvac { + private static WhiteBox wb = WhiteBox.getWhiteBox(); + + private static final int RANDOM_SEED = 46; + + // Smaller table will cause creation of more old-gen garbage + // as previous entries in table are overwritten with new values. + private static final int TABLE_SIZE = 53; + private static final int MAX_STRING_LENGTH = 47; + private static final int SENTENCE_LENGTH = 5; + + private static Random random = new Random(RANDOM_SEED); + + public static class Node { + + private String name; + + // Each Node instance holds an array containing all substrings of its name + + // This array has entries from 0 .. (name.length() - 1). + // numSubstrings[i] represents the number of substrings that + // correspond to a name of length i+1. + private static int [] numSubstrings; + + static { + // Initialize numSubstrings. + // For a name of length N, there are + // N substrings of length 1 + // N-1 substrings of length 2 + // N-2 substrings of length 3 + // ... + // 1 substring of length N + // Note that: + // numSubstrings[0] = 1 + // numSubstrings[1] = 3 + // numSubstrings[i] = (i + 1) + numSubstrings[i - 1] + numSubstrings = new int[MAX_STRING_LENGTH]; + numSubstrings[0] = 1; + for (int i = 1; i < MAX_STRING_LENGTH; i++) { + numSubstrings[i] = (i + 1) + numSubstrings[i - 1]; + } + } + + private String [] substrings; + private Node [] neighbors; + + public Node(String name) { + this.name = name; + this.substrings = new String[numSubstrings[name.length() - 1]]; + + int index = 0; + for (int substringLength = 1; substringLength <= name.length(); substringLength++) { + for (int offset = 0; offset + substringLength <= name.length(); offset++) { + this.substrings[index++] = name.substring(offset, offset + substringLength); + } + } + } + + public String value() { + return name; + } + + public String arbitrarySubstring() { + int index = TestConcurrentEvac.randomUnsignedInt(substrings.length); + return substrings[index]; + } + } + + + // Return random int between 1 and MAX_STRING_LENGTH inclusive + static int randomStringLength() { + return randomUnsignedInt(MAX_STRING_LENGTH - 1) + 1; + } + + static String randomCharacter() { + int index = randomUnsignedInt(52); + return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".substring(index, index + 1); + } + + static String randomString() { + int length = randomStringLength(); + String result = new String(); // make the compiler work for this garbage... + for (int i = 0; i < length; i++) { + result += randomCharacter(); + } + return result; + } + + static int randomUnsignedInt(int max) { + return random.nextInt(max); + } + + static int randomIndex() { + return randomUnsignedInt(TABLE_SIZE); + } + + public static void main(String args[]) throws Exception { + HashMap table = new HashMap(TABLE_SIZE); + + if (!wb.getBooleanVMFlag("UseShenandoahGC") || !wb.getStringVMFlag("ShenandoahGCMode").equals("generational")) { + throw new IllegalStateException("Command-line options not honored!"); + } + + for (int count = java.lang.Integer.MAX_VALUE/1024; count >= 0; count--) { + int index = randomIndex(); + String name = randomString(); + table.put(index, new Node(name)); + } + + String conclusion = ""; + + for (int i = 0; i < SENTENCE_LENGTH; i++) { + Node node = table.get(randomIndex()); + if (node == null) { + i--; + } else { + String s = node.arbitrarySubstring(); + conclusion += s; + conclusion += " "; + } + } + + conclusion = conclusion.substring(0, conclusion.length() - 1); + + System.out.println("Conclusion is [" + conclusion + "]"); + + if (!conclusion.equals("HN TInkzoLSDFVJYM mQAirHXbbgCJmUWozx DeispxWF MYFKBh")) { + throw new IllegalStateException("Random sequence of words did not end well!"); + } + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java new file mode 100644 index 00000000000..d8471f2db2f --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestOldGrowthTriggers.java @@ -0,0 +1,110 @@ +/* + * Copyright Amazon.com Inc. 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 id=generational + * @summary Test that growth of old-gen triggers old-gen marking + * @requires vm.gc.Shenandoah + * @library /test/lib + * @run driver TestOldGrowthTriggers + */ + +import java.util.*; +import java.math.BigInteger; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class TestOldGrowthTriggers { + + public static void makeOldAllocations() { + // Expect most of the BigInteger entries placed into array to be promoted, and most will eventually become garbage within old + + final int ArraySize = 512 * 1024; // 512K entries + final int BitsInBigInteger = 128; + final int RefillIterations = 64; + BigInteger array[] = new BigInteger[ArraySize]; + Random r = new Random(46); + + for (int i = 0; i < ArraySize; i++) { + array[i] = new BigInteger(BitsInBigInteger, r); + } + + for (int refillCount = 0; refillCount < RefillIterations; refillCount++) { + // Each refill repopulates ArraySize randomly selected elements within array + for (int i = 0; i < ArraySize; i++) { + int replaceIndex = r.nextInt(ArraySize); + int deriveIndex = r.nextInt(ArraySize); + switch (i & 0x3) { + case 0: + // 50% chance of creating garbage + array[replaceIndex] = array[replaceIndex].max(array[deriveIndex]); + break; + case 1: + // 50% chance of creating garbage + array[replaceIndex] = array[replaceIndex].min(array[deriveIndex]); + break; + case 2: + // creates new old BigInteger, releases old BigInteger, + // may create ephemeral data while computing gcd + array[replaceIndex] = array[replaceIndex].gcd(array[deriveIndex]); + break; + case 3: + // creates new old BigInteger, releases old BigInteger + array[replaceIndex] = array[replaceIndex].multiply(array[deriveIndex]); + break; + } + } + } + } + + public static void testOld(String... args) throws Exception { + String[] cmds = Arrays.copyOf(args, args.length + 2); + cmds[args.length] = TestOldGrowthTriggers.class.getName(); + cmds[args.length + 1] = "test"; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(cmds); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("Trigger (Old): Old has overgrown"); + } + + public static void main(String[] args) throws Exception { + if (args.length > 0 && args[0].equals("test")) { + makeOldAllocations(); + return; + } + + testOld("-Xlog:gc", + "-Xms96m", + "-Xmx96m", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", + "-XX:ShenandoahGCMode=generational", + "-XX:ShenandoahGuaranteedYoungGCInterval=0", + "-XX:ShenandoahGuaranteedOldGCInterval=0" + ); + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestSimpleGenerational.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestSimpleGenerational.java new file mode 100644 index 00000000000..d8d5ee9a7b8 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestSimpleGenerational.java @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +package gc.shenandoah.generational; + +import jdk.test.whitebox.WhiteBox; +import java.util.Random; + +/* + * @test id=generational + * @requires vm.gc.Shenandoah + * @summary Confirm that card marking and remembered set scanning do not crash. + * @library /testlibrary /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * gc.shenandoah.generational.TestSimpleGenerational + */ +public class TestSimpleGenerational { + private static WhiteBox WB = WhiteBox.getWhiteBox(); + private static final int RANDOM_SEED = 46; + // Sequence of random numbers should end with same value + private static final int EXPECTED_LAST_RANDOM = 136227050; + + + public static class Node { + private static final int NEIGHBOR_COUNT = 5; + private static final int INT_ARRAY_SIZE = 8; + private static final Random RANDOM = new Random(RANDOM_SEED); + + private int val; + private Object objectField; + + // Each Node instance holds references to two "private" arrays. + // One array holds raw seething bits (primitive integers) and the other + // holds references. + + private int[] intsField; + private Node [] neighbors; + + public Node(int val) { + this.val = val; + this.objectField = new Object(); + this.intsField = new int[INT_ARRAY_SIZE]; + this.intsField[0] = 0xca; + this.intsField[1] = 0xfe; + this.intsField[2] = 0xba; + this.intsField[3] = 0xbe; + this.intsField[4] = 0xba; + this.intsField[5] = 0xad; + this.intsField[6] = 0xba; + this.intsField[7] = 0xbe; + + this.neighbors = new Node[NEIGHBOR_COUNT]; + } + + public int value() { + return val; + } + + // Copy each neighbor of n into a new node's neighbor array. + // Then overwrite arbitrarily selected neighbor with newly allocated + // leaf node. + public static Node upheaval(Node n) { + int firstValue = RANDOM.nextInt(Integer.MAX_VALUE); + Node result = new Node(firstValue); + if (n != null) { + for (int i = 0; i < NEIGHBOR_COUNT; i++) { + result.neighbors[i] = n.neighbors[i]; + } + } + int secondValue = RANDOM.nextInt(Integer.MAX_VALUE); + int overwriteIndex = firstValue % NEIGHBOR_COUNT; + result.neighbors[overwriteIndex] = new Node(secondValue); + return result; + } + } + + public static void main(String args[]) throws Exception { + Node n = null; + + if (!WB.getBooleanVMFlag("UseShenandoahGC") || !WB.getStringVMFlag("ShenandoahGCMode").equals("generational")) { + throw new IllegalStateException("Command-line options not honored!"); + } + + for (int count = 10000; count > 0; count--) { + n = Node.upheaval(n); + } + + System.out.println("Expected Last Random: [" + n.value() + "]"); + if (n.value() != EXPECTED_LAST_RANDOM) { + throw new IllegalStateException("Random number sequence ended badly!"); + } + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/jni/TestJNICritical.java b/test/hotspot/jtreg/gc/shenandoah/jni/TestJNICritical.java index c5bcc623d45..62d9addeaa4 100644 --- a/test/hotspot/jtreg/gc/shenandoah/jni/TestJNICritical.java +++ b/test/hotspot/jtreg/gc/shenandoah/jni/TestJNICritical.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -22,7 +23,7 @@ * */ -/* @test +/* @test id=default * @summary test JNI critical arrays support in Shenandoah * @key randomness * @requires vm.gc.Shenandoah @@ -32,6 +33,16 @@ * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive TestJNICritical */ + /* @test id=generational + * @summary test JNI critical arrays support in Shenandoah + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+ShenandoahVerify TestJNICritical + * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational TestJNICritical + */ + import java.util.Arrays; import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/jni/TestJNIGlobalRefs.java b/test/hotspot/jtreg/gc/shenandoah/jni/TestJNIGlobalRefs.java index d2a34ccb6af..6f211edf343 100644 --- a/test/hotspot/jtreg/gc/shenandoah/jni/TestJNIGlobalRefs.java +++ b/test/hotspot/jtreg/gc/shenandoah/jni/TestJNIGlobalRefs.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -41,6 +42,25 @@ * TestJNIGlobalRefs */ +/* @test id=generational-verify + * @summary Test JNI Global Refs with Shenandoah + * @requires vm.gc.Shenandoah + * + * @run main/othervm/native -Xmx1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestJNIGlobalRefs + */ + +/* @test id=generational + * @summary Test JNI Global Refs with Shenandoah + * @requires vm.gc.Shenandoah + * + * @run main/othervm/native -Xmx1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestJNIGlobalRefs + */ + import java.util.Arrays; import java.util.Random; diff --git a/test/hotspot/jtreg/gc/shenandoah/jni/TestPinnedGarbage.java b/test/hotspot/jtreg/gc/shenandoah/jni/TestPinnedGarbage.java index a6b53a0ee2b..eabe244a9d1 100644 --- a/test/hotspot/jtreg/gc/shenandoah/jni/TestPinnedGarbage.java +++ b/test/hotspot/jtreg/gc/shenandoah/jni/TestPinnedGarbage.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -61,6 +62,22 @@ * TestPinnedGarbage */ +/* @test id=generational + * @summary Test that garbage in the pinned region does not crash VM + * @key randomness + * @requires vm.gc.Shenandoah + * @library /test/lib + * + * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx128m + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * TestPinnedGarbage + * + * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx128m + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestPinnedGarbage + */ + import java.util.Arrays; import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/jvmti/TestHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/jvmti/TestHeapDump.java index b570dad875f..dec5efd1855 100644 --- a/test/hotspot/jtreg/gc/shenandoah/jvmti/TestHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/jvmti/TestHeapDump.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -63,6 +64,46 @@ * -XX:+UseStringDeduplication TestHeapDump */ +/** + * @test id=generational + * @summary Tests JVMTI heap dumps + * @requires vm.gc.Shenandoah + * @requires vm.jvmti + * @compile TestHeapDump.java + * @run main/othervm/native/timeout=300 -agentlib:TestHeapDump + * -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -Xmx128m + * -XX:ShenandoahGCMode=generational + * TestHeapDump + * + */ + +/** + * @test id=no-coops-generational + * @summary Tests JVMTI heap dumps + * @requires vm.gc.Shenandoah + * @requires vm.jvmti + * @requires vm.bits == "64" + * @compile TestHeapDump.java + * @run main/othervm/native/timeout=300 -agentlib:TestHeapDump + * -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -Xmx128m + * -XX:ShenandoahGCMode=generational + * -XX:-UseCompressedOops TestHeapDump + */ + +/** + * @test id=generational-strdedup + * @summary Tests JVMTI heap dumps + * @requires vm.gc.Shenandoah + * @requires vm.jvmti + * @compile TestHeapDump.java + * @run main/othervm/native/timeout=300 -agentlib:TestHeapDump + * -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -Xmx128m + * -XX:ShenandoahGCMode=generational + * -XX:+UseStringDeduplication TestHeapDump + */ import java.lang.ref.Reference; public class TestHeapDump { diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java index 73743aadedc..bd3c4508c7b 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -26,7 +27,7 @@ * @test id=passive * @summary Check that MX notifications are reported for all cycles * @library /test/lib / - * @requires vm.gc.Shenandoah + * @requires vm.gc.Shenandoah & vm.opt.ShenandoahGCMode != "generational" * * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive @@ -43,7 +44,7 @@ * @test id=aggressive * @summary Check that MX notifications are reported for all cycles * @library /test/lib / - * @requires vm.gc.Shenandoah + * @requires vm.gc.Shenandoah & vm.opt.ShenandoahGCMode != "generational" * * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive @@ -55,7 +56,7 @@ * @test id=adaptive * @summary Check that MX notifications are reported for all cycles * @library /test/lib / - * @requires vm.gc.Shenandoah + * @requires vm.gc.Shenandoah & vm.opt.ShenandoahGCMode != "generational" * * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive @@ -67,7 +68,7 @@ * @test id=static * @summary Check that MX notifications are reported for all cycles * @library /test/lib / - * @requires vm.gc.Shenandoah + * @requires vm.gc.Shenandoah & vm.opt.ShenandoahGCMode != "generational" * * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=static @@ -79,7 +80,7 @@ * @test id=compact * @summary Check that MX notifications are reported for all cycles * @library /test/lib / - * @requires vm.gc.Shenandoah + * @requires vm.gc.Shenandoah & vm.opt.ShenandoahGCMode != "generational" * * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact @@ -87,6 +88,18 @@ * TestChurnNotifications */ +/* + * @test id=generational + * @summary Check that MX notifications are reported for all cycles + * @library /test/lib / + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -Dprecise=false -Dmem.pool=Young + * TestChurnNotifications + */ + import java.util.*; import java.util.concurrent.atomic.*; import javax.management.*; @@ -111,6 +124,8 @@ public class TestChurnNotifications { static volatile Object sink; + private static final String POOL_NAME = "Young".equals(System.getProperty("mem.pool")) ? "Shenandoah Young Gen" : "Shenandoah"; + public static void main(String[] args) throws Exception { final long startTimeNanos = System.nanoTime(); @@ -124,8 +139,8 @@ public void handleNotification(Notification n, Object o) { Map mapBefore = info.getGcInfo().getMemoryUsageBeforeGc(); Map mapAfter = info.getGcInfo().getMemoryUsageAfterGc(); - MemoryUsage before = mapBefore.get("Shenandoah"); - MemoryUsage after = mapAfter.get("Shenandoah"); + MemoryUsage before = mapBefore.get(POOL_NAME); + MemoryUsage after = mapAfter.get(POOL_NAME); if ((before != null) && (after != null)) { long diff = before.getUsed() - after.getUsed(); diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java index 56988e2f993..ac350448ba4 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryMXBeans.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,7 +24,7 @@ */ /** - * @test + * @test id=default * @summary Test JMX memory beans * @requires vm.gc.Shenandoah * @modules java.base/jdk.internal.misc @@ -35,6 +36,19 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xms128m -Xmx1g -XX:ShenandoahUncommitDelay=0 TestMemoryMXBeans 128 1024 */ +/** + * @test id=generational + * @summary Test JMX memory beans + * @requires vm.gc.Shenandoah + * @modules java.base/jdk.internal.misc + * java.management + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx1g TestMemoryMXBeans -1 1024 + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms1g -Xmx1g TestMemoryMXBeans 1024 1024 + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms128m -Xmx1g TestMemoryMXBeans 128 1024 + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms1g -Xmx1g -XX:ShenandoahUncommitDelay=0 TestMemoryMXBeans 1024 1024 + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xms128m -Xmx1g -XX:ShenandoahUncommitDelay=0 TestMemoryMXBeans 128 1024 + */ + import java.lang.management.*; import java.util.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryPools.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryPools.java index 7c7cbe67384..50f710a92c0 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryPools.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestMemoryPools.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -23,7 +24,7 @@ */ /** - * @test + * @test id=default * @summary Test JMX memory pools * @requires vm.gc.Shenandoah * @modules java.base/jdk.internal.misc @@ -31,6 +32,15 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xmx1g -Xms1g TestMemoryPools */ +/** + * @test id=generational + * @summary Test JMX memory pools + * @requires vm.gc.Shenandoah + * @modules java.base/jdk.internal.misc + * java.management + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -Xmx1g -Xms1g TestMemoryPools + */ + import java.lang.management.*; import java.util.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java index 2dd9aba4c62..1ae3b690b0d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -83,6 +84,17 @@ * TestPauseNotifications */ +/* + * @test id=generational + * @summary Check that MX notifications are reported for all cycles + * @library /test/lib / + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestPauseNotifications + */ + import java.util.*; import java.util.concurrent.atomic.*; import javax.management.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargeObj.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargeObj.java deleted file mode 100644 index f9e660cb200..00000000000 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargeObj.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018, Red Hat, Inc. 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 - * @summary Test allocation of small object to result OOM, but not to crash JVM - * @requires vm.gc.Shenandoah - * @library /test/lib - * @run driver TestAllocLargeObj - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; - -public class TestAllocLargeObj { - - static final int SIZE = 1 * 1024 * 1024; - static final int COUNT = 16; - - static volatile Object sink; - - public static void work() throws Exception { - Object[] root = new Object[COUNT]; - sink = root; - for (int c = 0; c < COUNT; c++) { - root[c] = new Object[SIZE]; - } - } - - public static void main(String[] args) throws Exception { - if (args.length > 0) { - work(); - return; - } - - { - OutputAnalyzer analyzer = ProcessTools.executeLimitedTestJava( - "-Xmx16m", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - TestAllocLargeObj.class.getName(), - "test"); - - analyzer.shouldHaveExitValue(1); - analyzer.shouldContain("java.lang.OutOfMemoryError: Java heap space"); - } - - { - OutputAnalyzer analyzer = ProcessTools.executeLimitedTestJava( - "-Xmx1g", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - TestAllocLargeObj.class.getName(), - "test"); - - analyzer.shouldHaveExitValue(0); - analyzer.shouldNotContain("java.lang.OutOfMemoryError: Java heap space"); - } - } -} diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargerThanHeap.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargerThanHeap.java deleted file mode 100644 index a87fda5c98d..00000000000 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocLargerThanHeap.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018, Red Hat, Inc. 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 - * @summary Test that allocation of the object larger than heap fails predictably - * @requires vm.gc.Shenandoah - * @library /test/lib - * @run driver TestAllocLargerThanHeap - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; - -public class TestAllocLargerThanHeap { - - static final int SIZE = 16 * 1024 * 1024; - - static volatile Object sink; - - public static void work() throws Exception { - sink = new Object[SIZE]; - } - - public static void main(String[] args) throws Exception { - if (args.length > 0) { - work(); - return; - } - - { - OutputAnalyzer analyzer = ProcessTools.executeLimitedTestJava( - "-Xmx16m", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - TestAllocLargerThanHeap.class.getName(), - "test"); - - analyzer.shouldHaveExitValue(1); - analyzer.shouldContain("java.lang.OutOfMemoryError: Java heap space"); - } - - { - OutputAnalyzer analyzer = ProcessTools.executeLimitedTestJava( - "-Xmx1g", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - TestAllocLargerThanHeap.class.getName(), - "test"); - - analyzer.shouldHaveExitValue(0); - analyzer.shouldNotContain("java.lang.OutOfMemoryError: Java heap space"); - } - } -} diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocOutOfMemory.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocOutOfMemory.java new file mode 100644 index 00000000000..d1a0c81c4d4 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocOutOfMemory.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018, Red Hat, Inc. 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 id=large + * @summary Test allocation of large objects results in OOM, but will not crash the JVM + * @requires vm.gc.Shenandoah + * @library /test/lib + * @run driver TestAllocOutOfMemory large + */ + +/** + * @test id=heap + * @summary Test allocation of a heap-sized object results in OOM, but will not crash the JVM + * @requires vm.gc.Shenandoah + * @library /test/lib + * @run driver TestAllocOutOfMemory heap + */ + +/** + * @test id=small + * @summary Test allocation of small objects results in OOM, but will not crash the JVM + * @requires vm.gc.Shenandoah + * @library /test/lib + * @run driver TestAllocOutOfMemory small + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestAllocOutOfMemory { + + static volatile Object sink; + + public static void work(int size, int count) throws Exception { + Object[] root = new Object[count]; + sink = root; + for (int c = 0; c < count; c++) { + root[c] = new Object[size]; + } + } + + private static void allocate(String size, int multiplier) throws Exception { + switch (size) { + case "large": + work(1024 * 1024, 16 * multiplier); + break; + case "heap": + work(16 * 1024 * 1024, multiplier); + break; + case "small": + work(1, 16 * 1024 * 1024 * multiplier); + break; + default: + throw new IllegalArgumentException("Usage: test [large|small|heap]"); + } + } + + public static void main(String[] args) throws Exception { + if (args.length > 2) { + // Called from test, size is second argument, heap requested is third + String size = args[1]; + long spec_heap = Integer.parseInt(args[2]); + + // The actual heap we get may be larger than the one we asked for + // (particularly in the generational case) + final long actual_heap = Runtime.getRuntime().maxMemory(); + int multiplier = 1; + if (actual_heap > spec_heap) { + // A suitable multiplier is used, so as to allocate an + // amount appropriate to the larger actual heap size than what + // was specified. + multiplier = (int)((actual_heap + spec_heap - 1)/spec_heap); + } + + allocate(size, multiplier); + return; + } + + // Called from jtreg, size is first argument + String size = args[0]; + { + int heap = 16*1024*1024; // -Xmx16m + expectFailure("-Xmx16m", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", + TestAllocOutOfMemory.class.getName(), + "test", size, Integer.toString(heap)); + + expectFailure("-Xmx16m", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", + TestAllocOutOfMemory.class.getName(), + "test", size, Integer.toString(heap)); + } + + { + int heap = 1*1024*1024*1024; // -Xmx1g + expectSuccess("-Xmx1g", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", + TestAllocOutOfMemory.class.getName(), + "test", size, Integer.toString(heap)); + + expectSuccess("-Xmx1g", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", + TestAllocOutOfMemory.class.getName(), + "test", size, Integer.toString(heap)); + } + } + + private static void expectSuccess(String... args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args); + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(0); + analyzer.shouldNotContain("java.lang.OutOfMemoryError: Java heap space"); + } + + private static void expectFailure(String... args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args); + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(1); + analyzer.shouldContain("java.lang.OutOfMemoryError: Java heap space"); + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocSmallObj.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocSmallObj.java deleted file mode 100644 index 572351b498f..00000000000 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestAllocSmallObj.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2018, Red Hat, Inc. 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 - * @summary Test allocation of small object to result OOM, but not to crash JVM - * @requires vm.gc.Shenandoah - * @library /test/lib - * @run driver TestAllocSmallObj - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; - -public class TestAllocSmallObj { - - static final int COUNT = 16 * 1024 * 1024; - - static volatile Object sink; - - public static void work() throws Exception { - Object[] root = new Object[COUNT]; - sink = root; - for (int c = 0; c < COUNT; c++) { - root[c] = new Object(); - } - } - - public static void main(String[] args) throws Exception { - if (args.length > 0) { - work(); - return; - } - - { - OutputAnalyzer analyzer = ProcessTools.executeLimitedTestJava( - "-Xmx16m", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - TestAllocSmallObj.class.getName(), - "test"); - - analyzer.shouldHaveExitValue(1); - analyzer.shouldContain("java.lang.OutOfMemoryError: Java heap space"); - } - - { - OutputAnalyzer analyzer = ProcessTools.executeLimitedTestJava( - "-Xmx1g", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - TestAllocSmallObj.class.getName(), - "test"); - - analyzer.shouldHaveExitValue(0); - analyzer.shouldNotContain("java.lang.OutOfMemoryError: Java heap space"); - } - } -} diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java index 1a3d07bf80d..3fe3f7374b4 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -123,8 +124,9 @@ public static void main(String[] args) throws Exception { } String[][][] modeHeuristics = new String[][][] { - {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, - {{"passive"}, {"passive"}} + {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, + {{"passive"}, {"passive"}}, + {{"generational"}, {"adaptive"}} }; for (String[][] mh : modeHeuristics) { diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestThreadFailure.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestThreadFailure.java index fea5761df2f..49bcc55febc 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestThreadFailure.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestThreadFailure.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -53,9 +54,17 @@ public void run() { public static void main(String[] args) throws Exception { if (args.length > 0) { for (int t = 0; t < COUNT; t++) { + // If we experience OutOfMemoryError during our attempt to instantiate NastyThread, we'll abort + // main and will not print "All good". We'll also report a non-zero termination code. In the + // case that the previously instantiated NastyThread accumulated more than SheanndoahNoProgressThreshold + // unproductive GC cycles before failing, the main thread may not try a Full GC before it experiences + // OutOfMemoryError exception. Thread thread = new NastyThread(); thread.start(); thread.join(); + // Having joined thread, we know the memory consumed by thread is now garbage, and will eventually be + // collected. Some or all of that memory may have been promoted, so we may need to perform a Full GC + // in order to reclaim it quickly. } System.out.println("All good"); return; @@ -73,5 +82,19 @@ public static void main(String[] args) throws Exception { analyzer.shouldContain("java.lang.OutOfMemoryError"); analyzer.shouldContain("All good"); } + + { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xmx32m", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseShenandoahGC", "-XX:ShenandoahGCMode=generational", + TestThreadFailure.class.getName(), + "test"); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(0); + analyzer.shouldContain("java.lang.OutOfMemoryError"); + analyzer.shouldContain("All good"); + } } } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java b/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java index 5422a86a496..70bec36da62 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -44,8 +45,9 @@ enum Mode { } public static void main(String[] args) throws Exception { - testWith("-XX:ShenandoahGCMode=satb", Mode.PRODUCT); - testWith("-XX:ShenandoahGCMode=passive", Mode.DIAGNOSTIC); + testWith("-XX:ShenandoahGCMode=satb", Mode.PRODUCT); + testWith("-XX:ShenandoahGCMode=passive", Mode.DIAGNOSTIC); + testWith("-XX:ShenandoahGCMode=generational", Mode.EXPERIMENTAL); } private static void testWith(String h, Mode mode) throws Exception { diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java index 9bf1e745856..f461bb4ae5c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java @@ -45,11 +45,18 @@ public static void main(String[] args) throws Exception { "ShenandoahStackWatermarkBarrier", }; + String[] generational = { + "ShenandoahCardBarrier" + }; + shouldFailAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=static", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=compact", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); + shouldPassAll("-XX:ShenandoahGCMode=passive", generational); + shouldPassAll("-XX:ShenandoahGCMode=satb", generational); + shouldFailAll("-XX:ShenandoahGCMode=generational", generational); } private static void shouldFailAll(String h, String[] barriers) throws Exception { diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java index ea64d3ee712..411c2b1003d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java @@ -37,14 +37,18 @@ public class TestWrongBarrierEnable { public static void main(String[] args) throws Exception { - String[] concurrent = { - "ShenandoahSATBBarrier", - }; + String[] concurrent = { "ShenandoahSATBBarrier" }; + String[] generational = { "ShenandoahCardBarrier" }; + String[] all = { "ShenandoahSATBBarrier", "ShenandoahCardBarrier" }; + shouldPassAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); shouldPassAll("-XX:ShenandoahGCHeuristics=static", concurrent); shouldPassAll("-XX:ShenandoahGCHeuristics=compact", concurrent); shouldPassAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); + shouldPassAll("-XX:ShenandoahGCMode=generational", all); + shouldFailAll("-XX:ShenandoahGCMode=satb", generational); + shouldFailAll("-XX:ShenandoahGCMode=passive", generational); } private static void shouldFailAll(String h, String[] barriers) throws Exception { @@ -78,5 +82,4 @@ private static void shouldPassAll(String h, String[] barriers) throws Exception output.shouldHaveExitValue(0); } } - } diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 501605cd0f5..0d1a4af9a2a 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -179,6 +180,43 @@ * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ +/* + * @test id=generational + * @key stress + * @library / + * @requires vm.gc.Shenandoah + * @requires vm.flavor == "server" & !vm.emulatedClient + * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. + * + * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + * + * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + */ + + /* + * @test id=generational-deopt-nmethod + * @key stress + * @library / + * @requires vm.gc.Shenandoah + * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. + * + * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline + * -XX:+ShenandoahVerify + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + * + * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline + * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 + */ public class TestGCBasherWithShenandoah { public static void main(String[] args) throws IOException { diff --git a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java index 7b9794ec4db..42f74df080f 100644 --- a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. +* Copyright Amazon.com Inc. 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 @@ -102,6 +103,22 @@ * gc.stress.gcold.TestGCOld 50 1 20 10 10000 */ +/* + * @test id=generational + * @key stress randomness + * @library / /test/lib + * @requires vm.gc.Shenandoah + * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects. + * + * @run main/othervm/timeout=600 -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * gc.stress.gcold.TestGCOld 50 1 20 10 10000 + * + * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * gc.stress.gcold.TestGCOld 50 1 20 10 10000 + */ public class TestGCOldWithShenandoah { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java b/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java index f1b743bfc15..13129552873 100644 --- a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. + * Copyright Amazon.com Inc. 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 @@ -40,6 +41,23 @@ * -XX:+UseShenandoahGC * gc.stress.systemgc.TestSystemGCWithShenandoah 270 */ + +/* + * @test id=generational + * @key stress + * @library / + * @requires vm.gc.Shenandoah + * @summary Stress the Shenandoah GC full GC by allocating objects of different lifetimes concurrently with System.gc(). + * + * @run main/othervm/timeout=300 -Xlog:gc*=info -Xmx512m -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * -XX:+ShenandoahVerify + * gc.stress.systemgc.TestSystemGCWithShenandoah 270 + * + * @run main/othervm/timeout=300 -Xlog:gc*=info -Xmx512m -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * gc.stress.systemgc.TestSystemGCWithShenandoah 270 + */ public class TestSystemGCWithShenandoah { public static void main(String[] args) throws Exception { TestSystemGC.main(args); diff --git a/test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahEvacuationInformationEvent.java b/test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahEvacuationInformationEvent.java new file mode 100644 index 00000000000..33310ab721a --- /dev/null +++ b/test/jdk/jdk/jfr/event/gc/detailed/TestShenandoahEvacuationInformationEvent.java @@ -0,0 +1,113 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ + +package jdk.jfr.event.gc.detailed; + +import java.time.Duration; +import java.util.List; +import java.util.Random; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @bug 8221507 + * @requires vm.hasJFR & vm.gc.Shenandoah + * @key jfr + * @library /test/lib /test/jdk + * @run main/othervm -Xmx64m -XX:+UnlockExperimentalVMOptions -XX:ShenandoahRegionSize=1m -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational jdk.jfr.event.gc.detailed.TestShenandoahEvacuationInformationEvent + */ + +public class TestShenandoahEvacuationInformationEvent { + private final static String EVENT_NAME = EventNames.ShenandoahEvacuationInformation; + + public static void main(String[] args) throws Exception { + final long shenandoahHeapRegionSize = 1024 * 1024; + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + allocate(); + recording.stop(); + + List events = Events.fromRecording(recording); + Asserts.assertFalse(events.isEmpty(), "No events found"); + for (RecordedEvent event : events) { + if (!Events.isEventType(event, EVENT_NAME)) { + continue; + } + System.out.println("Event: " + event); + + long setRegions = Events.assertField(event, "cSetRegions").atLeast(0L).getValue(); + long setUsedAfter = Events.assertField(event, "cSetUsedAfter").atLeast(0L).getValue(); + long setUsedBefore = Events.assertField(event, "cSetUsedBefore").atLeast(setUsedAfter).getValue(); + long regionsFreed = Events.assertField(event, "regionsFreed").atLeast(0L).getValue(); + Events.assertField(event, "collectedOld").atLeast(0L).getValue(); + Events.assertField(event, "collectedYoung").atLeast(0L).getValue(); + + Asserts.assertGreaterThanOrEqual(setRegions, regionsFreed, "setRegions >= regionsFreed"); + Asserts.assertGreaterThanOrEqual(shenandoahHeapRegionSize * setRegions, setUsedAfter, "ShenandoahHeapRegionSize * setRegions >= setUsedAfter"); + Asserts.assertGreaterThanOrEqual(shenandoahHeapRegionSize * setRegions, setUsedBefore, "ShenandoahHeapRegionSize * setRegions >= setUsedBefore"); + + int gcId = Events.assertField(event, "gcId").getValue(); + } + } + + /** + * Allocate memory to trigger garbage collections. + * We want the allocated objects to have different life time, because we want both "young" and "old" objects. + * This is done by keeping the objects in an array and step the current index by a small random number in the loop. + * The loop will continue until we have allocated a fixed number of bytes. + */ + private static void allocate() { + DummyObject[] dummys = new DummyObject[6000]; + + Random r = new Random(0); + long bytesToAllocate = 256 * 1024 * 1024; + int currPos = 0; + while (bytesToAllocate > 0) { + int allocSize = 1000 + (r.nextInt(4000)); + bytesToAllocate -= allocSize; + dummys[currPos] = new DummyObject(allocSize); + + // Skip a few positions to get different duration on the objects. + currPos = (currPos + r.nextInt(20)) % dummys.length; + } + for (int c=0; c Date: Sun, 1 Dec 2024 13:55:47 +0000 Subject: [PATCH 033/171] 8345102: [s390x/ppc] ShowRegistersOnAssertTest.java fails after 8343756 Reviewed-by: mdoerr, mbaesken --- .../ShowRegistersOnAssertTest.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java index d9ccac0a3df..3b038ebd8a0 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/ShowRegistersOnAssertTest.java @@ -63,20 +63,28 @@ private static void do_test(boolean do_assert, // true - assert, false - guarant OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); - // we should have crashed with an internal error. We should definitly NOT have crashed with a segfault + // we should have crashed with an internal error. We should definitely NOT have crashed with a segfault // (which would be a sign that the assert poison page mechanism does not work). output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); output_detail.shouldMatch("# +Internal Error.*"); if (show_registers_on_assert) { // Extract the hs_err_pid file. File hs_err_file = HsErrFileUtils.openHsErrFileFromOutput(output_detail); - Pattern[] pattern = new Pattern[] { Pattern.compile("Registers:"), null }; + Pattern[] pattern = null; if (Platform.isX64()) { - pattern[1] = Pattern.compile("RAX=.*"); + pattern = new Pattern[] { Pattern.compile("Registers:"), Pattern.compile("RAX=.*")}; } else if (Platform.isX86()) { - pattern[1] = Pattern.compile("EAX=.*"); + pattern = new Pattern[] { Pattern.compile("Registers:"), Pattern.compile("EAX=.*")}; } else if (Platform.isAArch64()) { - pattern[1] = Pattern.compile("R0=.*"); + pattern = new Pattern[] { Pattern.compile("Registers:"), Pattern.compile("R0=.*")}; + } else if (Platform.isS390x()) { + pattern = new Pattern[] { Pattern.compile("General Purpose Registers:"), + Pattern.compile("^-{26}$"), + Pattern.compile(" r0 =.*")}; + } else if (Platform.isPPC()) { + pattern = new Pattern[] { Pattern.compile("Registers:"), Pattern.compile("pc =.*")}; + } else { + pattern = new Pattern[] { Pattern.compile("Registers:") }; } // Pattern match the hs_err_pid file. HsErrFileUtils.checkHsErrFileContent(hs_err_file, pattern, false); From c40b570b71793afd1db665cccaab302e53a75510 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Sun, 1 Dec 2024 22:59:54 +0000 Subject: [PATCH 034/171] 8343775: Add since checker tests to the security area modules Reviewed-by: alanb --- .../JavaSecurityJgssCheckSince.java | 30 +++++++++++++++++++ .../JavaSecuritySaslCheckSince.java | 30 +++++++++++++++++++ .../JavaSmartcardioCheckSince.java | 30 +++++++++++++++++++ .../JavaXmlCryptoCheckSince.java | 30 +++++++++++++++++++ .../JdkCryptoCryptokiCheckSince.java | 30 +++++++++++++++++++ .../JavaScriptingCheckSince.java | 30 +++++++++++++++++++ .../JavaScriptingCheckSince.java | 30 +++++++++++++++++++ 7 files changed, 210 insertions(+) create mode 100644 test/jdk/tools/sincechecker/modules/java.security.jgss/JavaSecurityJgssCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/java.security.sasl/JavaSecuritySaslCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/java.smartcardio/JavaSmartcardioCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/java.xml.crypto/JavaXmlCryptoCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.crypto.cryptoki/JdkCryptoCryptokiCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.security.auth/JavaScriptingCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.security.jgss/JavaScriptingCheckSince.java diff --git a/test/jdk/tools/sincechecker/modules/java.security.jgss/JavaSecurityJgssCheckSince.java b/test/jdk/tools/sincechecker/modules/java.security.jgss/JavaSecurityJgssCheckSince.java new file mode 100644 index 00000000000..f3ee99f2084 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java.security.jgss/JavaSecurityJgssCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in java.security.jgss module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker java.security.jgss + */ diff --git a/test/jdk/tools/sincechecker/modules/java.security.sasl/JavaSecuritySaslCheckSince.java b/test/jdk/tools/sincechecker/modules/java.security.sasl/JavaSecuritySaslCheckSince.java new file mode 100644 index 00000000000..dbfdfbbbb43 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java.security.sasl/JavaSecuritySaslCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in java.security.sasl module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker java.security.sasl + */ diff --git a/test/jdk/tools/sincechecker/modules/java.smartcardio/JavaSmartcardioCheckSince.java b/test/jdk/tools/sincechecker/modules/java.smartcardio/JavaSmartcardioCheckSince.java new file mode 100644 index 00000000000..ceb157c699f --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java.smartcardio/JavaSmartcardioCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in java.smartcardio module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker java.smartcardio + */ diff --git a/test/jdk/tools/sincechecker/modules/java.xml.crypto/JavaXmlCryptoCheckSince.java b/test/jdk/tools/sincechecker/modules/java.xml.crypto/JavaXmlCryptoCheckSince.java new file mode 100644 index 00000000000..74888710ca3 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java.xml.crypto/JavaXmlCryptoCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in java.xml.crypto module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker java.xml.crypto + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.crypto.cryptoki/JdkCryptoCryptokiCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.crypto.cryptoki/JdkCryptoCryptokiCheckSince.java new file mode 100644 index 00000000000..83f3740eae1 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.crypto.cryptoki/JdkCryptoCryptokiCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in jdk.crypto.cryptoki module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.crypto.cryptoki + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.security.auth/JavaScriptingCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.security.auth/JavaScriptingCheckSince.java new file mode 100644 index 00000000000..4bbb380d051 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.security.auth/JavaScriptingCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in jdk.security.auth module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.security.auth + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.security.jgss/JavaScriptingCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.security.jgss/JavaScriptingCheckSince.java new file mode 100644 index 00000000000..210274d28eb --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.security.jgss/JavaScriptingCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343775 + * @summary Test for `@since` in jdk.security.jgss module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.security.jgss + */ From f5ebda43709984214a25e23926860fea2ba5819a Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Dec 2024 06:32:43 +0000 Subject: [PATCH 035/171] 8345173: BlockLocationPrinter::print_location misses a ResourceMark Reviewed-by: sjohanss, iwalulya --- src/hotspot/share/gc/shared/locationPrinter.inline.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/gc/shared/locationPrinter.inline.hpp b/src/hotspot/share/gc/shared/locationPrinter.inline.hpp index bb79bf80a5b..2820c1ad5b9 100644 --- a/src/hotspot/share/gc/shared/locationPrinter.inline.hpp +++ b/src/hotspot/share/gc/shared/locationPrinter.inline.hpp @@ -27,6 +27,7 @@ #include "gc/shared/locationPrinter.hpp" +#include "memory/resourceArea.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oopsHierarchy.hpp" @@ -51,6 +52,7 @@ oop BlockLocationPrinter::base_oop_or_null(void* addr) { template bool BlockLocationPrinter::print_location(outputStream* st, void* addr) { + ResourceMark rm; // Check if addr points into Java heap. bool in_heap = CollectedHeapT::heap()->is_in(addr); if (in_heap) { From 1c4c653168bd4f39544eca08f8256a6ac6e4477c Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 2 Dec 2024 08:14:50 +0000 Subject: [PATCH 036/171] 8345247: Deproblemlist test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java Reviewed-by: abhiscxk --- test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java b/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java index 4c29a33ffa0..ce914a4dea3 100644 --- a/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java +++ b/test/jdk/javax/swing/JRadioButton/8075609/bug8075609.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -51,9 +51,8 @@ public static void main(String[] args) throws Throwable { SwingUtilities.invokeAndWait(bug8075609::createAndShowGUI); robot = new Robot(); - Thread.sleep(100); robot.waitForIdle(); - + robot.delay(1000); robot.setAutoDelay(100); // Radio button group tab key test From ac2fede165e0ecbfa51f5cc75a3218c51e3528be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 2 Dec 2024 08:39:47 +0000 Subject: [PATCH 037/171] 8344041: Re-enable external specs page Reviewed-by: erikj, nbenalla, liach --- make/Docs.gmk | 6 ++--- .../doclets/formats/html/HtmlDoclet.java | 4 +-- .../doclets/formats/html/HtmlOptions.java | 26 +------------------ .../doclet/testSpecTag/TestSpecTag.java | 16 +----------- 4 files changed, 5 insertions(+), 47 deletions(-) diff --git a/make/Docs.gmk b/make/Docs.gmk index e6a98c4fbd2..fb2726e6dad 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -107,15 +107,13 @@ JAVA_WARNINGS_ARE_ERRORS ?= -Werror JAVADOC_OPTIONS := -use -keywords -notimestamp \ -encoding ISO-8859-1 -docencoding UTF-8 -breakiterator \ -splitIndex --system none -javafx --expand-requires transitive \ - --override-methods=summary \ - --no-external-specs-page + --override-methods=summary # The reference options must stay stable to allow for comparisons across the # development cycle. REFERENCE_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \ -encoding ISO-8859-1 -breakiterator -splitIndex --system none \ - -html5 -javafx --expand-requires transitive \ - --no-external-specs-page + -html5 -javafx --expand-requires transitive # Should we add DRAFT stamps to the generated javadoc? ifeq ($(VERSION_IS_GA), true) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index 193e1f3a129..fefe0ab191c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -280,9 +280,7 @@ protected void generateOtherFiles(ClassTree classTree) } if (options.createIndex()) { - if (!options.noExternalSpecsPage()){ - writerFactory.newExternalSpecsWriter().buildPage(); - } + writerFactory.newExternalSpecsWriter().buildPage(); writerFactory.newSearchTagsWriter().buildPage(); writerFactory.newSystemPropertiesWriter().buildPage(); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java index f9d1f6f5dd3..10483fb4a4c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -144,13 +144,6 @@ public class HtmlOptions extends BaseOptions { */ private boolean noDeprecatedList = false; - /** - * Argument for command-line option {@code --no-external-spec-page}. - * True if command-line option "--no-external-spec-page" is used. Default value is - * false. - */ - private boolean noExternalSpecsPage = false; - /** * Argument for command-line option {@code -nohelp}. * True if command-line option "-nohelp" is used. Default value is false. @@ -374,14 +367,6 @@ public boolean process(String opt, List args) { } }, - new Hidden(resources, "--no-external-specs-page") { - @Override - public boolean process(String opt, List args) { - noExternalSpecsPage = true; - return true; - } - }, - new Option(resources, "-notree") { @Override public boolean process(String opt, List args) { @@ -749,15 +734,6 @@ public boolean noDeprecatedList() { return noDeprecatedList; } - /** - * Argument for command-line option {@code --no-external-specs-page}. - * True if command-line option "--no-external-specs-page" is used. Default value is - * false. - */ - public boolean noExternalSpecsPage() { - return noExternalSpecsPage; - } - /** * Argument for command-line option {@code -nohelp}. * True if command-line option "-nohelp" is used. Default value is false. diff --git a/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java b/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java index 265dfcb8600..cd92bbe0364 100644 --- a/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSpecTag/TestSpecTag.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6251738 8226279 8297802 8296546 8305407 + * @bug 6251738 8226279 8297802 8305407 * @summary JDK-8226279 javadoc should support a new at-spec tag * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -511,20 +511,6 @@ public void m2() { } .replace("#FILE#", src.resolve("p").resolve("C.java").toString())); } - @Test - public void testSuppressSpecPage(Path base) throws IOException { - Path src = base.resolve("src"); - tb.writeJavaFiles(src, "package p; /** @spec http://example.com label */ public class C { }"); - - javadoc("-d", base.resolve("out").toString(), - "--source-path", src.toString(), - "--no-external-specs-page", - "p"); - checkExit(Exit.OK); - - checkFiles(false, "external-specs.html"); - } - @Test public void testCombo(Path base) throws IOException { for (LinkKind lk : LinkKind.values()) { From dfcbfb5a410592c6d5e54b4f9c1756853683414d Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Mon, 2 Dec 2024 10:25:09 +0000 Subject: [PATCH 038/171] 8345236: RISC-V: Remove revb_h_h_u and revb_h_w_u macro assembler routines Reviewed-by: rehn, fjiang --- .../cpu/riscv/macroAssembler_riscv.cpp | 38 +---------- .../cpu/riscv/macroAssembler_riscv.hpp | 8 +-- src/hotspot/cpu/riscv/templateTable_riscv.cpp | 64 +++++++++++-------- 3 files changed, 43 insertions(+), 67 deletions(-) diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 46e6bc1b534..3ecfd5de725 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -2461,45 +2461,9 @@ void MacroAssembler::load_long_misaligned(Register dst, Address src, Register tm } } -// reverse bytes in halfword in lower 16 bits and zero-extend -// Rd[15:0] = Rs[7:0] Rs[15:8] (zero-extend to 64 bits) -void MacroAssembler::revb_h_h_u(Register Rd, Register Rs, Register tmp) { - if (UseZbb) { - rev8(Rd, Rs); - srli(Rd, Rd, 48); - return; - } - assert_different_registers(Rs, tmp); - assert_different_registers(Rd, tmp); - srli(tmp, Rs, 8); - andi(tmp, tmp, 0xFF); - andi(Rd, Rs, 0xFF); - slli(Rd, Rd, 8); - orr(Rd, Rd, tmp); -} - -// reverse bytes in halfwords in lower 32 bits and zero-extend -// Rd[31:0] = Rs[23:16] Rs[31:24] Rs[7:0] Rs[15:8] (zero-extend to 64 bits) -void MacroAssembler::revb_h_w_u(Register Rd, Register Rs, Register tmp1, Register tmp2) { - if (UseZbb) { - rev8(Rd, Rs); - rori(Rd, Rd, 32); - roriw(Rd, Rd, 16); - zero_extend(Rd, Rd, 32); - return; - } - assert_different_registers(Rs, tmp1, tmp2); - assert_different_registers(Rd, tmp1, tmp2); - srli(tmp2, Rs, 16); - revb_h_h_u(tmp2, tmp2, tmp1); - revb_h_h_u(Rd, Rs, tmp1); - slli(tmp2, tmp2, 16); - orr(Rd, Rd, tmp2); -} - // reverse bytes in lower word, sign-extend // Rd[32:0] = Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24] -void MacroAssembler::revb_w(Register Rd, Register Rs, Register tmp1, Register tmp2) { +void MacroAssembler::revbw(Register Rd, Register Rs, Register tmp1, Register tmp2) { if (UseZbb) { rev8(Rd, Rs); srai(Rd, Rd, 32); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 9c38e8424d7..26a0ed3b0d1 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -912,11 +912,9 @@ class MacroAssembler: public Assembler { void andn(Register Rd, Register Rs1, Register Rs2); void orn(Register Rd, Register Rs1, Register Rs2); - // revb - void revb_h_h_u(Register Rd, Register Rs, Register tmp = t0); // reverse bytes in halfword in lower 16 bits, zero-extend - void revb_h_w_u(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); // reverse bytes in halfwords in lower 32 bits, zero-extend - void revb_w(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2= t1); // reverse bytes in lower word, sign-extend - void revb(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); // reverse bytes in doubleword + // reverse bytes + void revbw(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2= t1); // reverse bytes in lower word, sign-extend + void revb(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); // reverse bytes in doubleword void ror_imm(Register dst, Register src, uint32_t shift, Register tmp = t0); void rolw_imm(Register dst, Register src, uint32_t, Register tmp = t0); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 5ae6805872a..e583579d618 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -658,8 +658,12 @@ void TemplateTable::aload() { } void TemplateTable::locals_index_wide(Register reg) { - __ lhu(reg, at_bcp(2)); - __ revb_h_h_u(reg, reg); // reverse bytes in half-word and zero-extend + assert_different_registers(reg, t1); + // Convert the 16-bit value into native byte-ordering and zero-extend + __ lbu(reg, at_bcp(2)); + __ lbu(t1, at_bcp(3)); + __ slli(reg, reg, 8); + __ orr(reg, reg, t1); __ neg(reg, reg); } @@ -671,8 +675,12 @@ void TemplateTable::wide_iload() { void TemplateTable::wide_lload() { transition(vtos, ltos); - __ lhu(x11, at_bcp(2)); - __ revb_h_h_u(x11, x11); // reverse bytes in half-word and zero-extend + // Convert the 16-bit value into native byte-ordering and zero-extend + __ lbu(x11, at_bcp(2)); + __ lbu(t1, at_bcp(3)); + __ slli(x11, x11, 8); + __ orr(x11, x11, t1); + __ slli(x11, x11, LogBytesPerWord); __ sub(x11, xlocals, x11); __ ld(x10, Address(x11, Interpreter::local_offset_in_bytes(1))); @@ -686,8 +694,12 @@ void TemplateTable::wide_fload() { void TemplateTable::wide_dload() { transition(vtos, dtos); - __ lhu(x11, at_bcp(2)); - __ revb_h_h_u(x11, x11); // reverse bytes in half-word and zero-extend + // Convert the 16-bit value into native byte-ordering and zero-extend + __ lbu(x11, at_bcp(2)); + __ lbu(t1, at_bcp(3)); + __ slli(x11, x11, 8); + __ orr(x11, x11, t1); + __ slli(x11, x11, LogBytesPerWord); __ sub(x11, xlocals, x11); __ fld(f10, Address(x11, Interpreter::local_offset_in_bytes(1))); @@ -1471,12 +1483,14 @@ void TemplateTable::iinc() { void TemplateTable::wide_iinc() { transition(vtos, vtos); - __ lwu(x11, at_bcp(2)); // get constant and index - __ revb_h_w_u(x11, x11); // reverse bytes in half-word (32bit) and zero-extend - __ zero_extend(x12, x11, 16); - __ neg(x12, x12); - __ slli(x11, x11, 32); - __ srai(x11, x11, 48); + // get constant + // Convert the 16-bit value into native byte-ordering and sign-extend + __ lb(x11, at_bcp(4)); + __ lbu(t1, at_bcp(5)); + __ slli(x11, x11, 8); + __ orr(x11, x11, t1); + + locals_index_wide(x12); __ ld(x10, iaddress(x12, t0, _masm)); __ addw(x10, x10, x11); __ sd(x10, iaddress(x12, t0, _masm)); @@ -1625,10 +1639,10 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) { __ lb(x12, at_bcp(1)); __ lbu(t1, at_bcp(2)); __ slli(x12, x12, 8); - __ add(x12, x12, t1); + __ orr(x12, x12, t1); } else { __ lwu(x12, at_bcp(1)); - __ revb_w(x12, x12); + __ revbw(x12, x12); } // Handle all the JSR stuff here, then exit. @@ -1893,8 +1907,8 @@ void TemplateTable::tableswitch() { // load lo & hi __ lwu(x12, Address(x11, BytesPerInt)); __ lwu(x13, Address(x11, 2 * BytesPerInt)); - __ revb_w(x12, x12); - __ revb_w(x13, x13); + __ revbw(x12, x12); + __ revbw(x13, x13); // check against lo & hi __ blt(x10, x12, default_case); __ bgt(x10, x13, default_case); @@ -1905,7 +1919,7 @@ void TemplateTable::tableswitch() { __ profile_switch_case(x10, x11, x12); // continue execution __ bind(continue_execution); - __ revb_w(x13, x13); + __ revbw(x13, x13); __ add(xbcp, xbcp, x13); __ load_unsigned_byte(t0, Address(xbcp)); __ dispatch_only(vtos, /*generate_poll*/true); @@ -1925,7 +1939,7 @@ void TemplateTable::fast_linearswitch() { transition(itos, vtos); Label loop_entry, loop, found, continue_execution; // bswap x10 so we can avoid bswapping the table entries - __ revb_w(x10, x10); + __ revbw(x10, x10); // align xbcp __ la(x9, at_bcp(BytesPerInt)); // btw: should be able to get rid of // this instruction (change offsets @@ -1936,7 +1950,7 @@ void TemplateTable::fast_linearswitch() { // Convert the 32-bit npairs (number of pairs) into native byte-ordering // We can use sign-extension here because npairs must be greater than or // equal to 0 per JVM spec on 'lookupswitch' bytecode. - __ revb_w(x11, x11); + __ revbw(x11, x11); __ j(loop_entry); // table search __ bind(loop); @@ -1957,7 +1971,7 @@ void TemplateTable::fast_linearswitch() { __ profile_switch_case(x11, x10, x9); // continue execution __ bind(continue_execution); - __ revb_w(x13, x13); + __ revbw(x13, x13); __ add(xbcp, xbcp, x13); __ lbu(t0, Address(xbcp, 0)); __ dispatch_only(vtos, /*generate_poll*/true); @@ -2012,7 +2026,7 @@ void TemplateTable::fast_binaryswitch() { // Convert the 32-bit npairs (number of pairs) into native byte-ordering // We can use sign-extension here because npairs must be greater than or // equal to 0 per JVM spec on 'lookupswitch' bytecode. - __ revb_w(j, j); + __ revbw(j, j); // And start Label entry; @@ -2030,7 +2044,7 @@ void TemplateTable::fast_binaryswitch() { // Convert array[h].match to native byte-ordering before compare __ shadd(temp, h, array, temp, 3); __ lwu(temp, Address(temp, 0)); - __ revb_w(temp, temp); + __ revbw(temp, temp); Label L_done, L_greater; __ bge(key, temp, L_greater); @@ -2053,14 +2067,14 @@ void TemplateTable::fast_binaryswitch() { // Convert array[i].match to native byte-ordering before compare __ shadd(temp, i, array, temp, 3); __ lwu(temp, Address(temp, 0)); - __ revb_w(temp, temp); + __ revbw(temp, temp); __ bne(key, temp, default_case); // entry found -> j = offset __ shadd(temp, i, array, temp, 3); __ lwu(j, Address(temp, BytesPerInt)); __ profile_switch_case(i, key, array); - __ revb_w(j, j); + __ revbw(j, j); __ add(temp, xbcp, j); __ load_unsigned_byte(t0, Address(temp, 0)); @@ -2073,7 +2087,7 @@ void TemplateTable::fast_binaryswitch() { __ bind(default_case); __ profile_switch_default(i); __ lwu(j, Address(array, -2 * BytesPerInt)); - __ revb_w(j, j); + __ revbw(j, j); __ add(temp, xbcp, j); __ load_unsigned_byte(t0, Address(temp, 0)); From 0b0f83c01e30587ca2e23b46493bdc7fcb21559f Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 2 Dec 2024 10:30:56 +0000 Subject: [PATCH 039/171] 8345220: Serial: Refactor TenuredGeneration::promotion_attempt_is_safe Reviewed-by: tschatzl, mli --- src/hotspot/share/gc/serial/tenuredGeneration.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index 99031c379d8..a00eb369980 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -378,11 +378,14 @@ void TenuredGeneration::update_counters() { bool TenuredGeneration::promotion_attempt_is_safe(size_t max_promotion_in_bytes) const { size_t available = _the_space->free() + _virtual_space.uncommitted_size(); - size_t av_promo = (size_t)_avg_promoted->padded_average(); - bool res = (available >= av_promo) || (available >= max_promotion_in_bytes); + + size_t avg_promoted = (size_t)_avg_promoted->padded_average(); + size_t promotion_estimate = MIN2(avg_promoted, max_promotion_in_bytes); + + bool res = (promotion_estimate <= available); log_trace(gc)("Tenured: promo attempt is%s safe: available(" SIZE_FORMAT ") %s av_promo(" SIZE_FORMAT "), max_promo(" SIZE_FORMAT ")", - res? "":" not", available, res? ">=":"<", av_promo, max_promotion_in_bytes); + res? "":" not", available, res? ">=":"<", avg_promoted, max_promotion_in_bytes); return res; } From c7be41ee0cf632c0d24f4444afefe2f7ff02f23a Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 2 Dec 2024 11:21:13 +0000 Subject: [PATCH 040/171] 8340133: Add concise usage message to the java executable Reviewed-by: jpai, alanb, ihse, rriggs --- .../classes/sun/launcher/LauncherHelper.java | 9 ++ .../launcher/resources/launcher.properties | 29 +++- src/java.base/share/native/libjli/java.c | 127 ++++++++++-------- 3 files changed, 104 insertions(+), 61 deletions(-) diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index cbcc61e332c..3ba0758585e 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -589,6 +589,15 @@ static void printXUsageMessage(boolean printToStderr) { } } + /** + * Prints the short usage text to the desired output stream. + */ + static void printConciseUsageMessage(boolean printToStderr) { + initOutput(printToStderr); + ostream.println(getLocalizedMessage("java.launcher.opt.concise.header", + File.pathSeparator)); + } + static void initOutput(boolean printToStderr) { ostream = (printToStderr) ? System.err : System.out; } diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index 61ff563cb24..e8dc89161fd 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -26,14 +26,14 @@ # Translators please note do not translate the options themselves java.launcher.opt.header = Usage: {0} [options] [args...]\n\ \ (to execute a class)\n\ -\ or {0} [options] -jar [args...]\n\ +\ or {0} [options] -jar .jar [args...]\n\ \ (to execute a jar file)\n\ \ or {0} [options] -m [/] [args...]\n\ \ {0} [options] --module [/] [args...]\n\ \ (to execute the main class in a module)\n\ -\ or {0} [options] [args]\n\ +\ or {0} [options] .java [args]\n\ \ (to execute a source-file program)\n\n\ -\ Arguments following the main class, source file, -jar ,\n\ +\ Arguments following the main class, source file, -jar .jar,\n\ \ -m or --module / are passed as the arguments to\n\ \ main class.\n\n\ \ where options include:\n\n @@ -46,15 +46,15 @@ java.launcher.opt.footer = \ \ -cp \n\ \ -classpath \n\ \ --class-path \n\ -\ A {0} separated list of directories, JAR archives,\n\ +\ A "{0}"-separated list of directories, JAR archives,\n\ \ and ZIP archives to search for class files.\n\ \ -p \n\ \ --module-path ...\n\ -\ A {0} separated list of elements, each element is a file path\n\ +\ A "{0}"-separated list of elements, each element is a file path\n\ \ to a module or a directory containing modules. Each module is either\n\ \ a modular JAR or an exploded-module directory.\n\ \ --upgrade-module-path ...\n\ -\ A {0} separated list of elements, each element is a file path\n\ +\ A "{0}"-separated list of elements, each element is a file path\n\ \ to a module or a directory containing modules to replace\n\ \ upgradeable modules in the runtime image. Each module is either\n\ \ a modular JAR or an exploded-module directory.\n\ @@ -232,6 +232,23 @@ The following options are macOS specific:\n\ \ -Xdock:icon=\n\ \ override default icon displayed in dock\n\n +# Translators please note do not translate the options themselves +java.launcher.opt.concise.header = Usage: java [java options...] [application arguments...]\n\n\ +\Where is one of:\n\ +\ to execute the main method of a compiled main class\n\ +\ -jar .jar to execute the main class of a JAR archive\n\ +\ -m [/] to execute the main class of a module\n\ +\ .java to compile and execute a source-file program\n\n\ +\Where key java options include:\n\ +\ --class-path \n\ +\ where is a list of directories and JAR archives to search for class files, separated by "{0}"\n\ +\ --module-path \n\ +\ where is a list of directories and JAR archives to search for modules, separated by "{0}"\n\ +\ -version\n\ +\ to print product version to the error stream and exit\n\n\ +\For additional help on usage: java --help\n\ +\For an interactive Java environment: jshell + java.launcher.bad.option=\ \n\ Unrecognized showSettings option: {0}\n\ diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index 3ceef480b47..2d1849fc344 100644 --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -63,11 +63,17 @@ #define USE_STDERR JNI_TRUE /* we usually print to stderr */ #define USE_STDOUT JNI_FALSE +enum HelpKind { + HELP_NONE, + HELP_CONCISE, + HELP_FULL, + HELP_EXTRA +}; + static jboolean printVersion = JNI_FALSE; /* print and exit */ static jboolean showVersion = JNI_FALSE; /* print but continue */ -static jboolean printUsage = JNI_FALSE; /* print and exit*/ +static enum HelpKind printUsageKind = HELP_NONE; /* if not NONE, print specified usage and exit*/ static jboolean printTo = USE_STDERR; /* where to print version/usage */ -static jboolean printXUsage = JNI_FALSE; /* print and exit*/ static jboolean dryRun = JNI_FALSE; /* initialize VM and exit */ static char *showSettings = NULL; /* print but continue */ static jboolean showResolvedModules = JNI_FALSE; @@ -120,7 +126,7 @@ static void TranslateApplicationArgs(int jargc, const char **jargv, int *pargc, static jboolean AddApplicationOptions(int cpathc, const char **cpathv); static void PrintJavaVersion(JNIEnv *env); -static void PrintUsage(JNIEnv* env, jboolean doXUsage); +static void PrintUsage(JNIEnv* env, enum HelpKind printUsageKind); static void ShowSettings(JNIEnv* env, char *optString); static void ShowResolvedModules(JNIEnv* env); static void ListModules(JNIEnv* env); @@ -179,7 +185,7 @@ static jboolean IsWildCardEnabled(); do { \ if (!AC_ok) { \ JLI_ReportErrorMessage(AC_failure_message, AC_questionable_arg); \ - printUsage = JNI_FALSE; \ + printUsageKind = HELP_NONE; \ *pret = 1; \ return JNI_FALSE; \ } \ @@ -189,7 +195,7 @@ static jboolean IsWildCardEnabled(); do { \ if (AC_arg_count < 1) { \ JLI_ReportErrorMessage(AC_failure_message, AC_questionable_arg); \ - printUsage = JNI_TRUE; \ + printUsageKind = HELP_FULL; \ *pret = 1; \ return JNI_TRUE; \ } \ @@ -537,8 +543,8 @@ JavaMain(void* _args) } /* If the user specified neither a class name nor a JAR file */ - if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) { - PrintUsage(env, printXUsage); + if (printUsageKind != HELP_NONE) { + PrintUsage(env, printUsageKind); CHECK_EXCEPTION_LEAVE(1); LEAVE(); } @@ -1235,10 +1241,10 @@ ParseArguments(int *pargc, char ***pargv, } else if (JLI_StrCmp(arg, "-help") == 0 || JLI_StrCmp(arg, "-h") == 0 || JLI_StrCmp(arg, "-?") == 0) { - printUsage = JNI_TRUE; + printUsageKind = HELP_FULL; return JNI_TRUE; } else if (JLI_StrCmp(arg, "--help") == 0) { - printUsage = JNI_TRUE; + printUsageKind = HELP_FULL; printTo = USE_STDOUT; return JNI_TRUE; } else if (JLI_StrCmp(arg, "-version") == 0) { @@ -1256,10 +1262,10 @@ ParseArguments(int *pargc, char ***pargv, } else if (JLI_StrCmp(arg, "--dry-run") == 0) { dryRun = JNI_TRUE; } else if (JLI_StrCmp(arg, "-X") == 0) { - printXUsage = JNI_TRUE; + printUsageKind = HELP_EXTRA; return JNI_TRUE; } else if (JLI_StrCmp(arg, "--help-extra") == 0) { - printXUsage = JNI_TRUE; + printUsageKind = HELP_EXTRA; printTo = USE_STDOUT; return JNI_TRUE; /* @@ -1350,6 +1356,7 @@ ParseArguments(int *pargc, char ***pargv, /* LM_UNKNOWN okay for options that exit */ if (!listModules && !describeModule && !validateModules && !dumpSharedSpaces) { *pret = 1; + printUsageKind = HELP_CONCISE; } } else if (mode == LM_UNKNOWN) { if (!_have_classpath) { @@ -1919,58 +1926,68 @@ DescribeModule(JNIEnv *env, char *optString) * Prints default usage or the Xusage message, see sun.launcher.LauncherHelper.java */ static void -PrintUsage(JNIEnv* env, jboolean doXUsage) +PrintUsage(JNIEnv* env, enum HelpKind printUsageKind) { - jmethodID initHelp, vmSelect, vmSynonym, printHelp, printXUsageMessage; + jmethodID initHelp, vmSelect, vmSynonym; + jmethodID printHelp, printConciseUsageMessage, printXUsageMessage; jstring jprogname, vm1, vm2; int i; jclass cls = GetLauncherHelperClass(env); NULL_CHECK(cls); - if (doXUsage) { - NULL_CHECK(printXUsageMessage = (*env)->GetStaticMethodID(env, cls, - "printXUsageMessage", "(Z)V")); - (*env)->CallStaticVoidMethod(env, cls, printXUsageMessage, printTo); - } else { - NULL_CHECK(initHelp = (*env)->GetStaticMethodID(env, cls, - "initHelpMessage", "(Ljava/lang/String;)V")); - - NULL_CHECK(vmSelect = (*env)->GetStaticMethodID(env, cls, "appendVmSelectMessage", - "(Ljava/lang/String;Ljava/lang/String;)V")); - - NULL_CHECK(vmSynonym = (*env)->GetStaticMethodID(env, cls, - "appendVmSynonymMessage", - "(Ljava/lang/String;Ljava/lang/String;)V")); - - NULL_CHECK(printHelp = (*env)->GetStaticMethodID(env, cls, - "printHelpMessage", "(Z)V")); - - NULL_CHECK(jprogname = (*env)->NewStringUTF(env, _program_name)); - - /* Initialize the usage message with the usual preamble */ - (*env)->CallStaticVoidMethod(env, cls, initHelp, jprogname); - CHECK_EXCEPTION_RETURN(); - - - /* Assemble the other variant part of the usage */ - for (i=1; iNewStringUTF(env, knownVMs[i].name)); - NULL_CHECK(vm2 = (*env)->NewStringUTF(env, knownVMs[i].name+1)); - (*env)->CallStaticVoidMethod(env, cls, vmSelect, vm1, vm2); - CHECK_EXCEPTION_RETURN(); + switch (printUsageKind) { + case HELP_NONE: break; + case HELP_CONCISE: + NULL_CHECK(printConciseUsageMessage = (*env)->GetStaticMethodID(env, cls, + "printConciseUsageMessage", "(Z)V")); + (*env)->CallStaticVoidMethod(env, cls, printConciseUsageMessage, printTo); + break; + case HELP_EXTRA: + NULL_CHECK(printXUsageMessage = (*env)->GetStaticMethodID(env, cls, + "printXUsageMessage", "(Z)V")); + (*env)->CallStaticVoidMethod(env, cls, printXUsageMessage, printTo); + break; + case HELP_FULL: + NULL_CHECK(initHelp = (*env)->GetStaticMethodID(env, cls, + "initHelpMessage", "(Ljava/lang/String;)V")); + + NULL_CHECK(vmSelect = (*env)->GetStaticMethodID(env, cls, "appendVmSelectMessage", + "(Ljava/lang/String;Ljava/lang/String;)V")); + + NULL_CHECK(vmSynonym = (*env)->GetStaticMethodID(env, cls, + "appendVmSynonymMessage", + "(Ljava/lang/String;Ljava/lang/String;)V")); + + NULL_CHECK(printHelp = (*env)->GetStaticMethodID(env, cls, + "printHelpMessage", "(Z)V")); + + NULL_CHECK(jprogname = (*env)->NewStringUTF(env, _program_name)); + + /* Initialize the usage message with the usual preamble */ + (*env)->CallStaticVoidMethod(env, cls, initHelp, jprogname); + CHECK_EXCEPTION_RETURN(); + + + /* Assemble the other variant part of the usage */ + for (i=1; iNewStringUTF(env, knownVMs[i].name)); + NULL_CHECK(vm2 = (*env)->NewStringUTF(env, knownVMs[i].name+1)); + (*env)->CallStaticVoidMethod(env, cls, vmSelect, vm1, vm2); + CHECK_EXCEPTION_RETURN(); + } } - } - for (i=1; iNewStringUTF(env, knownVMs[i].name)); - NULL_CHECK(vm2 = (*env)->NewStringUTF(env, knownVMs[i].alias+1)); - (*env)->CallStaticVoidMethod(env, cls, vmSynonym, vm1, vm2); - CHECK_EXCEPTION_RETURN(); + for (i=1; iNewStringUTF(env, knownVMs[i].name)); + NULL_CHECK(vm2 = (*env)->NewStringUTF(env, knownVMs[i].alias+1)); + (*env)->CallStaticVoidMethod(env, cls, vmSynonym, vm1, vm2); + CHECK_EXCEPTION_RETURN(); + } } - } - /* Complete the usage message and print to stderr*/ - (*env)->CallStaticVoidMethod(env, cls, printHelp, printTo); + /* Complete the usage message and print to stderr*/ + (*env)->CallStaticVoidMethod(env, cls, printHelp, printTo); + break; } return; } From e3b679a2f631bb2c3c9ba3014d9b56c73eef95d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 2 Dec 2024 12:56:49 +0000 Subject: [PATCH 041/171] 8345176: Add tests to verify java.net.Socket constructors close the socket on failure Reviewed-by: dfuchs --- test/jdk/java/net/Socket/CtorFailTest.java | 220 +++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 test/jdk/java/net/Socket/CtorFailTest.java diff --git a/test/jdk/java/net/Socket/CtorFailTest.java b/test/jdk/java/net/Socket/CtorFailTest.java new file mode 100644 index 00000000000..2dd8034ffc0 --- /dev/null +++ b/test/jdk/java/net/Socket/CtorFailTest.java @@ -0,0 +1,220 @@ +/* + * 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. + */ + +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketImpl; +import java.net.SocketImplFactory; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/* + * @test + * @summary verifies that the socket is closed on constructor failures + * @modules java.base/java.net:+open + * @run junit CtorFailTest + */ +class CtorFailTest { + + private static final VarHandle SOCKET_IMPL_FACTORY_HANDLE = createSocketImplFactoryHandle(); + + private static final int DEAD_SERVER_PORT = 0xDEAD; + + private static VarHandle createSocketImplFactoryHandle() { + try { + Field field = Socket.class.getDeclaredField("factory"); + field.setAccessible(true); + return MethodHandles + .privateLookupIn(Socket.class, MethodHandles.lookup()) + .findStaticVarHandle(Socket.class, "factory", SocketImplFactory.class); + } catch (NoSuchFieldException | IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + private static void withSocketImplFactory(SocketImplFactory newFactory, Executable executable) throws Throwable { + SocketImplFactory oldFactory = (SocketImplFactory) SOCKET_IMPL_FACTORY_HANDLE.getAndSet(newFactory); + try { + executable.execute(); + } finally { + SOCKET_IMPL_FACTORY_HANDLE.set(oldFactory); + } + } + + @ParameterizedTest + @MethodSource("socketImpls") + @SuppressWarnings("resource") + void test(MockSocketImpl socketImpl) throws Throwable { + withSocketImplFactory(() -> socketImpl, () -> { + + // Trigger the failure + Exception caughtException = assertThrows(Exception.class, () -> { + // Address and port are mostly ineffective. + // They just need to be _valid enough_ to reach to the point where both `SocketImpl#bind()` and `SocketImpl#connect()` are invoked. + // Failure will be triggered by the injected `SocketImpl`. + InetAddress serverAddress = InetAddress.getLoopbackAddress(); + new Socket(serverAddress, DEAD_SERVER_PORT, null, 0); + }); + + // Run verifications + Exception expectedException = socketImpl.bindException != null + ? socketImpl.bindException + : socketImpl.connectException; + assertSame(expectedException, caughtException); + assertEquals(1, socketImpl.closeInvocationCounter.get()); + + }); + } + + static List socketImpls() { + String exceptionMessage = "intentional test failure"; + IOException checkedException = new IOException(exceptionMessage); + IllegalArgumentException uncheckedException = new IllegalArgumentException(exceptionMessage); + return List.of( + new MockSocketImpl(checkedException, null), + new MockSocketImpl(null, checkedException), + new MockSocketImpl(uncheckedException, null), + new MockSocketImpl(null, uncheckedException)); + } + + private static final class MockSocketImpl extends SocketImpl { + + private final AtomicInteger closeInvocationCounter = new AtomicInteger(0); + + private final Exception bindException; + + private final Exception connectException; + + private MockSocketImpl(Exception bindException, Exception connectException) { + this.bindException = bindException; + this.connectException = connectException; + } + + @Override + protected void create(boolean stream) { + // Do nothing + } + + @Override + protected void bind(InetAddress host, int port) throws IOException { + throwIfPresent(bindException); + } + + @Override + protected void connect(SocketAddress address, int timeoutMillis) throws IOException { + throwIfPresent(connectException); + } + + private void throwIfPresent(Exception exception) throws IOException { + if (exception != null) { + switch (exception) { + case IOException error -> throw error; + case RuntimeException error -> throw error; + default -> throw new IllegalStateException( + "unknown exception type: " + exception.getClass().getCanonicalName()); + } + } + } + + @Override + public String toString() { + record MockSocket(Exception bindException, Exception connectException, int closeInvocationCounter) {} + return new MockSocket(bindException, connectException, closeInvocationCounter.get()).toString(); + } + + // Rest of the `SocketImpl` methods should not be used, hence overriding them to throw `UOE` + + @Override + protected void close() { + closeInvocationCounter.incrementAndGet(); + } + + @Override + protected void connect(String host, int port) { + throw new UnsupportedOperationException(); + } + + @Override + protected void connect(InetAddress address, int port) { + throw new UnsupportedOperationException(); + } + + @Override + protected void listen(int backlog) { + throw new UnsupportedOperationException(); + } + + @Override + protected void accept(SocketImpl impl) { + throw new UnsupportedOperationException(); + + } + + @Override + protected InputStream getInputStream() { + throw new UnsupportedOperationException(); + } + + @Override + protected OutputStream getOutputStream() { + throw new UnsupportedOperationException(); + } + + @Override + protected int available() { + throw new UnsupportedOperationException(); + } + + @Override + protected void sendUrgentData(int data) { + throw new UnsupportedOperationException(); + } + + @Override + public void setOption(int optID, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getOption(int optID) { + throw new UnsupportedOperationException(); + } + + } + +} From 9a48e4d9d2637bf152d6611061a0a0a195cc2caf Mon Sep 17 00:00:00 2001 From: Robbin Ehn Date: Mon, 2 Dec 2024 13:14:20 +0000 Subject: [PATCH 042/171] 8345177: RISC-V: Add gtests for cmpxchg Reviewed-by: fyang, mli --- .../gtest/riscv/test_assembler_riscv.cpp | 158 +++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) diff --git a/test/hotspot/gtest/riscv/test_assembler_riscv.cpp b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp index 152b997b3c8..415f72f9a0f 100644 --- a/test/hotspot/gtest/riscv/test_assembler_riscv.cpp +++ b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp @@ -47,8 +47,7 @@ class CmovTester { _masm.mv(c_rarg0, c_rarg2); _masm.ret(); } - _masm.flush(); - OrderAccess::cross_modify_fence(); + _masm.flush(); // icache invalidate int64_t ret = ((zicond_func)entry)(a0, a1, a2, a3); ASSERT_EQ(ret, result); BufferBlob::free(bb); @@ -106,4 +105,159 @@ TEST_VM(RiscV, cmov) { } } +template +class CmpxchgTester { + public: + typedef TESTSIZE (*cmpxchg_func)(intptr_t addr, TESTSIZE expected, TESTSIZE new_value, TESTSIZE result); + + static TESTSIZE base_cmpxchg(int variant, intptr_t addr, TESTSIZE expected, TESTSIZE new_value, TESTSIZE result, bool boolean_result = false) { + BufferBlob* bb = BufferBlob::create("riscvTest", 128); + CodeBuffer code(bb); + MacroAssembler _masm(&code); + address entry = _masm.pc(); + { + switch(variant) { + default: + _masm.cmpxchg(/*addr*/ c_rarg0, /*expected*/ c_rarg1, /*new_value*/c_rarg2, + ASMSIZE, Assembler::relaxed, Assembler::relaxed, + /*result*/ c_rarg3, boolean_result); + _masm.mv(c_rarg0, c_rarg3); + break; + case 1: + // expected == result + _masm.cmpxchg(/*addr*/ c_rarg0, /*expected*/ c_rarg1, /*new_value*/c_rarg2, + ASMSIZE, Assembler::relaxed, Assembler::relaxed, + /*result*/ c_rarg1, boolean_result); + _masm.mv(c_rarg0, c_rarg1); + break; + case 2: + // new_value == result + _masm.cmpxchg(/*addr*/ c_rarg0, /*expected*/ c_rarg1, /*new_value*/c_rarg2, + ASMSIZE, Assembler::relaxed, Assembler::relaxed, + /*result*/ c_rarg2, boolean_result); + _masm.mv(c_rarg0, c_rarg2); + break; + case 3: + // expected == new_value + _masm.cmpxchg(/*addr*/ c_rarg0, /*expected*/ c_rarg1, /*new_value*/ c_rarg1, + ASMSIZE, Assembler::relaxed, Assembler::relaxed, + /*result*/ c_rarg2, boolean_result); + _masm.mv(c_rarg0, c_rarg2); + break; + + } + _masm.ret(); + } + _masm.flush(); // icache invalidate + TESTSIZE ret = ((cmpxchg_func)entry)(addr, expected, new_value, result); + BufferBlob::free(bb); + return ret; + } +}; + +template +void plain_cmpxchg_test(int variant, TESTSIZE dv, TESTSIZE ex, TESTSIZE nv, TESTSIZE eret, TESTSIZE edata, bool bv) { + TESTSIZE data = dv; + TESTSIZE ret = CmpxchgTester::base_cmpxchg(variant, (intptr_t)&data, ex, nv, /* dummy */ 67, bv); + ASSERT_EQ(ret, eret); + ASSERT_EQ(data, edata); +} + +template +void run_plain_cmpxchg_tests() { + // Normal + plain_cmpxchg_test( 0 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1337 /* return */ , 42 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 0 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 1337 /* return */ , 1337 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 0 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1 /* return */ , 42 /* end value*/, true /* boolean ret*/); + + plain_cmpxchg_test( 0 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 0 /* return */ , 1337 /* end value*/, true /* boolean ret*/); + + // result == expected register + plain_cmpxchg_test( 1 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1337 /* return */ , 42 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 1 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 1337 /* return */ , 1337 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 1 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1 /* return */ , 42 /* end value*/, true /* boolean ret*/); + + plain_cmpxchg_test( 1 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 0 /* return */ , 1337 /* end value*/, true /* boolean ret*/); + + // new_value == result register + plain_cmpxchg_test( 2 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1337 /* return */ , 42 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 2 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 1337 /* return */ , 1337 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 2 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1 /* return */ , 42 /* end value*/, true /* boolean ret*/); + + plain_cmpxchg_test( 2 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 0 /* return */ , 1337 /* end value*/, true /* boolean ret*/); + + // expected == new_value register + plain_cmpxchg_test( 3 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1337 /* return */ , 1337 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 3 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 1337 /* return */ , 1337 /* end value*/, false /* boolean ret*/); + + plain_cmpxchg_test( 3 /* variant */ , 1337 /* start value*/, + 1337 /* expected */, 42 /* new value */, + 1 /* return */ , 1337 /* end value*/, true /* boolean ret*/); + + plain_cmpxchg_test( 3 /* variant */ , 1337 /* start value*/, + 1336 /* expected */, 42 /* new value */, + 0 /* return */ , 1337 /* end value*/, true /* boolean ret*/); +} + +TEST_VM(RiscV, cmpxchg_int64_plain_lr_sc) { + bool zacas = UseZacas; + UseZacas = false; + run_plain_cmpxchg_tests(); + UseZacas = zacas; +} + +TEST_VM(RiscV, cmpxchg_int64_plain_maybe_zacas) { + if (UseZacas) { + run_plain_cmpxchg_tests(); + } +} + +TEST_VM(RiscV, cmpxchg_int32_plain_lr_sc) { + bool zacas = UseZacas; + UseZacas = false; + run_plain_cmpxchg_tests(); + UseZacas = zacas; +} + +TEST_VM(RiscV, cmpxchg_int32_plain_maybe_zacas) { + if (UseZacas) { + run_plain_cmpxchg_tests(); + } +} + #endif // RISCV From b8233989e7605268dda908e6b639ca373789792b Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Mon, 2 Dec 2024 13:56:30 +0000 Subject: [PATCH 043/171] 8345267: Fix memory leak in JVMCIEnv dtor Reviewed-by: simonis, kbarrett --- src/hotspot/share/jvmci/jvmciEnv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index c930910dd44..712745e2a02 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -613,7 +613,7 @@ JVMCIEnv::~JVMCIEnv() { if (_init_error_msg != nullptr) { // The memory allocated in libjvmci was not allocated with os::malloc // so must not be freed with os::free. - ALLOW_C_FUNCTION(::free((void*) _init_error_msg)); + ALLOW_C_FUNCTION(::free, ::free((void*) _init_error_msg);) } if (_init_error != JNI_OK) { return; From d589bafee371c2bd16510f3e3039343331d4c524 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie Date: Mon, 2 Dec 2024 14:22:37 +0000 Subject: [PATCH 044/171] 8345218: Clean out references to windows-x86 in jib profiles Reviewed-by: shade, dholmes, kbarrett, jwaters --- make/conf/jib-profiles.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 25cf15c5f3b..0d906acdfcb 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -241,7 +241,7 @@ var getJibProfilesCommon = function (input, data) { // List of the main profile names used for iteration common.main_profile_names = [ "linux-x64", "linux-x86", "macosx-x64", "macosx-aarch64", - "windows-x64", "windows-x86", "windows-aarch64", + "windows-x64", "windows-aarch64", "linux-aarch64", "linux-arm32", "linux-ppc64le", "linux-s390x", "linux-riscv64" ]; @@ -465,15 +465,6 @@ var getJibProfilesProfiles = function (input, common, data) { configure_args: concat(common.configure_args_64bit), }, - "windows-x86": { - target_os: "windows", - target_cpu: "x86", - build_cpu: "x64", - dependencies: ["devkit", "gtest"], - configure_args: concat(common.configure_args_32bit, - "--enable-deprecated-ports"), - }, - "windows-aarch64": { target_os: "windows", target_cpu: "aarch64", @@ -716,10 +707,6 @@ var getJibProfilesProfiles = function (input, common, data) { platform: "windows-x64", jdk_suffix: "zip", }, - "windows-x86": { - platform: "windows-x86", - jdk_suffix: "zip", - }, "windows-aarch64": { platform: "windows-aarch64", jdk_suffix: "zip", From 1ca764454b1cb296f4aa38a4dfdf3d4abb5c19d6 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie Date: Mon, 2 Dec 2024 15:12:24 +0000 Subject: [PATCH 045/171] 8339480: Build static-jdk image with a statically linked launcher Co-authored-by: Magnus Ihse Bursie Co-authored-by: Jiangli Zhou Reviewed-by: dholmes, erikj, coleenp --- make/Images.gmk | 21 -- make/Main.gmk | 22 +- make/ModuleWrapper.gmk | 19 +- make/StaticLibs.gmk | 191 ++++++++++++++++++ make/autoconf/flags-ldflags.m4 | 9 +- make/autoconf/jdk-options.m4 | 4 + make/autoconf/spec.gmk.template | 3 + make/common/FileUtils.gmk | 23 +++ make/common/JdkNativeCompilation.gmk | 15 +- make/common/modules/LauncherCommon.gmk | 16 +- make/common/native/Link.gmk | 1 + make/common/native/LinkMicrosoft.gmk | 3 +- .../modules/java.desktop/lib/AwtLibraries.gmk | 9 + .../java.desktop/lib/ClientLibraries.gmk | 10 + make/modules/jdk.accessibility/Lib.gmk | 2 + make/modules/jdk.jpackage/Lib.gmk | 5 + src/hotspot/os/linux/os_linux.cpp | 27 ++- src/hotspot/os/posix/os_posix.cpp | 6 + src/hotspot/os/windows/os_windows.cpp | 6 + src/hotspot/share/classfile/classLoader.cpp | 22 ++ src/hotspot/share/classfile/verifier.cpp | 19 +- src/hotspot/share/runtime/os.cpp | 5 + src/hotspot/share/runtime/os.hpp | 3 + src/hotspot/share/utilities/zipLibrary.cpp | 29 ++- .../macosx/native/libjli/java_md_macosx.m | 44 ++-- src/java.base/unix/native/libjli/java_md.c | 52 +++-- .../unix/native/libawt/awt/awt_LoadLibrary.c | 4 +- 27 files changed, 473 insertions(+), 97 deletions(-) create mode 100644 make/StaticLibs.gmk diff --git a/make/Images.gmk b/make/Images.gmk index acdc594b009..5f987a2f71a 100644 --- a/make/Images.gmk +++ b/make/Images.gmk @@ -281,27 +281,6 @@ else endif CMDS_TARGET_SUBDIR := bin -# Param 1 - dir to find debuginfo files in -FindDebuginfoFiles = \ - $(wildcard $(addprefix $1/*, $(DEBUGINFO_SUFFIXES)) \ - $(addprefix $1/*/*, $(DEBUGINFO_SUFFIXES)) \ - $(addprefix $1/*/*/*, $(DEBUGINFO_SUFFIXES))) - -# Pick the correct debug info files to copy, either zipped or not. -ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), true) - DEBUGINFO_SUFFIXES += .diz -else - DEBUGINFO_SUFFIXES := .debuginfo .pdb .map - # On Macosx, if debug symbols have not been zipped, find all files inside *.dSYM - # dirs. - ifeq ($(call isTargetOs, macosx), true) - $(call FillFindCache, \ - $(SUPPORT_OUTPUTDIR)/modules_libs $(SUPPORT_OUTPUTDIR)/modules_cmds) - FindDebuginfoFiles = \ - $(if $(wildcard $1), $(call containing, .dSYM/, $(call FindFiles, $1))) - endif -endif - # Param 1 - either JDK or JRE SetupCopyDebuginfo = \ $(foreach m, $(ALL_$1_MODULES), \ diff --git a/make/Main.gmk b/make/Main.gmk index 152dbf17cd3..ddcc0c7f674 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -454,6 +454,18 @@ $(eval $(call SetupTarget, symbols-image, \ TARGET := symbols, \ )) +$(eval $(call SetupTarget, static-launcher, \ + MAKEFILE := StaticLibs, \ + TARGET := static-launcher, \ + DEPS := hotspot-static-libs static-libs, \ +)) + +$(eval $(call SetupTarget, static-jdk-image, \ + MAKEFILE := StaticLibs, \ + TARGET := static-jdk-image, \ + DEPS := static-exploded-image jdk-image, \ +)) + $(eval $(call SetupTarget, static-libs-image, \ MAKEFILE := StaticLibsImage, \ TARGET := static-libs-image, \ @@ -1086,9 +1098,9 @@ else symbols-image: $(LIBS_TARGETS) $(LAUNCHER_TARGETS) - static-libs-image: hotspot-static-libs $(STATIC_LIBS_TARGETS) + static-libs-image: hotspot-static-libs static-libs - static-libs-graal-image: $(STATIC_LIBS_TARGETS) + static-libs-graal-image: static-libs bootcycle-images: jdk-image @@ -1254,6 +1266,8 @@ ifeq ($(call isTargetOs, macosx), true) legacy-images: mac-legacy-jre-bundle endif +static-exploded-image: static-launcher exploded-image + # These targets build the various documentation images docs-jdk-image: docs-jdk docs-javase-image: docs-javase @@ -1296,7 +1310,7 @@ endif ################################################################################ # all-images builds all our deliverables as images. -all-images: product-images test-image all-docs-images +all-images: product-images static-jdk-image test-image all-docs-images # all-bundles packages all our deliverables as tar.gz bundles. all-bundles: product-bundles test-bundles docs-bundles static-libs-bundles @@ -1309,7 +1323,7 @@ ALL_TARGETS += buildtools hotspot hotspot-libs hotspot-static-libs \ create-buildjdk docs-jdk-api docs-javase-api docs-reference-api docs-jdk \ docs-javase docs-reference docs-javadoc mac-bundles product-images legacy-images \ docs-image docs-javase-image docs-reference-image all-docs-images \ - docs-bundles all-docs-bundles test-image all-images \ + docs-bundles all-docs-bundles test-image all-images static-exploded-image \ all-bundles ################################################################################ diff --git a/make/ModuleWrapper.gmk b/make/ModuleWrapper.gmk index 14298d25a53..e99f34cdd71 100644 --- a/make/ModuleWrapper.gmk +++ b/make/ModuleWrapper.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 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 @@ -45,6 +45,23 @@ TARGETS := # Include the file being wrapped. include $(MAKEFILE_PREFIX).gmk +ifeq ($(MAKEFILE_PREFIX), Lib) + # We need to keep track of what libraries are generated/needed by this + # module. This information is required when doing static linking, to know + # which static library files to include. The variable $(MODULE)_INCLUDED_LIBS is + # added to for each call to SetupJdkLibrary. The file module-included-libs.txt is then + # read in StaticLibs.gmk. + ifneq ($($(MODULE)_JDK_LIBS), ) + LIBLIST := $(SUPPORT_OUTPUTDIR)/modules_static-libs/$(MODULE)/module-included-libs.txt + + $(LIBLIST): $(TARGETS) + $(call MakeDir, $(@D)) + $(ECHO) $($(MODULE)_INCLUDED_LIBS) > $@ + + TARGETS += $(LIBLIST) + endif +endif + # Setup copy rules from the modules directories to the jdk image directory. ifeq ($(call isTargetOs, windows), true) TO_BIN_FILTER := %$(SHARED_LIBRARY_SUFFIX) %.diz %.pdb %.map diff --git a/make/StaticLibs.gmk b/make/StaticLibs.gmk new file mode 100644 index 00000000000..78918c456ee --- /dev/null +++ b/make/StaticLibs.gmk @@ -0,0 +1,191 @@ +# +# 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. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# 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. +# + +default: all + +include $(SPEC) +include MakeBase.gmk + +include CopyFiles.gmk +include Modules.gmk +include modules/LauncherCommon.gmk + +################################################################################ +# +# Create the static java launcher +# +################################################################################ + +STATIC_JDK_IMAGE_DIR := $(IMAGES_OUTPUTDIR)/static-jdk +STATIC_LAUNCHER_OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/static-native/launcher +HOTSPOT_STATIC_LIB_PATH := $(HOTSPOT_OUTPUTDIR)/*/libjvm/objs/static + +ifneq ($(word 2, $(wildcard $(HOTSPOT_STATIC_LIB_PATH))), ) + $(error Cannot perform static linking when building more than one JVM library) +endif + +# Find all modules with static libraries +STATIC_LIB_MODULES := $(patsubst $(SUPPORT_OUTPUTDIR)/modules_static-libs/%, \ + %, $(wildcard $(SUPPORT_OUTPUTDIR)/modules_static-libs/*)) + +# Filter out known broken libraries. This is a temporary measure until +# proper support for these libraries can be provided. +ifeq ($(call isTargetOs, linux), true) + # libsplashscreen has a name conflict with libawt in the function + # BitmapToYXBandedRectangles, so we exclude it for now. + BROKEN_STATIC_LIBS += splashscreen +else ifeq ($(call isTargetOs, macosx), true) + # libosxsecurity has a name conflict with libosxapp in the function + # JavaStringToNSString, so we exclude it for now. + BROKEN_STATIC_LIBS += osxsecurity +else ifeq ($(call isTargetOs, windows), true) + # libsplashscreen has a name conflict with libawt in the function + # BitmapToYXBandedRectangles, so we exclude it for now. + BROKEN_STATIC_LIBS += splashscreen + # libsspi_bridge has name conflicts with sunmscapi + BROKEN_STATIC_LIBS += sspi_bridge + # These libs define DllMain which conflict with Hotspot + BROKEN_STATIC_LIBS += awt dt_shmem dt_socket + # These libs are dependent on any of the above disabled libs + BROKEN_STATIC_LIBS += fontmanager jawt lcms net nio +endif + +$(foreach module, $(STATIC_LIB_MODULES), \ + $(eval LIBS_$(module) := $(filter-out $(BROKEN_STATIC_LIBS), $(shell cat \ + $(SUPPORT_OUTPUTDIR)/modules_static-libs/$(module)/module-included-libs.txt))) \ +) + +STATIC_LIB_FILES := $(foreach module, $(STATIC_LIB_MODULES), \ + $(foreach lib, $(LIBS_$(module)), \ + $(SUPPORT_OUTPUTDIR)/native/$(module)/lib$(lib)/static/$(LIBRARY_PREFIX)$(lib)$(STATIC_LIBRARY_SUFFIX))) + +# Add Hotspot +STATIC_LIB_FILES += $(wildcard $(HOTSPOT_STATIC_LIB_PATH)/$(LIBRARY_PREFIX)jvm$(STATIC_LIBRARY_SUFFIX)) + +# Figure out what external libraries are required to link these static JDK +# libraries. +LIB_FLAGS_FILES := $(addsuffix .lib-flags.txt, $(STATIC_LIB_FILES)) + +# Gather the lib flags from all individual libraries. There are many duplicates, +# so sort and just keep unique instances. On macOS, a common pattern is +# "-framework FooFramework", so we must make sure we keep the two words together. +EXTERNAL_LIBS := $(strip $(shell $(CAT) $(LIB_FLAGS_FILES) | \ + $(SED) -e 's/-framework /-framework_/g' | $(TR) ' ' '\n' | $(SORT) -u | \ + $(SED) -e 's/-framework_/-framework /g')) + +ifeq ($(call isTargetOs, macosx), true) + STATIC_LIBS := $(addprefix -force_load$(SPACE), $(STATIC_LIB_FILES)) + STANDARD_LIBS += -lstdc++ +else ifeq ($(call isTargetOs, linux), true) + STATIC_LIBS := -Wl,--export-dynamic -Wl,--whole-archive $(STATIC_LIB_FILES) -Wl,--no-whole-archive + STANDARD_LIBS := -l:libstdc++.a +else ifeq ($(call isTargetOs, windows), true) + STATIC_LIBS := $(addprefix -wholearchive:, $(STATIC_LIB_FILES)) +else + $(error Unsupported platform) +endif + +$(eval $(call SetupBuildLauncher, java, \ + CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS -DENABLE_ARG_FILES, \ + EXTRA_RCFLAGS := $(JAVA_RCFLAGS), \ + VERSION_INFO_RESOURCE := $(JAVA_VERSION_INFO_RESOURCE), \ + OPTIMIZATION := HIGH, \ + STATIC_LAUNCHER := true, \ + LDFLAGS := $(LDFLAGS_STATIC_JDK), \ + LIBS := $(STATIC_LIBS) $(EXTERNAL_LIBS) $(STANDARD_LIBS), \ + OUTPUT_DIR := $(STATIC_LAUNCHER_OUTPUT_DIR), \ + OBJECT_DIR := $(STATIC_LAUNCHER_OUTPUT_DIR), \ +)) + +$(java): $(STATIC_LIB_FILES) + +TARGETS += $(java) + +JAVA_LAUNCHER := $(BUILD_LAUNCHER_java_TARGET) + +static-launcher: $(java) + +################################################################################ +# +# Create the static-jdk image with the statically built java launcher +# +################################################################################ + +# Until we get proper support in jlink for generating an image with static +# builds, we need to create the image ourselves. We base it on a normal +# dynamically linked JDK image. + +# All these files/dirs should be copied as-is +JDK_IMAGE_COPY_FILES := $(addprefix $(JDK_IMAGE_DIR)/, conf demo include jmods \ + legal man/man1/java.1 release README) + +# We need to copy some files from lib, but not the dynamic libraries themselves +ALL_LIB_FILES := $(call FindFiles, $(JDK_IMAGE_DIR)/lib) + +# Remove all dynamic libraries from the list +JDK_IMAGE_COPY_LIB_FILES := $(filter-out %$(SHARED_LIBRARY_SUFFIX), $(ALL_LIB_FILES)) +# Remove all debug files from the list +ifeq ($(call isTargetOs, macosx), true) + JDK_IMAGE_COPY_LIB_FILES := $(call not-containing, .dSYM, $(JDK_IMAGE_COPY_LIB_FILES)) +else + JDK_IMAGE_COPY_LIB_FILES := $(filter-out %.debuginfo %.pdb %.map, $(JDK_IMAGE_COPY_LIB_FILES)) +endif + +static-jdk-info: + $(call LogWarn, Creating static-jdk image) + +$(eval $(call SetupCopyFiles, copy-from-jdk-image, \ + SRC := $(JDK_IMAGE_DIR), \ + DEST := $(STATIC_JDK_IMAGE_DIR), \ + FILES := $(call FindFiles, $(JDK_IMAGE_COPY_FILES)) \ + $(JDK_IMAGE_COPY_LIB_FILES), \ +)) + +TARGETS += $(copy-from-jdk-image) + +$(copy-from-jdk-image): | static-jdk-info + +$(eval $(call SetupCopyFiles, copy-static-launcher, \ + FILES := $(JAVA_LAUNCHER), \ + DEST := $(STATIC_JDK_IMAGE_DIR)/bin, \ +)) + +TARGETS += $(copy-static-launcher) + +$(eval $(call SetupCopyFiles, copy-static-launcher-debuginfo, \ + SRC := $(STATIC_LAUNCHER_OUTPUT_DIR), \ + DEST := $(STATIC_JDK_IMAGE_DIR)/bin, \ + FILES := $(call FindDebuginfoFiles, $(STATIC_LAUNCHER_OUTPUT_DIR)), \ +)) + +TARGETS += $(copy-static-launcher-debuginfo) + +static-jdk-image: $(copy-from-jdk-image) $(copy-static-launcher) $(copy-static-launcher-debuginfo) + +TARGETS += static-jdk-image + +all: $(TARGETS) + +.PHONY: all static-launcher static-jdk-image diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index 851b3b7a0ef..ffb1f0d6e19 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -192,18 +192,23 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_CPU_DEP], # Export variables according to old definitions, prefix with $2 if present. LDFLAGS_JDK_COMMON="$BASIC_LDFLAGS $BASIC_LDFLAGS_JDK_ONLY \ $OS_LDFLAGS $DEBUGLEVEL_LDFLAGS_JDK_ONLY ${$2EXTRA_LDFLAGS}" - $2LDFLAGS_JDKLIB="$LDFLAGS_JDK_COMMON $BASIC_LDFLAGS_JDK_LIB_ONLY \ + $2LDFLAGS_JDKLIB="$LDFLAGS_JDK_COMMON \ $SHARED_LIBRARY_FLAGS $REPRODUCIBLE_LDFLAGS $FILE_MACRO_LDFLAGS" $2LDFLAGS_JDKEXE="$LDFLAGS_JDK_COMMON $EXECUTABLE_LDFLAGS \ ${$1_CPU_EXECUTABLE_LDFLAGS} $REPRODUCIBLE_LDFLAGS $FILE_MACRO_LDFLAGS" + $2LDFLAGS_STATIC_JDK="$BASIC_LDFLAGS $BASIC_LDFLAGS_JVM_ONLY \ + $OS_LDFLAGS ${$2EXTRA_LDFLAGS} $REPRODUCIBLE_LDFLAGS $FILE_MACRO_LDFLAGS" + $2JVM_LDFLAGS="$BASIC_LDFLAGS $BASIC_LDFLAGS_JVM_ONLY $OS_LDFLAGS $OS_LDFLAGS_JVM_ONLY \ - $DEBUGLEVEL_LDFLAGS $DEBUGLEVEL_LDFLAGS_JVM_ONLY $BASIC_LDFLAGS_ONLYCXX \ + $DEBUGLEVEL_LDFLAGS $DEBUGLEVEL_LDFLAGS_JVM_ONLY \ ${$1_CPU_LDFLAGS} ${$1_CPU_LDFLAGS_JVM_ONLY} ${$2EXTRA_LDFLAGS} \ $REPRODUCIBLE_LDFLAGS $FILE_MACRO_LDFLAGS" AC_SUBST($2LDFLAGS_JDKLIB) AC_SUBST($2LDFLAGS_JDKEXE) + AC_SUBST($2LDFLAGS_STATIC_JDK) + AC_SUBST($2JVM_LDFLAGS) ]) diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index cf8a856de96..61638ce5a2c 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -369,6 +369,7 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_CODE_COVERAGE], CXXFLAGS_JDKEXE="$CXXFLAGS_JDKEXE $GCOV_CFLAGS" LDFLAGS_JDKLIB="$LDFLAGS_JDKLIB $GCOV_LDFLAGS" LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE $GCOV_LDFLAGS" + LDFLAGS_STATIC_JDK="$LDFLAGS_STATIC_JDK $GCOV_LDFLAGS" ]) AC_SUBST(GCOV_ENABLED) @@ -463,6 +464,7 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_ADDRESS_SANITIZER], CXXFLAGS_JDKEXE="$CXXFLAGS_JDKEXE $ASAN_CFLAGS" LDFLAGS_JDKLIB="$LDFLAGS_JDKLIB $ASAN_LDFLAGS" LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE $ASAN_LDFLAGS" + LDFLAGS_STATIC_JDK="$LDFLAGS_STATIC_JDK $ASAN_LDFLAGS" ]) AC_SUBST(ASAN_ENABLED) ]) @@ -496,6 +498,7 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_LEAK_SANITIZER], CXXFLAGS_JDKEXE="$CXXFLAGS_JDKEXE $LSAN_CFLAGS" LDFLAGS_JDKLIB="$LDFLAGS_JDKLIB $LSAN_LDFLAGS" LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE $LSAN_LDFLAGS" + LDFLAGS_STATIC_JDK="$LDFLAGS_STATIC_JDK $LSAN_LDFLAGS" ]) AC_SUBST(LSAN_ENABLED) ]) @@ -538,6 +541,7 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER], CXXFLAGS_JDKEXE="$CXXFLAGS_JDKEXE $UBSAN_CFLAGS" LDFLAGS_JDKLIB="$LDFLAGS_JDKLIB $UBSAN_LDFLAGS" LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE $UBSAN_LDFLAGS" + LDFLAGS_STATIC_JDK="$LDFLAGS_STATIC_JDK $UBSAN_LDFLAGS" ]) if test "x$UBSAN_ENABLED" = xfalse; then UBSAN_CFLAGS="" diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template index bcd54058c28..18770c6d360 100644 --- a/make/autoconf/spec.gmk.template +++ b/make/autoconf/spec.gmk.template @@ -559,6 +559,9 @@ LDFLAGS_JDKLIB := @LDFLAGS_JDKLIB@ # LDFLAGS used to link the jdk native launchers (C-code) LDFLAGS_JDKEXE := @LDFLAGS_JDKEXE@ +# LDFLAGS used to link the static jdk library +LDFLAGS_STATIC_JDK := @LDFLAGS_STATIC_JDK@ + # LDFLAGS specific to C++ linking. LDFLAGS_CXX_JDK := @LDFLAGS_CXX_JDK@ diff --git a/make/common/FileUtils.gmk b/make/common/FileUtils.gmk index d3cc4872ebb..d546ab94a58 100644 --- a/make/common/FileUtils.gmk +++ b/make/common/FileUtils.gmk @@ -307,3 +307,26 @@ ifeq ($(DISABLE_CACHE_FIND), true) else FindFiles = $(CacheFindFiles) endif + +# Find native debuginfo files in a directory +# +# Param 1 - dir to find debuginfo files in +FindDebuginfoFiles = \ + $(wildcard $(addprefix $1/*, $(DEBUGINFO_SUFFIXES)) \ + $(addprefix $1/*/*, $(DEBUGINFO_SUFFIXES)) \ + $(addprefix $1/*/*/*, $(DEBUGINFO_SUFFIXES))) + +# Pick the correct debug info files to copy, either zipped or not. +ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), true) + DEBUGINFO_SUFFIXES += .diz +else + DEBUGINFO_SUFFIXES := .debuginfo .pdb .map + # On Macosx, if debug symbols have not been zipped, find all files inside *.dSYM + # dirs. + ifeq ($(call isTargetOs, macosx), true) + $(call FillFindCache, \ + $(SUPPORT_OUTPUTDIR)/modules_libs $(SUPPORT_OUTPUTDIR)/modules_cmds) + FindDebuginfoFiles = \ + $(if $(wildcard $1), $(call containing, .dSYM/, $(call FindFiles, $1))) + endif +endif diff --git a/make/common/JdkNativeCompilation.gmk b/make/common/JdkNativeCompilation.gmk index 52b6dea6725..ca0f1429c61 100644 --- a/make/common/JdkNativeCompilation.gmk +++ b/make/common/JdkNativeCompilation.gmk @@ -275,6 +275,8 @@ JDK_RCFLAGS=$(RCFLAGS) \ # and EXTRA_HEADER_DIRS will be added. # JDK_LIBS_ or JDK_LIBS_ -- additional JDK_LIBS for the given OS # or OS type only +# ONLY_EXPORTED -- if true, this library will be flagged as not +# to be included for this module when building static libs # EXTRA_RCFLAGS -- additional RCFLAGS to append. # RC_FILEDESC -- override the default FILEDESC for Windows version.rc # DEFAULT_LIBCXX -- if false, do not add LIBCXX to LIBS for C++ compilations @@ -303,6 +305,15 @@ define SetupJdkNativeCompilationBody $1_RC_FTYPE := 0x2L endif + ifneq ($$(MODULE), ) + # Record the fact that this native library is part of the current module + # (unless told otherwise). This variable stores information about all + # created libraries, and is read by ModuleWrapper. + ifneq ($$($1_ONLY_EXPORTED), true) + $$(MODULE)_INCLUDED_LIBS += $$($1_NAME) + endif + endif + ifeq ($$($1_OUTPUT_DIR), ) ifneq ($$(MODULE), ) ifeq ($$($1_TYPE), STATIC_LIBRARY) @@ -422,10 +433,10 @@ define SetupJdkNativeCompilationBody ifneq ($$($1_DEFAULT_LDFLAGS), false) ifeq ($$($1_TYPE), EXECUTABLE) # Set the default flags first to be able to override - $1_LDFLAGS := $$(filter-out $$($1_LDFLAGS_FILTER_OUT), $$(LDFLAGS_JDKEXE)) $$($1_LDFLAGS) + $1_LDFLAGS := $$(filter-out $$($1_LDFLAGS_FILTER_OUT), $$(LDFLAGS_JDKEXE) $$($1_LDFLAGS)) else # Set the default flags first to be able to override - $1_LDFLAGS := $$(filter-out $$($1_LDFLAGS_FILTER_OUT), $$(LDFLAGS_JDKLIB)) $$($1_LDFLAGS) + $1_LDFLAGS := $$(filter-out $$($1_LDFLAGS_FILTER_OUT), $$(LDFLAGS_JDKLIB) $$($1_LDFLAGS)) endif endif diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index b522df076f3..77f39457b4c 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -62,6 +62,7 @@ JAVA_MANIFEST := $(TOPDIR)/src/java.base/windows/native/launcher/java.manifest # OPTIMIZATION Override default optimization level (LOW) # OUTPUT_DIR Override default output directory # VERSION_INFO_RESOURCE Override default Windows resource file +# STATIC_LAUNCHER If true, will use settings for building a static launcher SetupBuildLauncher = $(NamedParamsMacroTemplate) define SetupBuildLauncherBody # Setup default values (unless overridden) @@ -120,6 +121,15 @@ define SetupBuildLauncherBody $1_EXTRA_FILES += $(TOPDIR)/make/data/lsan/lsan_default_options.c endif + ifneq ($$($1_STATIC_LAUNCHER), true) + $1_JDK_LIBS := java.base:libjli + $1_JDK_LIBS_windows := java.base:libjava + else + ifneq ($(findstring $(TOOLCHAIN_TYPE), gcc clang), ) + $1_LDFLAGS_FILTER_OUT := -Wl$(COMMA)--exclude-libs$(COMMA)ALL + endif + endif + ############################################################################## ## Build launcher "$1" ############################################################################## @@ -140,8 +150,9 @@ define SetupBuildLauncherBody LDFLAGS := $$($1_LDFLAGS), \ LDFLAGS_linux := $$(call SET_EXECUTABLE_ORIGIN,/../lib), \ LDFLAGS_macosx := $$(call SET_EXECUTABLE_ORIGIN,/../lib), \ - JDK_LIBS := java.base:libjli, \ - JDK_LIBS_windows := java.base:libjava, \ + LDFLAGS_FILTER_OUT := $$($1_LDFLAGS_FILTER_OUT), \ + JDK_LIBS := $$($1_JDK_LIBS), \ + JDK_LIBS_windows := $$($1_JDK_LIBS_windows), \ LIBS := $$($1_LIBS), \ LIBS_unix := $(LIBZ_LIBS), \ LIBS_linux := $(LIBDL) -lpthread, \ @@ -150,6 +161,7 @@ define SetupBuildLauncherBody -framework Cocoa \ -framework Security, \ OUTPUT_DIR := $$($1_OUTPUT_DIR), \ + OBJECT_DIR := $$($1_OBJECT_DIR), \ VERSIONINFO_RESOURCE := $$($1_VERSION_INFO_RESOURCE), \ EXTRA_RCFLAGS := $$($1_EXTRA_RCFLAGS), \ MANIFEST := $(JAVA_MANIFEST), \ diff --git a/make/common/native/Link.gmk b/make/common/native/Link.gmk index 23977e954ca..1461f7302dc 100644 --- a/make/common/native/Link.gmk +++ b/make/common/native/Link.gmk @@ -119,6 +119,7 @@ define CreateStaticLibrary $(if $$($1_LINK_OBJS_RELATIVE), $$(CD) $$(OUTPUTDIR) ; ) \ $$($1_AR) $$(ARFLAGS) -r -cs $$($1_TARGET) \ $$($1_AR_OBJ_ARG) $$($1_RES)) + $$(ECHO) $$(strip $$($1_LIBS) $$($1_EXTRA_LIBS)) > $$($1_TARGET).lib-flags.txt endef ################################################################################ diff --git a/make/common/native/LinkMicrosoft.gmk b/make/common/native/LinkMicrosoft.gmk index cb457034a7e..7c895a9507d 100644 --- a/make/common/native/LinkMicrosoft.gmk +++ b/make/common/native/LinkMicrosoft.gmk @@ -54,7 +54,8 @@ define CreateStaticLibraryMicrosoft $$(call MakeDir, $$($1_OUTPUT_DIR) $$($1_SYMBOLS_DIR)) $$(call ExecuteWithLog, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_run_lib, \ $$($1_LIB) -nologo $$(LIBFLAGS) -out:$$($1_TARGET) \ - $$($1_LD_OBJ_ARG) $$($1_RES)) + $$($1_LD_OBJ_ARG)) + $$(ECHO) $$(strip $$($1_LIBS) $$($1_EXTRA_LIBS)) > $$($1_TARGET).lib-flags.txt endef ################################################################################ diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk index 90eda1c4e64..5ba7c819008 100644 --- a/make/modules/java.desktop/lib/AwtLibraries.gmk +++ b/make/modules/java.desktop/lib/AwtLibraries.gmk @@ -168,10 +168,16 @@ ifeq ($(call isTargetOs, windows macosx), false) # static libraries cause linking errors due to duplicate symbols. LIBAWT_HEADLESS_STATIC_EXCLUDE_OBJS := systemScale.o + ifneq ($(ENABLE_HEADLESS_ONLY), true) + # We cannot link with both awt_headless and awt_xawt at the same time + LIBAWT_HEADLESS_ONLY_EXPORTED := true + endif + $(eval $(call SetupJdkLibrary, BUILD_LIBAWT_HEADLESS, \ NAME := awt_headless, \ EXTRA_SRC := $(LIBAWT_HEADLESS_EXTRA_SRC), \ EXCLUDES := medialib, \ + ONLY_EXPORTED := $(LIBAWT_HEADLESS_ONLY_EXPORTED), \ OPTIMIZATION := LOW, \ CFLAGS := -DHEADLESS=true $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) \ $(X_CFLAGS), \ @@ -308,6 +314,8 @@ ifeq ($(call isTargetOs, macosx), true) LIBAWT_LWAWT_EXCLUDE_FILES := fontpath.c awt_Font.c X11Color.c LIBAWT_LWAWT_EXCLUDES := $(TOPDIR)/src/$(MODULE)/unix/native/common/awt/medialib + LIBAWT_LWAWT_STATIC_EXCLUDE_OBJS := systemScale.o + $(eval $(call SetupJdkLibrary, BUILD_LIBAWT_LWAWT, \ NAME := awt_lwawt, \ EXTRA_SRC := $(LIBAWT_LWAWT_EXTRA_SRC), \ @@ -346,6 +354,7 @@ ifeq ($(call isTargetOs, macosx), true) -framework OpenGL \ -framework QuartzCore \ -framework Security, \ + STATIC_LIB_EXCLUDE_OBJS := $(LIBAWT_LWAWT_STATIC_EXCLUDE_OBJS), \ )) TARGETS += $(BUILD_LIBAWT_LWAWT) diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk index 31323021780..87f6e2e5099 100644 --- a/make/modules/java.desktop/lib/ClientLibraries.gmk +++ b/make/modules/java.desktop/lib/ClientLibraries.gmk @@ -155,6 +155,9 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) ifeq ($(USE_EXTERNAL_LIBJPEG), false) LIBSPLASHSCREEN_EXTRA_SRC += libjavajpeg + LIBJAVA_JPEG_OBJS := $(sort $(patsubst %.c,%.o, $(filter-out imageioJPEG.c, \ + $(notdir $(wildcard $(TOPDIR)/src/java.desktop/share/native/libjavajpeg/*.c))))) + LIBSPLASHSCREEN_STATIC_LIB_EXCLUDE_OBJS += $(LIBJAVA_JPEG_OBJS) endif ifeq ($(USE_EXTERNAL_LIBPNG), false) @@ -165,6 +168,10 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) ifeq ($(USE_EXTERNAL_LIBZ), false) LIBSPLASHSCREEN_EXTRA_SRC += java.base:libzip/zlib + LIBZIP_SRC_PATH := $(TOPDIR)/src/java.base/share/native/libzip + LIBZIP_OBJS := $(sort $(patsubst %.c,%.o, $(notdir \ + $(wildcard $(LIBZIP_SRC_PATH)/*.c $(LIBZIP_SRC_PATH)/zlib/*.c)))) + LIBSPLASHSCREEN_STATIC_LIB_EXCLUDE_OBJS += $(LIBZIP_OBJS) endif LIBSPLASHSCREEN_CFLAGS += -DSPLASHSCREEN -DPNG_NO_MMX_CODE \ @@ -207,6 +214,8 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) LIBSPLASHSCREEN_CFLAGS += -DWITH_X11 $(X_CFLAGS) endif + LIBSPLASHSCREEN_STATIC_LIB_EXCLUDE_OBJS += systemScale.o + $(eval $(call SetupJdkLibrary, BUILD_LIBSPLASHSCREEN, \ NAME := splashscreen, \ EXTRA_SRC := $(LIBSPLASHSCREEN_EXTRA_SRC), \ @@ -257,6 +266,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) -framework Security, \ LIBS_aix := $(LIBDL) -liconv $(X_LIBS) -lX11 -lXext, \ LIBS_windows := delayimp.lib gdi32.lib kernel32.lib user32.lib, \ + STATIC_LIB_EXCLUDE_OBJS := $(LIBSPLASHSCREEN_STATIC_LIB_EXCLUDE_OBJS), \ )) TARGETS += $(BUILD_LIBSPLASHSCREEN) diff --git a/make/modules/jdk.accessibility/Lib.gmk b/make/modules/jdk.accessibility/Lib.gmk index 6a429a56375..6323049c985 100644 --- a/make/modules/jdk.accessibility/Lib.gmk +++ b/make/modules/jdk.accessibility/Lib.gmk @@ -38,6 +38,7 @@ ifeq ($(call isTargetOs, windows), true) NAME := javaaccessbridge, \ EXTRA_SRC := common, \ OPTIMIZATION := LOW, \ + ONLY_EXPORTED := true, \ DISABLED_WARNINGS_microsoft := 4311 4302 4312, \ CXXFLAGS_FILTER_OUT := -MD, \ CXXFLAGS := -MT -DACCESSBRIDGE_ARCH_64, \ @@ -67,6 +68,7 @@ ifeq ($(call isTargetOs, windows), true) CXXFLAGS := -DACCESSBRIDGE_ARCH_64, \ EXTRA_HEADER_DIRS := \ include/bridge, \ + ONLY_EXPORTED := true, \ LDFLAGS := \ -def:$(ACCESSIBILITY_SRCDIR)/libwindowsaccessbridge/WinAccessBridge.DEF, \ LIBS_windows := advapi32.lib comdlg32.lib gdi32.lib kernel32.lib \ diff --git a/make/modules/jdk.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk index 75548133019..33d10336e6e 100644 --- a/make/modules/jdk.jpackage/Lib.gmk +++ b/make/modules/jdk.jpackage/Lib.gmk @@ -49,6 +49,7 @@ $(eval $(call SetupJdkExecutable, BUILD_JPACKAGEAPPLAUNCHER, \ LINK_TYPE := $(JPACKAGEAPPLAUNCHER_LINK_TYPE), \ OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncher, \ + ONLY_EXPORTED := true, \ SRC := applauncher, \ EXTRA_SRC := common, \ INCLUDE_FILES := $(JPACKAGEAPPLAUNCHER_INCLUDE_FILES), \ @@ -83,6 +84,7 @@ ifeq ($(call isTargetOs, linux), true) OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ SYMBOLS_DIR := \ $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjpackageapplauncheraux, \ + ONLY_EXPORTED := true, \ SRC := libapplauncher, \ EXTRA_SRC := \ applauncher \ @@ -127,6 +129,7 @@ ifeq ($(call isTargetOs, windows), true) NAME := wixhelper, \ OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libwixhelper, \ + ONLY_EXPORTED := true, \ OPTIMIZATION := LOW, \ EXTRA_SRC := common, \ CXXFLAGS_FILTER_OUT := -MD, \ @@ -146,6 +149,7 @@ ifeq ($(call isTargetOs, windows), true) NAME := msiwrapper, \ OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/msiwrapper, \ + ONLY_EXPORTED := true, \ EXTRA_SRC := common, \ CXXFLAGS_FILTER_OUT := -MD, \ CXXFLAGS_windows := -MT $(JPACKAGE_CXXFLAGS_windows), \ @@ -164,6 +168,7 @@ ifeq ($(call isTargetOs, windows), true) OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ SYMBOLS_DIR := \ $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/jpackageapplauncherw, \ + ONLY_EXPORTED := true, \ SRC := applauncher, \ EXTRA_SRC := common, \ OPTIMIZATION := LOW, \ diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index d51ca8a5ffb..d85120f3f1d 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -562,6 +562,7 @@ void os::init_system_properties_values() { // Base path of extensions installed on the system. #define SYS_EXT_DIR "/usr/java/packages" #define EXTENSIONS_DIR "/lib/ext" +#define JVM_LIB_NAME "libjvm.so" // Buffer that fits several snprintfs. // Note that the space for the colon and the trailing null are provided @@ -576,22 +577,32 @@ void os::init_system_properties_values() { char *pslash; os::jvm_path(buf, bufsize); - // Found the full path to libjvm.so. - // Now cut the path to /jre if we can. + // Found the full path to the binary. It is normally of this structure: + // /lib//libjvm.so + // but can also be like this for a statically linked binary: + // /bin/ pslash = strrchr(buf, '/'); if (pslash != nullptr) { - *pslash = '\0'; // Get rid of /libjvm.so. - } - pslash = strrchr(buf, '/'); - if (pslash != nullptr) { - *pslash = '\0'; // Get rid of /{client|server|hotspot}. + if (strncmp(pslash + 1, JVM_LIB_NAME, strlen(JVM_LIB_NAME)) == 0) { + // Binary name is libjvm.so. Get rid of /libjvm.so. + *pslash = '\0'; + } + + // Get rid of /, if binary is libjvm.so, + // or cut off /, if it is a statically linked binary. + pslash = strrchr(buf, '/'); + if (pslash != nullptr) { + *pslash = '\0'; + } } Arguments::set_dll_dir(buf); + // Get rid of /lib, if binary is libjvm.so, + // or cut off /bin, if it is a statically linked binary. if (pslash != nullptr) { pslash = strrchr(buf, '/'); if (pslash != nullptr) { - *pslash = '\0'; // Get rid of /lib. + *pslash = '\0'; } } Arguments::set_java_home(buf); diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 7d418d4ad43..3ae692b9e88 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -854,6 +854,12 @@ void os::dll_unload(void *lib) { LINUX_ONLY(os::free(l_pathdup)); } +void* os::lookup_function(const char* name) { + // This returns the global symbol in the main executable and its dependencies, + // as well as shared objects dynamically loaded with RTLD_GLOBAL flag. + return dlsym(RTLD_DEFAULT, name); +} + jlong os::lseek(int fd, jlong offset, int whence) { return (jlong) ::lseek(fd, offset, whence); } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 8bb3789bbad..45163765184 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1401,6 +1401,12 @@ void* os::dll_lookup(void *lib, const char *name) { return ret; } +void* os::lookup_function(const char* name) { + // This is needed only for static builds which are not supported on Windows + ShouldNotReachHere(); + return nullptr; // Satisfy compiler +} + // Directory routines copied from src/win32/native/java/io/dirent_md.c // * dirent_md.c 1.15 00/02/02 // diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 1b34f2ebede..aac312c36a3 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -949,16 +949,35 @@ void* ClassLoader::dll_lookup(void* lib, const char* name, const char* path) { void ClassLoader::load_java_library() { assert(CanonicalizeEntry == nullptr, "should not load java library twice"); + if (is_vm_statically_linked()) { + CanonicalizeEntry = CAST_TO_FN_PTR(canonicalize_fn_t, os::lookup_function("JDK_Canonicalize")); + assert(CanonicalizeEntry != nullptr, "could not lookup JDK_Canonicalize"); + return; + } + void *javalib_handle = os::native_java_library(); if (javalib_handle == nullptr) { vm_exit_during_initialization("Unable to load java library", nullptr); } CanonicalizeEntry = CAST_TO_FN_PTR(canonicalize_fn_t, dll_lookup(javalib_handle, "JDK_Canonicalize", nullptr)); + assert(CanonicalizeEntry != nullptr, "could not lookup JDK_Canonicalize in java library"); } void ClassLoader::load_jimage_library() { assert(JImageOpen == nullptr, "should not load jimage library twice"); + + if (is_vm_statically_linked()) { + JImageOpen = CAST_TO_FN_PTR(JImageOpen_t, os::lookup_function("JIMAGE_Open")); + JImageClose = CAST_TO_FN_PTR(JImageClose_t, os::lookup_function("JIMAGE_Close")); + JImageFindResource = CAST_TO_FN_PTR(JImageFindResource_t, os::lookup_function("JIMAGE_FindResource")); + JImageGetResource = CAST_TO_FN_PTR(JImageGetResource_t, os::lookup_function("JIMAGE_GetResource")); + assert(JImageOpen != nullptr && JImageClose != nullptr && + JImageFindResource != nullptr && JImageGetResource != nullptr, + "could not lookup all jimage library functions"); + return; + } + char path[JVM_MAXPATHLEN]; char ebuf[1024]; void* handle = nullptr; @@ -973,6 +992,9 @@ void ClassLoader::load_jimage_library() { JImageClose = CAST_TO_FN_PTR(JImageClose_t, dll_lookup(handle, "JIMAGE_Close", path)); JImageFindResource = CAST_TO_FN_PTR(JImageFindResource_t, dll_lookup(handle, "JIMAGE_FindResource", path)); JImageGetResource = CAST_TO_FN_PTR(JImageGetResource_t, dll_lookup(handle, "JIMAGE_GetResource", path)); + assert(JImageOpen != nullptr && JImageClose != nullptr && + JImageFindResource != nullptr && JImageGetResource != nullptr, + "could not lookup all jimage library functions in jimage library"); } int ClassLoader::crc32(int crc, const char* buf, int len) { diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 9a36584916d..0ac2cc350b1 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -85,15 +85,20 @@ static verify_byte_codes_fn_t verify_byte_codes_fn() { if (_verify_byte_codes_fn != nullptr) return _verify_byte_codes_fn; + void *lib_handle = nullptr; // Load verify dll - char buffer[JVM_MAXPATHLEN]; - char ebuf[1024]; - if (!os::dll_locate_lib(buffer, sizeof(buffer), Arguments::get_dll_dir(), "verify")) - return nullptr; // Caller will throw VerifyError + if (is_vm_statically_linked()) { + lib_handle = os::get_default_process_handle(); + } else { + char buffer[JVM_MAXPATHLEN]; + char ebuf[1024]; + if (!os::dll_locate_lib(buffer, sizeof(buffer), Arguments::get_dll_dir(), "verify")) + return nullptr; // Caller will throw VerifyError - void *lib_handle = os::dll_load(buffer, ebuf, sizeof(ebuf)); - if (lib_handle == nullptr) - return nullptr; // Caller will throw VerifyError + lib_handle = os::dll_load(buffer, ebuf, sizeof(ebuf)); + if (lib_handle == nullptr) + return nullptr; // Caller will throw VerifyError + } void *fn = os::dll_lookup(lib_handle, "VerifyClassForMajorVersion"); if (fn == nullptr) diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index deb2c30500c..8094261fe56 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -513,6 +513,11 @@ static void* _native_java_library = nullptr; void* os::native_java_library() { if (_native_java_library == nullptr) { + if (is_vm_statically_linked()) { + _native_java_library = get_default_process_handle(); + return _native_java_library; + } + char buffer[JVM_MAXPATHLEN]; char ebuf[1024]; diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 2e52440ead4..db4f0f8c790 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -763,6 +763,9 @@ class os: AllStatic { // Unload library static void dll_unload(void *lib); + // Lookup the named function. This is used by the static JDK. + static void* lookup_function(const char* name); + // Callback for loaded module information // Input parameters: // char* module_file_name, diff --git a/src/hotspot/share/utilities/zipLibrary.cpp b/src/hotspot/share/utilities/zipLibrary.cpp index 57b3e501f56..0b3c307c291 100644 --- a/src/hotspot/share/utilities/zipLibrary.cpp +++ b/src/hotspot/share/utilities/zipLibrary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -60,6 +60,10 @@ static inline bool not_loaded() { } static void* dll_lookup(const char* name, const char* path, bool vm_exit_on_failure) { + if (is_vm_statically_linked()) { + return os::lookup_function(name); + } + assert(_zip_handle != nullptr, "invariant"); void* func = os::dll_lookup(_zip_handle, name); if (func == nullptr && vm_exit_on_failure) { @@ -87,16 +91,23 @@ static void store_function_pointers(const char* path, bool vm_exit_on_failure) { static void load_zip_library(bool vm_exit_on_failure) { assert(!is_loaded(), "should not load zip library twice"); char path[JVM_MAXPATHLEN]; - if (os::dll_locate_lib(&path[0], sizeof path, Arguments::get_dll_dir(), "zip")) { - char ebuf[1024]; - _zip_handle = os::dll_load(&path[0], &ebuf[0], sizeof ebuf); - } - if (_zip_handle == nullptr) { - if (vm_exit_on_failure) { - vm_exit_during_initialization("Unable to load zip library", &path[0]); + + if (is_vm_statically_linked()) { + _zip_handle = os::get_default_process_handle(); + } else { + // Load the libzip shared library and lookup the needed functions. + if (os::dll_locate_lib(&path[0], sizeof path, Arguments::get_dll_dir(), "zip")) { + char ebuf[1024]; + _zip_handle = os::dll_load(&path[0], &ebuf[0], sizeof ebuf); + } + if (_zip_handle == nullptr) { + if (vm_exit_on_failure) { + vm_exit_during_initialization("Unable to load zip library", &path[0]); + } + return; } - return; } + store_function_pointers(&path[0], vm_exit_on_failure); Atomic::release_store(&_loaded, true); assert(is_loaded(), "invariant"); diff --git a/src/java.base/macosx/native/libjli/java_md_macosx.m b/src/java.base/macosx/native/libjli/java_md_macosx.m index c836ae903e5..2b205a65ba1 100644 --- a/src/java.base/macosx/native/libjli/java_md_macosx.m +++ b/src/java.base/macosx/native/libjli/java_md_macosx.m @@ -336,29 +336,31 @@ static void MacOSXStartup(int argc, char *argv[]) { int argc = *pargc; char **argv = *pargv; - /* Find out where the JDK is that we will be using. */ - if (!GetJDKInstallRoot(jdkroot, so_jdkroot, JNI_FALSE) ) { - JLI_ReportErrorMessage(LAUNCHER_ERROR1); - exit(2); - } - JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%sjvm.cfg", - jdkroot, FILESEP, FILESEP); - /* Find the specified JVM type */ - if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) { - JLI_ReportErrorMessage(CFG_ERROR7); - exit(1); - } + if (!JLI_IsStaticallyLinked()) { + /* Find out where the JDK is that we will be using. */ + if (!GetJDKInstallRoot(jdkroot, so_jdkroot, JNI_FALSE) ) { + JLI_ReportErrorMessage(LAUNCHER_ERROR1); + exit(2); + } + JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%sjvm.cfg", + jdkroot, FILESEP, FILESEP); + /* Find the specified JVM type */ + if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) { + JLI_ReportErrorMessage(CFG_ERROR7); + exit(1); + } - jvmpath[0] = '\0'; - jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE); - if (JLI_StrCmp(jvmtype, "ERROR") == 0) { - JLI_ReportErrorMessage(CFG_ERROR9); - exit(4); - } + jvmpath[0] = '\0'; + jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE); + if (JLI_StrCmp(jvmtype, "ERROR") == 0) { + JLI_ReportErrorMessage(CFG_ERROR9); + exit(4); + } - if (!GetJVMPath(jdkroot, jvmtype, jvmpath, so_jvmpath)) { - JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); - exit(4); + if (!GetJVMPath(jdkroot, jvmtype, jvmpath, so_jvmpath)) { + JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); + exit(4); + } } /* diff --git a/src/java.base/unix/native/libjli/java_md.c b/src/java.base/unix/native/libjli/java_md.c index c2d2ac93f36..2c25a7668c3 100644 --- a/src/java.base/unix/native/libjli/java_md.c +++ b/src/java.base/unix/native/libjli/java_md.c @@ -276,6 +276,13 @@ CreateExecutionEnvironment(int *pargc, char ***pargv, char jdkroot[], jint so_jdkroot, char jvmpath[], jint so_jvmpath, char jvmcfg[], jint so_jvmcfg) { + if (JLI_IsStaticallyLinked()) { + // With static builds, all JDK and VM natives are statically linked + // with the launcher executable. No need to manipulate LD_LIBRARY_PATH + // by adding /lib and etc. The 'jrepath', 'jvmpath' and + // 'jvmcfg' are not used by the caller for static builds. Simply return. + return; + } char * jvmtype = NULL; char **argv = *pargv; @@ -318,6 +325,7 @@ CreateExecutionEnvironment(int *pargc, char ***pargv, JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); exit(4); } + /* * we seem to have everything we need, so without further ado * we return back, otherwise proceed to set the environment. @@ -519,11 +527,15 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) JLI_TraceLauncher("JVM path is %s\n", jvmpath); - libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); - if (libjvm == NULL) { - JLI_ReportErrorMessage(DLL_ERROR1, __LINE__); - JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); - return JNI_FALSE; + if (JLI_IsStaticallyLinked()) { + libjvm = dlopen(NULL, RTLD_NOW + RTLD_GLOBAL); + } else { + libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); + if (libjvm == NULL) { + JLI_ReportErrorMessage(DLL_ERROR1, __LINE__); + JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); + return JNI_FALSE; + } } ifn->CreateJavaVM = (CreateJavaVM_t) @@ -600,22 +612,26 @@ void* SplashProcAddress(const char* name) { char jdkRoot[MAXPATHLEN]; char splashPath[MAXPATHLEN]; - if (!GetJDKInstallRoot(jdkRoot, sizeof(jdkRoot), JNI_FALSE)) { - JLI_ReportErrorMessage(LAUNCHER_ERROR1); - return NULL; - } - ret = JLI_Snprintf(splashPath, sizeof(splashPath), "%s/lib/%s", + if (JLI_IsStaticallyLinked()) { + hSplashLib = dlopen(NULL, RTLD_LAZY); + } else { + if (!GetJDKInstallRoot(jdkRoot, sizeof(jdkRoot), JNI_FALSE)) { + JLI_ReportErrorMessage(LAUNCHER_ERROR1); + return NULL; + } + ret = JLI_Snprintf(splashPath, sizeof(splashPath), "%s/lib/%s", jdkRoot, SPLASHSCREEN_SO); - if (ret >= (int) sizeof(splashPath)) { - JLI_ReportErrorMessage(LAUNCHER_ERROR3); - return NULL; - } - if (ret < 0) { - JLI_ReportErrorMessage(LAUNCHER_ERROR5); - return NULL; + if (ret >= (int) sizeof(splashPath)) { + JLI_ReportErrorMessage(LAUNCHER_ERROR3); + return NULL; + } + if (ret < 0) { + JLI_ReportErrorMessage(LAUNCHER_ERROR5); + return NULL; + } + hSplashLib = dlopen(splashPath, RTLD_LAZY | RTLD_GLOBAL); } - hSplashLib = dlopen(splashPath, RTLD_LAZY | RTLD_GLOBAL); JLI_TraceLauncher("Info: loaded %s\n", splashPath); } if (hSplashLib) { diff --git a/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c b/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c index 389e25caaec..f24a4eb9a2c 100644 --- a/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c +++ b/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c @@ -132,7 +132,9 @@ AWT_OnLoad(JavaVM *vm, void *reserved) } #endif - if (!JVM_IsStaticallyLinked()) { + if (JVM_IsStaticallyLinked()) { + awtHandle = dlopen(NULL, RTLD_LAZY); + } else { /* Get address of this library and the directory containing it. */ dladdr((void *)AWT_OnLoad, &dlinfo); realpath((char *)dlinfo.dli_fname, buf); From 30b8bbe255c1653d25961c3fe79096b2b8d62bd6 Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Mon, 2 Dec 2024 15:23:47 +0000 Subject: [PATCH 046/171] 8345060: Remove Security Manager dependencies from java.security.KeyStore and Identity APIs and implementations Reviewed-by: hchao, alanb, weijun --- .../classes/apple/security/AppleProvider.java | 16 +- .../classes/apple/security/KeychainStore.java | 35 +--- .../com/sun/crypto/provider/KeyProtector.java | 2 +- .../share/classes/java/security/Identity.java | 13 -- .../classes/java/security/IdentityScope.java | 17 +- .../share/classes/java/security/KeyStore.java | 151 +++++++----------- .../share/classes/java/security/Signer.java | 24 +-- .../sun/security/pkcs12/PKCS12KeyStore.java | 43 ++--- .../sun/security/tools/keytool/Main.java | 4 +- .../sun/security/util/KeyStoreDelegator.java | 6 +- .../java/security/KeyStore/EntryMethods.java | 14 +- 11 files changed, 95 insertions(+), 230 deletions(-) diff --git a/src/java.base/macosx/classes/apple/security/AppleProvider.java b/src/java.base/macosx/classes/apple/security/AppleProvider.java index 3f584be336b..0835f1f0dd3 100644 --- a/src/java.base/macosx/classes/apple/security/AppleProvider.java +++ b/src/java.base/macosx/classes/apple/security/AppleProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -75,20 +75,14 @@ public Object newInstance(Object ctrParamObj) } - @SuppressWarnings("removal") public AppleProvider() { /* We are the Apple provider */ super("Apple", PROVIDER_VER, info); final Provider p = this; - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - putService(new ProviderService(p, "KeyStore", - "KeychainStore", "apple.security.KeychainStore$USER")); - putService(new ProviderService(p, "KeyStore", - "KeychainStore-ROOT", "apple.security.KeychainStore$ROOT")); - return null; - } - }); + putService(new ProviderService(p, "KeyStore", + "KeychainStore", "apple.security.KeychainStore$USER")); + putService(new ProviderService(p, "KeyStore", + "KeychainStore-ROOT", "apple.security.KeychainStore$ROOT")); } } diff --git a/src/java.base/macosx/classes/apple/security/KeychainStore.java b/src/java.base/macosx/classes/apple/security/KeychainStore.java index c8bb717d8ad..6f70fccbb24 100644 --- a/src/java.base/macosx/classes/apple/security/KeychainStore.java +++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -188,15 +188,6 @@ static class TrustedCertEntry { jdk.internal.loader.BootLoader.loadLibrary("osxsecurity"); } - private static void permissionCheck() { - @SuppressWarnings("removal") - SecurityManager sec = System.getSecurityManager(); - - if (sec != null) { - sec.checkPermission(new RuntimePermission("useKeychainStore")); - } - } - private final String storeName; /** @@ -228,8 +219,6 @@ private KeychainStore(String name) { public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { - permissionCheck(); - // An empty password is rejected by MacOS API, no private key data // is exported. If no password is passed (as is the case when // this implementation is used as browser keystore in various @@ -332,8 +321,6 @@ public Key engineGetKey(String alias, char[] password) * key entry without a certificate chain). */ public Certificate[] engineGetCertificateChain(String alias) { - permissionCheck(); - Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); if (entry instanceof KeyEntry keyEntry) { @@ -363,8 +350,6 @@ public Certificate[] engineGetCertificateChain(String alias) { * does not contain a certificate. */ public Certificate engineGetCertificate(String alias) { - permissionCheck(); - Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); if (entry != null) { @@ -420,8 +405,6 @@ public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter * not exist */ public Date engineGetCreationDate(String alias) { - permissionCheck(); - Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); if (entry != null) { @@ -461,8 +444,6 @@ public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException { - permissionCheck(); - synchronized(entries) { try { KeyEntry entry = new KeyEntry(); @@ -532,8 +513,6 @@ public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { - permissionCheck(); - synchronized(entries) { // key must be encoded as EncryptedPrivateKeyInfo as defined in // PKCS#8 @@ -582,8 +561,6 @@ public void engineSetCertificateEntry(String alias, Certificate cert) public void engineDeleteEntry(String alias) throws KeyStoreException { - permissionCheck(); - String lowerAlias = alias.toLowerCase(Locale.ROOT); synchronized(entries) { Object entry = entries.remove(lowerAlias); @@ -597,7 +574,6 @@ public void engineDeleteEntry(String alias) * @return enumeration of the alias names */ public Enumeration engineAliases() { - permissionCheck(); return entries.keys(); } @@ -609,7 +585,6 @@ public Enumeration engineAliases() { * @return true if the alias exists, false otherwise */ public boolean engineContainsAlias(String alias) { - permissionCheck(); return entries.containsKey(alias.toLowerCase(Locale.ROOT)); } @@ -619,7 +594,6 @@ public boolean engineContainsAlias(String alias) { * @return the number of entries in this keystore */ public int engineSize() { - permissionCheck(); return entries.size(); } @@ -631,7 +605,6 @@ public int engineSize() { * key entry, false otherwise. */ public boolean engineIsKeyEntry(String alias) { - permissionCheck(); Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); return entry instanceof KeyEntry; } @@ -644,7 +617,6 @@ public boolean engineIsKeyEntry(String alias) { * trusted certificate entry, false otherwise. */ public boolean engineIsCertificateEntry(String alias) { - permissionCheck(); Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); return entry instanceof TrustedCertEntry; } @@ -666,7 +638,6 @@ public boolean engineIsCertificateEntry(String alias) { * or null if no such entry exists in this keystore. */ public String engineGetCertificateAlias(Certificate cert) { - permissionCheck(); Certificate certElem; for (Enumeration e = entries.keys(); e.hasMoreElements(); ) { @@ -704,8 +675,6 @@ public String engineGetCertificateAlias(Certificate cert) { public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { - permissionCheck(); - // Delete items that do have a keychain item ref. for (Enumeration e = deletedEntries.keys(); e.hasMoreElements(); ) { String alias = e.nextElement(); @@ -795,8 +764,6 @@ private long addCertificateToKeychain(String alias, Certificate cert) { public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { - permissionCheck(); - // Release any stray keychain references before clearing out the entries. synchronized(entries) { for (Enumeration e = entries.keys(); e.hasMoreElements(); ) { diff --git a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java index f2d3efd685c..06ccf6cba39 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java @@ -89,7 +89,7 @@ final class KeyProtector { */ static { int iterationCount = DEFAULT_ITERATION_COUNT; - String ic = SecurityProperties.privilegedGetOverridable( + String ic = SecurityProperties.getOverridableProperty( "jdk.jceks.iterationCount"); if (ic != null && !ic.isEmpty()) { try { diff --git a/src/java.base/share/classes/java/security/Identity.java b/src/java.base/share/classes/java/security/Identity.java index f717f5576b0..e23f2043c02 100644 --- a/src/java.base/share/classes/java/security/Identity.java +++ b/src/java.base/share/classes/java/security/Identity.java @@ -178,7 +178,6 @@ public PublicKey getPublicKey() { /* Should we throw an exception if this is already set? */ public void setPublicKey(PublicKey key) throws KeyManagementException { - check("setIdentityPublicKey"); this.publicKey = key; certificates = new Vector<>(); } @@ -191,7 +190,6 @@ public void setPublicKey(PublicKey key) throws KeyManagementException { * @see #getInfo */ public void setInfo(String info) { - check("setIdentityInfo"); this.info = info; } @@ -221,8 +219,6 @@ public String getInfo() { public void addCertificate(Certificate certificate) throws KeyManagementException { - check("addIdentityCertificate"); - if (certificates == null) { certificates = new Vector<>(); } @@ -260,7 +256,6 @@ private boolean keyEquals(PublicKey aKey, PublicKey anotherKey) { */ public void removeCertificate(Certificate certificate) throws KeyManagementException { - check("removeIdentityCertificate"); if (certificates != null) { certificates.removeElement(certificate); } @@ -358,7 +353,6 @@ String fullName() { * name of its scope (if any). */ public String toString() { - check("printIdentity"); String printable = name; if (scope != null) { printable += "[" + scope.getName() + "]"; @@ -429,11 +423,4 @@ String printCertificates() { public int hashCode() { return name.hashCode(); } - - private static void check(String directive) { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkSecurityAccess(directive); - } - } } diff --git a/src/java.base/share/classes/java/security/IdentityScope.java b/src/java.base/share/classes/java/security/IdentityScope.java index 73128d0e010..aedbd51bdf9 100644 --- a/src/java.base/share/classes/java/security/IdentityScope.java +++ b/src/java.base/share/classes/java/security/IdentityScope.java @@ -76,13 +76,7 @@ class IdentityScope extends Identity { // initialize the system scope private static void initializeSystemScope() { - String classname = AccessController.doPrivileged( - new PrivilegedAction<>() { - public String run() { - return Security.getProperty("system.scope"); - } - }); - + String classname = Security.getProperty("system.scope"); if (classname == null) { return; @@ -153,7 +147,6 @@ public static IdentityScope getSystemScope() { * @see #getSystemScope */ protected static void setSystemScope(IdentityScope scope) { - check("setSystemScope"); IdentityScope.scope = scope; } @@ -241,12 +234,4 @@ public abstract void removeIdentity(Identity identity) public String toString() { return super.toString() + "[" + size() + "]"; } - - private static void check(String directive) { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkSecurityAccess(directive); - } - } - } diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 0370b5e9884..b420cc69aa6 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -987,9 +987,7 @@ public static KeyStore getInstance(String type, Provider provider) * @see java.security.Security security properties */ public static final String getDefaultType() { - @SuppressWarnings("removal") - String kstype = AccessController.doPrivileged((PrivilegedAction) () -> - Security.getProperty(KEYSTORE_TYPE)); + String kstype = Security.getProperty(KEYSTORE_TYPE); if (kstype == null) { kstype = "pkcs12"; } @@ -1993,9 +1991,7 @@ public static Builder newInstance(String type, Provider provider, ("File does not exist or it does not refer " + "to a normal file: " + file); } - @SuppressWarnings("removal") - var acc = AccessController.getContext(); - return new FileBuilder(type, provider, file, protection, acc); + return new FileBuilder(type, provider, file, protection); } /** @@ -2048,24 +2044,19 @@ private static final class FileBuilder extends Builder { private final File file; private final ProtectionParameter protection; private ProtectionParameter keyProtection; - @SuppressWarnings("removal") - private final AccessControlContext context; private KeyStore keyStore; private Throwable oldException; FileBuilder(String type, Provider provider, File file, - ProtectionParameter protection, - @SuppressWarnings("removal") AccessControlContext context) { + ProtectionParameter protection) { this.type = type; this.provider = provider; this.file = file; this.protection = protection; - this.context = context; } - @SuppressWarnings("removal") public synchronized KeyStore getKeyStore() throws KeyStoreException { if (keyStore != null) { @@ -2076,19 +2067,18 @@ public synchronized KeyStore getKeyStore() throws KeyStoreException ("Previous KeyStore instantiation failed", oldException); } - PrivilegedExceptionAction action = - new PrivilegedExceptionAction() { - public KeyStore run() throws Exception { - if (!(protection instanceof CallbackHandlerProtection)) { - return run0(); - } + try { + if (!(protection instanceof CallbackHandlerProtection)) { + keyStore = getKeyStore0(); + } else { // when using a CallbackHandler, // reprompt if the password is wrong int tries = 0; while (true) { tries++; try { - return run0(); + keyStore = getKeyStore0(); + break; } catch (IOException e) { if ((tries < MAX_CALLBACK_TRIES) && (e.getCause() instanceof UnrecoverableKeyException)) { @@ -2098,58 +2088,53 @@ public KeyStore run() throws Exception { } } } - public KeyStore run0() throws Exception { - KeyStore ks; - char[] password; + } catch (Exception e) { + oldException = e; + throw new KeyStoreException + ("KeyStore instantiation failed", oldException); + } + return keyStore; + } - // Acquire keystore password - if (protection instanceof PasswordProtection) { - password = - ((PasswordProtection)protection).getPassword(); - keyProtection = protection; - } else { - CallbackHandler handler = - ((CallbackHandlerProtection)protection) - .getCallbackHandler(); - PasswordCallback callback = new PasswordCallback - ("Password for keystore " + file.getName(), - false); - handler.handle(new Callback[] {callback}); - password = callback.getPassword(); - if (password == null) { - throw new KeyStoreException("No password" + - " provided"); - } - callback.clearPassword(); - keyProtection = new PasswordProtection(password); - } + private KeyStore getKeyStore0() throws Exception { + KeyStore ks; + char[] password; - if (type.isEmpty()) { - // Instantiate keystore and load keystore data - ks = KeyStore.getInstance(file, password); - } else { - // Instantiate keystore - if (provider == null) { - ks = KeyStore.getInstance(type); - } else { - ks = KeyStore.getInstance(type, provider); - } - // Load keystore data - try (InputStream in = new FileInputStream(file)) { - ks.load(in, password); - } - } - return ks; + // Acquire keystore password + if (protection instanceof PasswordProtection) { + password = ((PasswordProtection)protection).getPassword(); + keyProtection = protection; + } else { + CallbackHandler handler = + ((CallbackHandlerProtection)protection) + .getCallbackHandler(); + PasswordCallback callback = new PasswordCallback + ("Password for keystore " + file.getName(), false); + handler.handle(new Callback[] {callback}); + password = callback.getPassword(); + if (password == null) { + throw new KeyStoreException("No password" + " provided"); + } + callback.clearPassword(); + keyProtection = new PasswordProtection(password); + } + + if (type.isEmpty()) { + // Instantiate keystore and load keystore data + ks = KeyStore.getInstance(file, password); + } else { + // Instantiate keystore + if (provider == null) { + ks = KeyStore.getInstance(type); + } else { + ks = KeyStore.getInstance(type, provider); + } + // Load keystore data + try (InputStream in = new FileInputStream(file)) { + ks.load(in, password); } - }; - try { - keyStore = AccessController.doPrivileged(action, context); - return keyStore; - } catch (PrivilegedActionException e) { - oldException = e.getCause(); - throw new KeyStoreException - ("KeyStore instantiation failed", oldException); } + return ks; } public synchronized ProtectionParameter @@ -2195,16 +2180,18 @@ public static Builder newInstance(final String type, if ((type == null) || (protection == null)) { throw new NullPointerException(); } - @SuppressWarnings("removal") - final AccessControlContext context = AccessController.getContext(); return new Builder() { private volatile boolean getCalled; private IOException oldException; - private final PrivilegedExceptionAction action - = new PrivilegedExceptionAction<>() { - - public KeyStore run() throws Exception { + public synchronized KeyStore getKeyStore() + throws KeyStoreException { + if (oldException != null) { + throw new KeyStoreException + ("Previous KeyStore instantiation failed", + oldException); + } + try { KeyStore ks; if (provider == null) { ks = KeyStore.getInstance(type); @@ -2237,23 +2224,9 @@ public KeyStore run() throws Exception { } getCalled = true; return ks; - } - }; - - @SuppressWarnings("removal") - public synchronized KeyStore getKeyStore() - throws KeyStoreException { - if (oldException != null) { - throw new KeyStoreException - ("Previous KeyStore instantiation failed", - oldException); - } - try { - return AccessController.doPrivileged(action, context); - } catch (PrivilegedActionException e) { - Throwable cause = e.getCause(); + } catch (Exception e) { throw new KeyStoreException - ("KeyStore instantiation failed", cause); + ("KeyStore instantiation failed", e); } } diff --git a/src/java.base/share/classes/java/security/Signer.java b/src/java.base/share/classes/java/security/Signer.java index 4bdbff00b69..44ad9d031f8 100644 --- a/src/java.base/share/classes/java/security/Signer.java +++ b/src/java.base/share/classes/java/security/Signer.java @@ -99,7 +99,6 @@ public Signer(String name, IdentityScope scope) * not yet been set. */ public PrivateKey getPrivateKey() { - check("getSignerPrivateKey"); return privateKey; } @@ -115,24 +114,13 @@ public PrivateKey getPrivateKey() { */ public final void setKeyPair(KeyPair pair) throws InvalidParameterException, KeyException { - check("setSignerKeyPair"); - final PublicKey pub = pair.getPublic(); + PublicKey pub = pair.getPublic(); PrivateKey priv = pair.getPrivate(); if (pub == null || priv == null) { throw new InvalidParameterException(); } - try { - AccessController.doPrivileged( - new PrivilegedExceptionAction<>() { - public Void run() throws KeyManagementException { - setPublicKey(pub); - return null; - } - }); - } catch (PrivilegedActionException pae) { - throw (KeyManagementException) pae.getException(); - } + setPublicKey(pub); privateKey = priv; } @@ -156,12 +144,4 @@ String printKeys() { public String toString() { return "[Signer]" + super.toString(); } - - private static void check(String directive) { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkSecurityAccess(directive); - } - } - } diff --git a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index 52393f0466a..bee89856756 100644 --- a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -26,7 +26,6 @@ package sun.security.pkcs12; import java.io.*; -import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Key; @@ -36,7 +35,6 @@ import java.security.KeyStoreException; import java.security.PKCS12Attribute; import java.security.PrivateKey; -import java.security.PrivilegedAction; import java.security.UnrecoverableEntryException; import java.security.UnrecoverableKeyException; import java.security.SecureRandom; @@ -66,7 +64,6 @@ import javax.security.auth.x500.X500Principal; import jdk.internal.access.SharedSecrets; -import sun.security.action.GetPropertyAction; import sun.security.tools.KeyStoreUtil; import sun.security.util.*; import sun.security.pkcs.ContentInfo; @@ -2651,15 +2648,14 @@ public boolean engineProbe(InputStream stream) throws IOException { // key entries. private static boolean useLegacy() { - return GetPropertyAction.privilegedGetProperty( - USE_LEGACY_PROP) != null; + return System.getProperty(USE_LEGACY_PROP) != null; } private static String defaultCertProtectionAlgorithm() { if (useLegacy()) { return LEGACY_CERT_PBE_ALGORITHM; } - String result = SecurityProperties.privilegedGetOverridable( + String result = SecurityProperties.getOverridableProperty( "keystore.pkcs12.certProtectionAlgorithm"); return (result != null && !result.isEmpty()) ? result : DEFAULT_CERT_PBE_ALGORITHM; @@ -2669,7 +2665,7 @@ private static int defaultCertPbeIterationCount() { if (useLegacy()) { return LEGACY_PBE_ITERATION_COUNT; } - String result = SecurityProperties.privilegedGetOverridable( + String result = SecurityProperties.getOverridableProperty( "keystore.pkcs12.certPbeIterationCount"); return (result != null && !result.isEmpty()) ? string2IC("certPbeIterationCount", result) @@ -2682,27 +2678,18 @@ private static String defaultKeyProtectionAlgorithm() { if (useLegacy()) { return LEGACY_KEY_PBE_ALGORITHM; } - @SuppressWarnings("removal") - String result = AccessController.doPrivileged(new PrivilegedAction() { - public String run() { - String result; - String name1 = "keystore.pkcs12.keyProtectionAlgorithm"; - String name2 = "keystore.PKCS12.keyProtectionAlgorithm"; - result = System.getProperty(name1); - if (result != null) { - return result; - } - result = System.getProperty(name2); - if (result != null) { - return result; - } + String name1 = "keystore.pkcs12.keyProtectionAlgorithm"; + String name2 = "keystore.PKCS12.keyProtectionAlgorithm"; + String result = System.getProperty(name1); + if (result == null) { + result = System.getProperty(name2); + if (result == null) { result = Security.getProperty(name1); - if (result != null) { - return result; + if (result == null) { + result = Security.getProperty(name2); } - return Security.getProperty(name2); } - }); + } return (result != null && !result.isEmpty()) ? result : DEFAULT_KEY_PBE_ALGORITHM; } @@ -2711,7 +2698,7 @@ private static int defaultKeyPbeIterationCount() { if (useLegacy()) { return LEGACY_PBE_ITERATION_COUNT; } - String result = SecurityProperties.privilegedGetOverridable( + String result = SecurityProperties.getOverridableProperty( "keystore.pkcs12.keyPbeIterationCount"); return (result != null && !result.isEmpty()) ? string2IC("keyPbeIterationCount", result) @@ -2722,7 +2709,7 @@ private static String defaultMacAlgorithm() { if (useLegacy()) { return LEGACY_MAC_ALGORITHM; } - String result = SecurityProperties.privilegedGetOverridable( + String result = SecurityProperties.getOverridableProperty( "keystore.pkcs12.macAlgorithm"); return (result != null && !result.isEmpty()) ? result : DEFAULT_MAC_ALGORITHM; @@ -2732,7 +2719,7 @@ private static int defaultMacIterationCount() { if (useLegacy()) { return LEGACY_MAC_ITERATION_COUNT; } - String result = SecurityProperties.privilegedGetOverridable( + String result = SecurityProperties.getOverridableProperty( "keystore.pkcs12.macIterationCount"); return (result != null && !result.isEmpty()) ? string2IC("macIterationCount", result) diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 3afd5b2142a..e10b05eceae 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -981,9 +981,9 @@ void doCommands(PrintStream out) throws Exception { // if certProtectionAlgorithm and macAlgorithm are both NONE. if (storetype.equalsIgnoreCase("pkcs12")) { isPasswordlessKeyStore = - "NONE".equals(SecurityProperties.privilegedGetOverridable( + "NONE".equals(SecurityProperties.getOverridableProperty( "keystore.pkcs12.certProtectionAlgorithm")) - && "NONE".equals(SecurityProperties.privilegedGetOverridable( + && "NONE".equals(SecurityProperties.getOverridableProperty( "keystore.pkcs12.macAlgorithm")); } diff --git a/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java b/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java index fb8816ce02b..b98ccf7ca12 100644 --- a/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java +++ b/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -64,9 +64,7 @@ public KeyStoreDelegator( Class secondaryKeyStore) { // Check whether compatibility mode has been disabled - @SuppressWarnings("removal") - var prop = AccessController.doPrivileged((PrivilegedAction) () -> - Security.getProperty(KEYSTORE_TYPE_COMPAT)); + var prop = Security.getProperty(KEYSTORE_TYPE_COMPAT); compatModeEnabled = "true".equalsIgnoreCase(prop); if (compatModeEnabled) { diff --git a/test/jdk/java/security/KeyStore/EntryMethods.java b/test/jdk/java/security/KeyStore/EntryMethods.java index 38c1ab56ae8..b21cd403621 100644 --- a/test/jdk/java/security/KeyStore/EntryMethods.java +++ b/test/jdk/java/security/KeyStore/EntryMethods.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -70,15 +70,9 @@ public EntryMethods() throws Exception { pre15fis = new FileInputStream (System.getProperty("test.src") + "/EntryMethods.pre15.keystore"); - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - put("KeyStore.Pre15KeyStore", "EntryMethods$Pre15"); - put("KeyStore.Post15KeyStore", "EntryMethods$Post15"); - put("KeyStore.UnrecoverableKeyStore", - "EntryMethods$UnrecoverableKS"); - return null; - } - }); + put("KeyStore.Pre15KeyStore", "EntryMethods$Pre15"); + put("KeyStore.Post15KeyStore", "EntryMethods$Post15"); + put("KeyStore.UnrecoverableKeyStore", "EntryMethods$UnrecoverableKS"); } public static void main(String[] args) throws Exception { From 29c57e8b346531c8675ad853460207f67e00f946 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Mon, 2 Dec 2024 16:55:33 +0000 Subject: [PATCH 047/171] 8342677: Add IR validation tests for newly added saturated vector add / sub operations Reviewed-by: epeter --- .../compiler/lib/ir_framework/IRNode.java | 40 ++ .../VectorSaturatedOperationsTest.java | 547 ++++++++++++++++++ 2 files changed, 587 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 1d586e972be..1eb6251b2bb 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -254,6 +254,46 @@ public class IRNode { vectorNode(ADD_VL, "AddVL", TYPE_LONG); } + public static final String SATURATING_ADD_VB = VECTOR_PREFIX + "SATURATING_ADD_VB" + POSTFIX; + static { + vectorNode(SATURATING_ADD_VB, "SaturatingAddV", TYPE_BYTE); + } + + public static final String SATURATING_ADD_VS = VECTOR_PREFIX + "SATURATING_ADD_VS" + POSTFIX; + static { + vectorNode(SATURATING_ADD_VS, "SaturatingAddV", TYPE_SHORT); + } + + public static final String SATURATING_ADD_VI = VECTOR_PREFIX + "SATURATING_ADD_VI" + POSTFIX; + static { + vectorNode(SATURATING_ADD_VI, "SaturatingAddV", TYPE_INT); + } + + public static final String SATURATING_ADD_VL = VECTOR_PREFIX + "SATURATING_ADD_VL" + POSTFIX; + static { + vectorNode(SATURATING_ADD_VL, "SaturatingAddV", TYPE_LONG); + } + + public static final String SATURATING_SUB_VB = VECTOR_PREFIX + "SATURATING_SUB_VB" + POSTFIX; + static { + vectorNode(SATURATING_SUB_VB, "SaturatingSubV", TYPE_BYTE); + } + + public static final String SATURATING_SUB_VS = VECTOR_PREFIX + "SATURATING_SUB_VS" + POSTFIX; + static { + vectorNode(SATURATING_SUB_VS, "SaturatingSubV", TYPE_SHORT); + } + + public static final String SATURATING_SUB_VI = VECTOR_PREFIX + "SATURATING_SUB_VI" + POSTFIX; + static { + vectorNode(SATURATING_SUB_VI, "SaturatingSubV", TYPE_INT); + } + + public static final String SATURATING_SUB_VL = VECTOR_PREFIX + "SATURATING_SUB_VL" + POSTFIX; + static { + vectorNode(SATURATING_SUB_VL, "SaturatingSubV", TYPE_LONG); + } + public static final String ADD_REDUCTION_V = PREFIX + "ADD_REDUCTION_V" + POSTFIX; static { beforeMatchingNameRegex(ADD_REDUCTION_V, "AddReductionV(B|S|I|L|F|D)"); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java new file mode 100644 index 00000000000..f4ef254a980 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorSaturatedOperationsTest.java @@ -0,0 +1,547 @@ +/* + * 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 8338021 8342677 +* @summary Add IR validation tests for newly added saturated vector add / sub operations +* @modules jdk.incubator.vector +* @library /test/lib / +* @run driver compiler.vectorapi.VectorSaturatedOperationsTest +*/ + +package compiler.vectorapi; + +import jdk.incubator.vector.*; +import compiler.lib.ir_framework.*; +import java.util.Random; +import java.util.stream.IntStream; + +public class VectorSaturatedOperationsTest { + private static final int COUNT = 2048; + private static final VectorSpecies lspec = LongVector.SPECIES_PREFERRED; + private static final VectorSpecies ispec = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies sspec = ShortVector.SPECIES_PREFERRED; + private static final VectorSpecies bspec = ByteVector.SPECIES_PREFERRED; + + private long[] long_in1; + private int[] int_in1; + private short[] short_in1; + private byte[] byte_in1; + + private long[] long_in2; + private int[] int_in2; + private short[] short_in2; + private byte[] byte_in2; + + private long[] long_out; + private int[] int_out; + private short[] short_out; + private byte[] byte_out; + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(5000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } + + public void setup_delimiting_byte_inputs() { + // Saturating add + byte_in1[COUNT - 1] = Byte.MAX_VALUE; + byte_in2[COUNT - 1] = 100; + // Saturating sub + byte_in1[COUNT - 2] = Byte.MIN_VALUE; + byte_in2[COUNT - 2] = 100; + // Saturating unsigned add + byte_in1[COUNT - 3] = -1; + byte_in2[COUNT - 3] = 100; + // Saturating unsigned sub + byte_in1[COUNT - 4] = 0; + byte_in2[COUNT - 4] = 100; + } + + public void setup_delimiting_short_inputs() { + // Saturating add + short_in1[COUNT - 1] = Short.MAX_VALUE; + short_in2[COUNT - 1] = 100; + // Saturating sub + short_in1[COUNT - 2] = Short.MIN_VALUE; + short_in2[COUNT - 2] = 100; + // Saturating unsigned add + short_in1[COUNT - 3] = -1; + short_in2[COUNT - 3] = 100; + // Saturating unsigned sub + short_in1[COUNT - 4] = 0; + short_in2[COUNT - 4] = 100; + } + + public void setup_delimiting_int_inputs() { + // Saturating add + int_in1[COUNT - 1] = Integer.MAX_VALUE; + int_in2[COUNT - 1] = 100; + // Saturating sub + int_in1[COUNT - 2] = Integer.MIN_VALUE; + int_in2[COUNT - 2] = 100; + // Saturating unsigned add + int_in1[COUNT - 3] = -1; + int_in2[COUNT - 3] = 100; + // Saturating unsigned sub + int_in1[COUNT - 4] = 0; + int_in2[COUNT - 4] = 100; + } + + public void setup_delimiting_long_inputs() { + // Saturating add + long_in1[COUNT - 1] = Long.MAX_VALUE; + long_in2[COUNT - 1] = 100; + // Saturating sub + long_in1[COUNT - 2] = Long.MIN_VALUE; + long_in2[COUNT - 2] = 100; + // Saturating unsigned add + long_in1[COUNT - 3] = -1L; + long_in2[COUNT - 3] = 100; + // Saturating unsigned sub + long_in1[COUNT - 4] = 0; + long_in2[COUNT - 4] = 100; + } + + public VectorSaturatedOperationsTest() { + Random r = jdk.test.lib.Utils.getRandomInstance(); + byte_in1 = new byte[COUNT]; + short_in1 = new short[COUNT]; + int_in1 = new int[COUNT]; + long_in1 = new long[COUNT]; + + byte_in2 = new byte[COUNT]; + short_in2 = new short[COUNT]; + int_in2 = new int[COUNT]; + long_in2 = new long[COUNT]; + IntStream.range(0, COUNT-4).forEach( + i -> { + long_in1[i] = r.nextLong(Long.MIN_VALUE, Long.MAX_VALUE); + long_in2[i] = r.nextLong(Long.MIN_VALUE, Long.MAX_VALUE); + int_in1[i] = r.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); + int_in2[i] = r.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); + short_in1[i] = (short)r.nextInt(Short.MIN_VALUE, Short.MAX_VALUE); + short_in2[i] = (short)r.nextInt(Short.MIN_VALUE, Short.MAX_VALUE); + byte_in1[i] = (byte)r.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE); + byte_in2[i] = (byte)r.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE); + } + ); + + setup_delimiting_byte_inputs(); + setup_delimiting_short_inputs(); + setup_delimiting_int_inputs(); + setup_delimiting_long_inputs(); + + long_out = new long[COUNT]; + int_out = new int[COUNT]; + short_out = new short[COUNT]; + byte_out = new byte[COUNT]; + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void sadd_byte() { + for (int i = 0; i < COUNT; i += bspec.length()) { + ByteVector.fromArray(bspec, byte_in1, i) + .lanewise(VectorOperators.SADD, + ByteVector.fromArray(bspec, byte_in2, i)) + .intoArray(byte_out, i); + } + } + + @Check(test = "sadd_byte") + public void sadd_byte_verify() { + for (int i = 0; i < COUNT; i++) { + byte actual = byte_out[i]; + byte expected = VectorMath.addSaturating(byte_in1[i], byte_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VS, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void sadd_short() { + for (int i = 0; i < COUNT; i += sspec.length()) { + ShortVector.fromArray(sspec, short_in1, i) + .lanewise(VectorOperators.SADD, + ShortVector.fromArray(sspec, short_in2, i)) + .intoArray(short_out, i); + } + } + + @Check(test = "sadd_short") + public void sadd_short_verify() { + for (int i = 0; i < COUNT; i++) { + short actual = short_out[i]; + short expected = VectorMath.addSaturating(short_in1[i], short_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VI, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void sadd_int() { + for (int i = 0; i < COUNT; i += ispec.length()) { + IntVector.fromArray(ispec, int_in1, i) + .lanewise(VectorOperators.SADD, + IntVector.fromArray(ispec, int_in2, i)) + .intoArray(int_out, i); + } + } + + @Check(test = "sadd_int") + public void sadd_int_verify() { + for (int i = 0; i < COUNT; i++) { + int actual = int_out[i]; + int expected = VectorMath.addSaturating(int_in1[i], int_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void sadd_long() { + for (int i = 0; i < COUNT; i += lspec.length()) { + LongVector.fromArray(lspec, long_in1, i) + .lanewise(VectorOperators.SADD, + LongVector.fromArray(lspec, long_in2, i)) + .intoArray(long_out, i); + } + } + + @Check(test = "sadd_long") + public void sadd_long_verify() { + for (int i = 0; i < COUNT; i++) { + long actual = long_out[i]; + long expected = VectorMath.addSaturating(long_in1[i], long_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VB, " >0 " , "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void suadd_byte() { + for (int i = 0; i < COUNT; i += bspec.length()) { + ByteVector.fromArray(bspec, byte_in1, i) + .lanewise(VectorOperators.SUADD, + ByteVector.fromArray(bspec, byte_in2, i)) + .intoArray(byte_out, i); + } + } + + @Check(test = "suadd_byte") + public void suadd_byte_verify() { + for (int i = 0; i < COUNT; i++) { + byte actual = byte_out[i]; + byte expected = VectorMath.addSaturatingUnsigned(byte_in1[i], byte_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VS, " >0 ", "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void suadd_short() { + for (int i = 0; i < COUNT; i += sspec.length()) { + ShortVector.fromArray(sspec, short_in1, i) + .lanewise(VectorOperators.SUADD, + ShortVector.fromArray(sspec, short_in2, i)) + .intoArray(short_out, i); + } + } + + @Check(test = "suadd_short") + public void suadd_short_verify() { + for (int i = 0; i < COUNT; i++) { + short actual = short_out[i]; + short expected = VectorMath.addSaturatingUnsigned(short_in1[i], short_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VI, " >0 ", "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void suadd_int() { + for (int i = 0; i < COUNT; i += ispec.length()) { + IntVector.fromArray(ispec, int_in1, i) + .lanewise(VectorOperators.SUADD, + IntVector.fromArray(ispec, int_in2, i)) + .intoArray(int_out, i); + } + } + + @Check(test = "suadd_int") + public void suadd_int_verify() { + for (int i = 0; i < COUNT; i++) { + int actual = int_out[i]; + int expected = VectorMath.addSaturatingUnsigned(int_in1[i], int_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_ADD_VL, " >0 ", "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void suadd_long() { + for (int i = 0; i < COUNT; i += lspec.length()) { + LongVector.fromArray(lspec, long_in1, i) + .lanewise(VectorOperators.SUADD, + LongVector.fromArray(lspec, long_in2, i)) + .intoArray(long_out, i); + } + } + + @Check(test = "suadd_long") + public void suadd_long_verify() { + for (int i = 0; i < COUNT; i++) { + long actual = long_out[i]; + long expected = VectorMath.addSaturatingUnsigned(long_in1[i], long_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VB, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void ssub_byte() { + for (int i = 0; i < COUNT; i += bspec.length()) { + ByteVector.fromArray(bspec, byte_in1, i) + .lanewise(VectorOperators.SSUB, + ByteVector.fromArray(bspec, byte_in2, i)) + .intoArray(byte_out, i); + } + } + + @Check(test = "ssub_byte") + public void ssub_byte_verify() { + for (int i = 0; i < COUNT; i++) { + byte actual = byte_out[i]; + byte expected = VectorMath.subSaturating(byte_in1[i], byte_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void ssub_short() { + for (int i = 0; i < COUNT; i += sspec.length()) { + ShortVector.fromArray(sspec, short_in1, i) + .lanewise(VectorOperators.SSUB, + ShortVector.fromArray(sspec, short_in2, i)) + .intoArray(short_out, i); + } + } + + @Check(test = "ssub_short") + public void ssub_short_verify() { + for (int i = 0; i < COUNT; i++) { + short actual = short_out[i]; + short expected = VectorMath.subSaturating(short_in1[i], short_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VI, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void ssub_int() { + for (int i = 0; i < COUNT; i += ispec.length()) { + IntVector.fromArray(ispec, int_in1, i) + .lanewise(VectorOperators.SSUB, + IntVector.fromArray(ispec, int_in2, i)) + .intoArray(int_out, i); + } + } + + @Check(test = "ssub_int") + public void ssub_int_verify() { + for (int i = 0; i < COUNT; i++) { + int actual = int_out[i]; + int expected = VectorMath.subSaturating(int_in1[i], int_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void ssub_long() { + for (int i = 0; i < COUNT; i += lspec.length()) { + LongVector.fromArray(lspec, long_in1, i) + .lanewise(VectorOperators.SSUB, + LongVector.fromArray(lspec, long_in2, i)) + .intoArray(long_out, i); + } + } + + @Check(test = "ssub_long") + public void ssub_long_verify() { + for (int i = 0; i < COUNT; i++) { + long actual = long_out[i]; + long expected = VectorMath.subSaturating(long_in1[i], long_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VB, " >0 " , "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void susub_byte() { + for (int i = 0; i < COUNT; i += bspec.length()) { + ByteVector.fromArray(bspec, byte_in1, i) + .lanewise(VectorOperators.SUSUB, + ByteVector.fromArray(bspec, byte_in2, i)) + .intoArray(byte_out, i); + } + } + + @Check(test = "susub_byte") + public void susub_byte_verify() { + for (int i = 0; i < COUNT; i++) { + byte actual = byte_out[i]; + byte expected = VectorMath.subSaturatingUnsigned(byte_in1[i], byte_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VS, " >0 ", "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void susub_short() { + for (int i = 0; i < COUNT; i += sspec.length()) { + ShortVector.fromArray(sspec, short_in1, i) + .lanewise(VectorOperators.SUSUB, + ShortVector.fromArray(sspec, short_in2, i)) + .intoArray(short_out, i); + } + } + + @Check(test = "susub_short") + public void susub_short_verify() { + for (int i = 0; i < COUNT; i++) { + short actual = short_out[i]; + short expected = VectorMath.subSaturatingUnsigned(short_in1[i], short_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VI, " >0 ", "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void susub_int() { + for (int i = 0; i < COUNT; i += ispec.length()) { + IntVector.fromArray(ispec, int_in1, i) + .lanewise(VectorOperators.SUSUB, + IntVector.fromArray(ispec, int_in2, i)) + .intoArray(int_out, i); + } + } + + @Check(test = "susub_int") + public void susub_int_verify() { + for (int i = 0; i < COUNT; i++) { + int actual = int_out[i]; + int expected = VectorMath.subSaturatingUnsigned(int_in1[i], int_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } + + @Test + @IR(counts = {IRNode.SATURATING_SUB_VL, " >0 ", "unsigned_vector_node", " >0 "}, + phase = {CompilePhase.BEFORE_MATCHING}, + applyIfCPUFeature = {"avx", "true"}) + @Warmup(value = 10000) + public void susub_long() { + for (int i = 0; i < COUNT; i += lspec.length()) { + LongVector.fromArray(lspec, long_in1, i) + .lanewise(VectorOperators.SUSUB, + LongVector.fromArray(lspec, long_in2, i)) + .intoArray(long_out, i); + } + } + + @Check(test = "susub_long") + public void susub_long_verify() { + for (int i = 0; i < COUNT; i++) { + long actual = long_out[i]; + long expected = VectorMath.subSaturatingUnsigned(long_in1[i], long_in2[i]); + if (actual != expected) { + throw new AssertionError("Result Mismatch : actual (" + actual + ") != expected (" + expected + ")"); + } + } + } +} From 352201ddecb048fe41bdf68d775a0a6cb2080122 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Mon, 2 Dec 2024 17:05:32 +0000 Subject: [PATCH 048/171] 8343788: Provide means to alter lib/tzmappings entries on Windows Reviewed-by: joehw --- .../tools/cldrconverter/CLDRConverter.java | 23 ++++++++++++- .../share/data/tzdata/tzmappings.override | 32 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/java.base/share/data/tzdata/tzmappings.override diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index 8865e3908ae..55dd6a8d6ad 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -33,6 +33,7 @@ import java.time.*; import java.util.*; import java.util.ResourceBundle.Control; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -1242,7 +1243,8 @@ private static Stream zidMapEntry() { private static Stream tzDataLinkEntry() { try { return Files.walk(Paths.get(tzDataDir), 1) - .filter(p -> !Files.isDirectory(p)) + .filter(p -> p.toFile().isFile()) + .filter(p -> p.getFileName().toString().matches("africa|antarctica|asia|australasia|backward|etcetera|europe|northamerica|southamerica")) .flatMap(CLDRConverter::extractLinks) .sorted(); } catch (IOException e) { @@ -1273,8 +1275,27 @@ private static Stream extractLinks(Path tzFile) { // Note: the entries are alphabetically sorted, *except* the "world" region // code, i.e., "001". It should be the last entry for the same windows time // zone name entries. (cf. TimeZone_md.c) + // + // The default entries from CLDR's windowsZones.xml file can be modified + // with /tzmappings.override where mapping overrides + // can be specified. + private static Pattern OVERRIDE_PATTERN = Pattern.compile("(?([^:]+:[^:]+)):(?[^:]+):"); private static void generateWindowsTZMappings() throws Exception { Files.createDirectories(Paths.get(DESTINATION_DIR, "windows", "conf")); + var override = Path.of(tzDataDir, "tzmappings.override"); + if (override.toFile().exists()) { + Files.readAllLines(override).stream() + .map(String::trim) + .filter(o -> !o.isBlank() && !o.startsWith("#")) + .forEach(o -> { + var m = OVERRIDE_PATTERN.matcher(o); + if (m.matches()) { + handlerWinZones.put(m.group("win"), m.group("java")); + } else { + System.out.printf("Unrecognized tzmappings override: %s. Ignored%n", o); + } + }); + } Files.write(Paths.get(DESTINATION_DIR, "windows", "conf", "tzmappings"), handlerWinZones.keySet().stream() .filter(k -> k.endsWith(":001") || diff --git a/src/java.base/share/data/tzdata/tzmappings.override b/src/java.base/share/data/tzdata/tzmappings.override new file mode 100644 index 00000000000..0f0e0c1372c --- /dev/null +++ b/src/java.base/share/data/tzdata/tzmappings.override @@ -0,0 +1,32 @@ +# +# 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. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# 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. +# + +# Extra definitions for Windows /lib/tzmappings file. These entries +# replace the existing (Windows Zone Name):(REGION) entries, or are added +# as new entries + +# Example entries +# Foo Standard Time:US:America/Los_Angeles: +# Bar Standard Time:001:Asia/Tokyo: From 67f18cc9cef0966ccafb21a47043a85b9f39642f Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 2 Dec 2024 17:31:21 +0000 Subject: [PATCH 049/171] 8345142: Remove uses of SecurityManager in Printing related classes Reviewed-by: azvegint, psadhukhan --- .../classes/java/awt/print/PrinterJob.java | 5 --- .../javax/print/PrintServiceLookup.java | 22 +--------- .../share/classes/javax/print/ServiceUI.java | 5 +-- .../classes/sun/print/PSStreamPrintJob.java | 7 +--- .../share/classes/sun/print/PrintJob2D.java | 42 +++++-------------- .../classes/sun/print/RasterPrinterJob.java | 32 ++------------ .../classes/sun/print/ServiceDialog.java | 23 ++-------- .../classes/sun/print/ServiceNotifier.java | 14 ++----- .../classes/sun/print/IPPPrintService.java | 35 ++-------------- .../sun/print/PrintServiceLookupProvider.java | 22 ---------- .../unix/classes/sun/print/UnixPrintJob.java | 18 +------- .../classes/sun/print/UnixPrintService.java | 35 ++-------------- .../classes/sun/awt/windows/WPrinterJob.java | 11 +---- .../sun/print/PrintServiceLookupProvider.java | 21 ---------- .../classes/sun/print/Win32PrintJob.java | 17 +------- .../classes/sun/print/Win32PrintService.java | 37 ++-------------- 16 files changed, 37 insertions(+), 309 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/print/PrinterJob.java b/src/java.desktop/share/classes/java/awt/print/PrinterJob.java index 0d2d2d76b53..b31cf32094c 100644 --- a/src/java.desktop/share/classes/java/awt/print/PrinterJob.java +++ b/src/java.desktop/share/classes/java/awt/print/PrinterJob.java @@ -64,11 +64,6 @@ public abstract class PrinterJob { * @return a new {@code PrinterJob}. */ public static PrinterJob getPrinterJob() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } return sun.print.PlatformPrinterJobProxy.getPrinterJob(); } diff --git a/src/java.desktop/share/classes/javax/print/PrintServiceLookup.java b/src/java.desktop/share/classes/javax/print/PrintServiceLookup.java index 29c7b420f7c..15a7fbbaa00 100644 --- a/src/java.desktop/share/classes/javax/print/PrintServiceLookup.java +++ b/src/java.desktop/share/classes/javax/print/PrintServiceLookup.java @@ -393,16 +393,7 @@ private static ArrayList getServices(DocFlavor flavor, /* * add any directly registered services */ - ArrayList registeredServices = null; - try { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } - registeredServices = getRegisteredServices(); - } catch (SecurityException se) { - } + ArrayList registeredServices = getRegisteredServices(); if (registeredServices != null) { PrintService[] services = registeredServices.toArray( new PrintService[registeredServices.size()]); @@ -458,16 +449,7 @@ private static ArrayList getMultiDocServices(DocFlavor[] f /* * add any directly registered services */ - ArrayList registeredServices = null; - try { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } - registeredServices = getRegisteredServices(); - } catch (Exception e) { - } + ArrayList registeredServices = getRegisteredServices(); if (registeredServices != null) { PrintService[] services = registeredServices.toArray(new PrintService[registeredServices.size()]); diff --git a/src/java.desktop/share/classes/javax/print/ServiceUI.java b/src/java.desktop/share/classes/javax/print/ServiceUI.java index 873d4448ffc..bb25207d6a1 100644 --- a/src/java.desktop/share/classes/javax/print/ServiceUI.java +++ b/src/java.desktop/share/classes/javax/print/ServiceUI.java @@ -203,10 +203,7 @@ public static PrintService printDialog(GraphicsConfiguration gc, flavor, attributes, owner); if (setOnTop) { - try { - dialog.setAlwaysOnTop(true); - } catch (SecurityException e) { - } + dialog.setAlwaysOnTop(true); } Rectangle dlgBounds = dialog.getBounds(); diff --git a/src/java.desktop/share/classes/sun/print/PSStreamPrintJob.java b/src/java.desktop/share/classes/sun/print/PSStreamPrintJob.java index 3a75a3232b5..e9e9aa142b2 100644 --- a/src/java.desktop/share/classes/sun/print/PSStreamPrintJob.java +++ b/src/java.desktop/share/classes/sun/print/PSStreamPrintJob.java @@ -434,12 +434,7 @@ public void pageableJob(Pageable pageable, } /* add the user name to the job */ - String userName = ""; - try { - userName = System.getProperty("user.name"); - } catch (SecurityException se) { - } - + String userName = System.getProperty("user.name"); if (userName == null || userName.isEmpty()) { RequestingUserName ruName = (RequestingUserName)reqSet.get(RequestingUserName.class); diff --git a/src/java.desktop/share/classes/sun/print/PrintJob2D.java b/src/java.desktop/share/classes/sun/print/PrintJob2D.java index fbec484435f..fb0fdbb17c5 100644 --- a/src/java.desktop/share/classes/sun/print/PrintJob2D.java +++ b/src/java.desktop/share/classes/sun/print/PrintJob2D.java @@ -310,12 +310,6 @@ private void initPrintJob2D(Frame frame, String doctitle, JobAttributes jobAttributes, PageAttributes pageAttributes) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } - if (frame == null && (jobAttributes == null || jobAttributes.getDialog() == DialogType.NATIVE)) { @@ -366,11 +360,6 @@ private void initPrintJob2D(Frame frame, String doctitle, } catch (IOException ioe) { throw new IllegalArgumentException("Cannot write to file:"+ destStr); - } catch (SecurityException se) { - //There is already file read/write access so at this point - // only delete access is denied. Just ignore it because in - // most cases the file created in createNewFile gets overwritten - // anyway. } File pFile = f.getParentFile(); @@ -676,29 +665,18 @@ private void copyAttributes(PrintService printServ) { attributes.add(defaultDest); } else { URI uri = null; - try { - if (fileName != null) { - if (fileName.isEmpty()) { - fileName = "."; - } - } else { - // defaultDest should not be null. The following code - // is only added to safeguard against a possible - // buggy implementation of a PrintService having a - // null default Destination. - fileName = "out.prn"; - } - uri = (new File(fileName)).toURI(); - } catch (SecurityException se) { - try { - // '\\' file separator is illegal character in opaque - // part and causes URISyntaxException, so we replace - // it with '/' - fileName = fileName.replace('\\', '/'); - uri = new URI("file:"+fileName); - } catch (URISyntaxException e) { + if (fileName != null) { + if (fileName.isEmpty()) { + fileName = "."; } + } else { + // defaultDest should not be null. The following code + // is only added to safeguard against a possible + // buggy implementation of a PrintService having a + // null default Destination. + fileName = "out.prn"; } + uri = (new File(fileName)).toURI(); if (uri != null) { attributes.add(new Destination(uri)); } diff --git a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index 4e096f506e5..dbd7999fdc0 100644 --- a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -816,10 +816,7 @@ public PageFormat pageDialog(final PrintRequestAttributeSet attributes) DocFlavor.SERVICE_FORMATTED.PAGEABLE, attributes, w); if (setOnTop) { - try { - pageDialog.setAlwaysOnTop(true); - } catch (SecurityException e) { - } + pageDialog.setAlwaysOnTop(true); } Rectangle dlgBounds = pageDialog.getBounds(); @@ -948,15 +945,6 @@ public boolean printDialog(final PrintRequestAttributeSet attributes) } - /* A security check has already been performed in the - * java.awt.print.printerJob.getPrinterJob method. - * So by the time we get here, it is OK for the current thread - * to print either to a file (from a Dialog we control!) or - * to a chosen printer. - * - * We raise privilege when we put up the dialog, to avoid - * the "warning applet window" banner. - */ GraphicsConfiguration grCfg = null; Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); if (w != null) { @@ -1311,11 +1299,7 @@ protected void setAttributes(PrintRequestAttributeSet attributes) (!fidelity && userName != null)) { userNameAttr = userName.getValue(); } else { - try { - userNameAttr = getUserName(); - } catch (SecurityException e) { - userNameAttr = ""; - } + userNameAttr = getUserName(); } /* OpenBook is used internally only when app uses Printable. @@ -1654,11 +1638,6 @@ protected void validateDestination(String dest) throws PrinterException { } catch (IOException ioe) { throw new PrinterException("Cannot write to file:"+ dest); - } catch (SecurityException se) { - //There is already file read/write access so at this point - // only delete access is denied. Just ignore it because in - // most cases the file created in createNewFile gets overwritten - // anyway. } File pFile = f.getParentFile(); @@ -1818,7 +1797,6 @@ protected int getCopiesInt() { /** * Get the name of the printing user. - * The caller must have security permission to read system properties. */ public String getUserName() { return System.getProperty("user.name"); @@ -1831,11 +1809,7 @@ protected String getUserNameInt() { if (userNameAttr != null) { return userNameAttr; } else { - try { - return getUserName(); - } catch (SecurityException e) { - return ""; - } + return getUserName(); } } diff --git a/src/java.desktop/share/classes/sun/print/ServiceDialog.java b/src/java.desktop/share/classes/sun/print/ServiceDialog.java index ba530bbf58f..d6d3b92ee8d 100644 --- a/src/java.desktop/share/classes/sun/print/ServiceDialog.java +++ b/src/java.desktop/share/classes/sun/print/ServiceDialog.java @@ -169,10 +169,7 @@ void initPrintDialog(int x, int y, * on top property */ if ((getOwner() == null) || (owner.getOwner() != getOwner())) { - try { - setAlwaysOnTop(true); - } catch (SecurityException e) { - } + setAlwaysOnTop(true); } } Container c = getContentPane(); @@ -255,10 +252,7 @@ void initPageDialog(int x, int y, /* See comments in same block in initPrintDialog */ DialogOwner owner = (DialogOwner)attributes.get(DialogOwner.class); if ((getOwner() == null) || (owner.getOwner() != getOwner())) { - try { - setAlwaysOnTop(true); - } catch (SecurityException e) { - } + setAlwaysOnTop(true); } } @@ -2937,13 +2931,7 @@ public void setSelected(boolean selected) { private static class ValidatingFileChooser extends JFileChooser { public void approveSelection() { File selected = getSelectedFile(); - boolean exists; - - try { - exists = selected.exists(); - } catch (SecurityException e) { - exists = false; - } + boolean exists = selected.exists(); if (exists) { int val; @@ -2966,11 +2954,6 @@ public void approveSelection() { getMsg("dialog.owtitle"), JOptionPane.WARNING_MESSAGE); return; - } catch (SecurityException se) { - //There is already file read/write access so at this point - // only delete access is denied. Just ignore it because in - // most cases the file created in createNewFile gets - // overwritten anyway. } File pFile = selected.getParentFile(); if ((selected.exists() && diff --git a/src/java.desktop/share/classes/sun/print/ServiceNotifier.java b/src/java.desktop/share/classes/sun/print/ServiceNotifier.java index 8df17defd05..cac06750330 100644 --- a/src/java.desktop/share/classes/sun/print/ServiceNotifier.java +++ b/src/java.desktop/share/classes/sun/print/ServiceNotifier.java @@ -55,12 +55,9 @@ class ServiceNotifier extends Thread { super(null, null, service.getName() + " notifier", 0, false); this.service = service; listeners = new Vector<>(); - try { - setPriority(Thread.NORM_PRIORITY-1); - setDaemon(true); - start(); - } catch (SecurityException e) { - } + setPriority(Thread.NORM_PRIORITY-1); + setDaemon(true); + start(); } void addListener(PrintServiceAttributeListener listener) { @@ -93,10 +90,7 @@ void stopNotifier() { * immediate notification of listeners. */ void wake() { - try { - interrupt(); - } catch (SecurityException e) { - } + interrupt(); } /* A heuristic is used to calculate sleep time. diff --git a/src/java.desktop/unix/classes/sun/print/IPPPrintService.java b/src/java.desktop/unix/classes/sun/print/IPPPrintService.java index 4d217ec6e3e..06533781571 100644 --- a/src/java.desktop/unix/classes/sun/print/IPPPrintService.java +++ b/src/java.desktop/unix/classes/sun/print/IPPPrintService.java @@ -511,11 +511,6 @@ private void initAttributes() { public DocPrintJob createPrintJob() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } // REMIND: create IPPPrintJob return new UnixPrintJob(this); } @@ -587,15 +582,7 @@ public DocPrintJob createPrintJob() { if (flavor == null || flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { - try { return new Destination((new File("out.ps")).toURI()); - } catch (SecurityException se) { - try { - return new Destination(new URI("file:out.ps")); - } catch (URISyntaxException e) { - return null; - } - } } return null; } else if (category == Fidelity.class) { @@ -797,11 +784,7 @@ public DocPrintJob createPrintJob() { return null; } } else if (category == RequestingUserName.class) { - String userName = ""; - try { - userName = System.getProperty("user.name", ""); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name", ""); return new RequestingUserName(userName, null); } else if (category == Sides.class) { // The printer takes care of Sides so if short-edge @@ -1574,15 +1557,7 @@ public boolean isAttributeValueSupported(Attribute attr, } else if (category == Chromaticity.class) { return Chromaticity.COLOR; } else if (category == Destination.class) { - try { - return new Destination((new File("out.ps")).toURI()); - } catch (SecurityException se) { - try { - return new Destination(new URI("file:out.ps")); - } catch (URISyntaxException e) { - return null; - } - } + return new Destination((new File("out.ps")).toURI()); } else if (category == Fidelity.class) { return Fidelity.FIDELITY_FALSE; } else if (category == Finishings.class) { @@ -1674,11 +1649,7 @@ public boolean isAttributeValueSupported(Attribute attr, return new PageRanges(1, Integer.MAX_VALUE); } } else if (category == RequestingUserName.class) { - String userName = ""; - try { - userName = System.getProperty("user.name", ""); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name", ""); return new RequestingUserName(userName, null); } else if (category == SheetCollate.class) { return SheetCollate.UNCOLLATED; diff --git a/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java b/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java index 15025d43d6f..e91ef99ab37 100644 --- a/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java +++ b/src/java.desktop/unix/classes/sun/print/PrintServiceLookupProvider.java @@ -210,12 +210,6 @@ public PrintServiceLookupProvider() { * lead people to assume its guaranteed. */ public synchronized PrintService[] getPrintServices() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } - if (printServices == null || !pollServices) { refreshServices(); } @@ -549,11 +543,6 @@ private PrintService getServiceByName(PrinterName nameAttr) { */ public PrintService[] getPrintServices(DocFlavor flavor, AttributeSet attributes) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } PrintRequestAttributeSet requestSet = null; PrintServiceAttributeSet serviceSet = null; @@ -613,22 +602,11 @@ public PrintService[] getPrintServices(DocFlavor flavor, public MultiDocPrintService[] getMultiDocPrintServices(DocFlavor[] flavors, AttributeSet attributes) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } return new MultiDocPrintService[0]; } public synchronized PrintService getDefaultPrintService() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } - // clear defaultPrintService defaultPrintService = null; String psuri = null; diff --git a/src/java.desktop/unix/classes/sun/print/UnixPrintJob.java b/src/java.desktop/unix/classes/sun/print/UnixPrintJob.java index 1249d29ec72..b8c455a7d00 100644 --- a/src/java.desktop/unix/classes/sun/print/UnixPrintJob.java +++ b/src/java.desktop/unix/classes/sun/print/UnixPrintJob.java @@ -707,12 +707,7 @@ public void pageableJob(Pageable pageable) throws PrintException { } /* add the user name to the job */ - String userName = ""; - try { - userName = System.getProperty("user.name"); - } catch (SecurityException se) { - } - + String userName = System.getProperty("user.name"); if (userName == null || userName.isEmpty()) { RequestingUserName ruName = (RequestingUserName)reqSet.get(RequestingUserName.class); @@ -791,17 +786,6 @@ private void getAttributeValues(DocFlavor flavor) throws PrintException { } catch (Exception e) { throw new PrintException(e); } - // check write access - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - try { - security.checkWrite(mDestination); - } catch (SecurityException se) { - notifyEvent(PrintJobEvent.JOB_FAILED); - throw new PrintException(se); - } - } } } else if (category == JobSheets.class) { if ((JobSheets)attr == JobSheets.NONE) { diff --git a/src/java.desktop/unix/classes/sun/print/UnixPrintService.java b/src/java.desktop/unix/classes/sun/print/UnixPrintService.java index 201487e9ca4..8185abc6005 100644 --- a/src/java.desktop/unix/classes/sun/print/UnixPrintService.java +++ b/src/java.desktop/unix/classes/sun/print/UnixPrintService.java @@ -416,11 +416,6 @@ private boolean isSupportedMedia(MediaSizeName msn) { } public DocPrintJob createPrintJob() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } return new UnixPrintJob(this); } @@ -627,15 +622,7 @@ public Class[] getSupportedAttributeCategories() { } else if (category == Chromaticity.class) { return Chromaticity.COLOR; } else if (category == Destination.class) { - try { - return new Destination((new File("out.ps")).toURI()); - } catch (SecurityException se) { - try { - return new Destination(new URI("file:out.ps")); - } catch (URISyntaxException e) { - return null; - } - } + return new Destination((new File("out.ps")).toURI()); } else if (category == Fidelity.class) { return Fidelity.FIDELITY_FALSE; } else if (category == JobName.class) { @@ -672,11 +659,7 @@ public Class[] getSupportedAttributeCategories() { } else if (category == PageRanges.class) { return new PageRanges(1, Integer.MAX_VALUE); } else if (category == RequestingUserName.class) { - String userName = ""; - try { - userName = System.getProperty("user.name", ""); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name", ""); return new RequestingUserName(userName, null); } else if (category == SheetCollate.class) { return SheetCollate.UNCOLLATED; @@ -733,15 +716,7 @@ private boolean isAutoSense(DocFlavor flavor) { return null; } } else if (category == Destination.class) { - try { return new Destination((new File("out.ps")).toURI()); - } catch (SecurityException se) { - try { - return new Destination(new URI("file:out.ps")); - } catch (URISyntaxException e) { - return null; - } - } } else if (category == JobName.class) { return new JobName("Java Printing", null); } else if (category == JobSheets.class) { @@ -750,11 +725,7 @@ private boolean isAutoSense(DocFlavor flavor) { arr[1] = JobSheets.STANDARD; return arr; } else if (category == RequestingUserName.class) { - String userName = ""; - try { - userName = System.getProperty("user.name", ""); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name", ""); return new RequestingUserName(userName, null); } else if (category == OrientationRequested.class) { if (flavor == null || isServiceFormattedFlavor(flavor)) { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java index 0d41aacde2f..970b317a8a8 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java @@ -1913,16 +1913,7 @@ private void setNativeAttributes(int flags, int fields, int values) { Destination destPrn = (Destination)attributes.get( Destination.class); if (destPrn == null) { - try { - attributes.add(new Destination( - new File("./out.prn").toURI())); - } catch (SecurityException se) { - try { - attributes.add(new Destination( - new URI("file:out.prn"))); - } catch (URISyntaxException e) { - } - } + attributes.add(new Destination(new File("./out.prn").toURI())); } } else { attributes.remove(Destination.class); diff --git a/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java b/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java index 44173fb2abf..aaaae37c6f5 100644 --- a/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java +++ b/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java @@ -106,11 +106,6 @@ public PrintServiceLookupProvider() { * lead people to assume its guaranteed. */ public synchronized PrintService[] getPrintServices() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } if (printServices == null) { refreshServices(); } @@ -207,11 +202,6 @@ boolean matchingService(PrintService service, public PrintService[] getPrintServices(DocFlavor flavor, AttributeSet attributes) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } PrintRequestAttributeSet requestSet = null; PrintServiceAttributeSet serviceSet = null; @@ -273,22 +263,11 @@ public PrintService[] getPrintServices(DocFlavor flavor, public MultiDocPrintService[] getMultiDocPrintServices(DocFlavor[] flavors, AttributeSet attributes) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } return new MultiDocPrintService[0]; } public synchronized PrintService getDefaultPrintService() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } - // Windows does not have notification for a change in default // so we always get the latest. diff --git a/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java b/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java index 53fe183e861..008641418d2 100644 --- a/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java +++ b/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java @@ -592,11 +592,7 @@ public void pageableJob(Pageable pageable) throws PrintException { } /* add the user name to the job */ - String userName = ""; - try { - userName = System.getProperty("user.name"); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name"); if (userName == null || userName.isEmpty()) { RequestingUserName ruName = @@ -674,17 +670,6 @@ private void getAttributeValues(DocFlavor flavor) throws PrintException { } catch (Exception e) { throw new PrintException(e); } - // check write access - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - try { - security.checkWrite(mDestination); - } catch (SecurityException se) { - notifyEvent(PrintJobEvent.JOB_FAILED); - throw new PrintException(se); - } - } } } else if (category == JobName.class) { jobName = ((JobName)attr).getValue(); diff --git a/src/java.desktop/windows/classes/sun/print/Win32PrintService.java b/src/java.desktop/windows/classes/sun/print/Win32PrintService.java index 3eb3549ad84..4706e05a8ff 100644 --- a/src/java.desktop/windows/classes/sun/print/Win32PrintService.java +++ b/src/java.desktop/windows/classes/sun/print/Win32PrintService.java @@ -858,11 +858,6 @@ private boolean isSupportedResolution(PrinterResolution res) { } public DocPrintJob createPrintJob() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPrintJobAccess(); - } return new Win32PrintJob(this); } @@ -1174,15 +1169,7 @@ public Class[] getSupportedAttributeCategories() { } else if (category == SunAlternateMedia.class) { return null; } else if (category == Destination.class) { - try { - return new Destination((new File("out.prn")).toURI()); - } catch (SecurityException se) { - try { - return new Destination(new URI("file:out.prn")); - } catch (URISyntaxException e) { - return null; - } - } + return new Destination((new File("out.prn")).toURI()); } else if (category == Sides.class) { switch(defSides) { case DMDUP_VERTICAL : @@ -1223,11 +1210,7 @@ public Class[] getSupportedAttributeCategories() { } } } else if (category == RequestingUserName.class) { - String userName = ""; - try { - userName = System.getProperty("user.name", ""); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name", ""); return new RequestingUserName(userName, null); } else if (category == SheetCollate.class) { if (defCollate == DMCOLLATE_TRUE) { @@ -1302,11 +1285,7 @@ private boolean isAutoSense(DocFlavor flavor) { if (category == JobName.class) { return new JobName("Java Printing", null); } else if (category == RequestingUserName.class) { - String userName = ""; - try { - userName = System.getProperty("user.name", ""); - } catch (SecurityException se) { - } + String userName = System.getProperty("user.name", ""); return new RequestingUserName(userName, null); } else if (category == ColorSupported.class) { int caps = getPrinterCapabilities(); @@ -1343,15 +1322,7 @@ private boolean isAutoSense(DocFlavor flavor) { return null; } } else if (category == Destination.class) { - try { - return new Destination((new File("out.prn")).toURI()); - } catch (SecurityException se) { - try { - return new Destination(new URI("file:out.prn")); - } catch (URISyntaxException e) { - return null; - } - } + return new Destination((new File("out.prn")).toURI()); } else if (category == OrientationRequested.class) { if (flavor == null || flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || From 7c944ee6f4dda4f1626721d63ac6bc6d1b40d33b Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 2 Dec 2024 17:34:49 +0000 Subject: [PATCH 050/171] 8345172: x86: Some CPU feature asserts are declared as 32-bit only Reviewed-by: dfenacci, kvn --- src/hotspot/cpu/x86/assembler_x86.cpp | 30 +++++++++++----------- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 1 - 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 36a122d2946..c2fcbcea71e 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -2968,7 +2968,7 @@ void Assembler::movb(Register dst, Address src) { } void Assembler::movddup(XMMRegister dst, XMMRegister src) { - NOT_LP64(assert(VM_Version::supports_sse3(), "")); + assert(VM_Version::supports_sse3(), ""); int vector_len = VM_Version::supports_avx512novl() ? AVX_512bit : AVX_128bit; InstructionAttr attributes(vector_len, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_rex_vex_w_reverted(); @@ -2977,7 +2977,7 @@ void Assembler::movddup(XMMRegister dst, XMMRegister src) { } void Assembler::movddup(XMMRegister dst, Address src) { - NOT_LP64(assert(VM_Version::supports_sse3(), "")); + assert(VM_Version::supports_sse3(), ""); InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); attributes.set_address_attributes(/* tuple_type */ EVEX_DUP, /* input_size_in_bits */ EVEX_64bit); @@ -4614,7 +4614,7 @@ void Assembler::vpacksswb(XMMRegister dst, XMMRegister nds, XMMRegister src, int } void Assembler::packssdw(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x6B, (0xC0 | encode)); @@ -4820,7 +4820,7 @@ void Assembler::pcmpestri(XMMRegister dst, XMMRegister src, int imm8) { // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::pcmpeqb(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x74, (0xC0 | encode)); @@ -4968,7 +4968,7 @@ void Assembler::evpcmpeqb(KRegister kdst, KRegister mask, XMMRegister nds, Addre // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::pcmpeqw(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x75, (0xC0 | encode)); @@ -5017,7 +5017,7 @@ void Assembler::evpcmpeqw(KRegister kdst, XMMRegister nds, Address src, int vect // In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::pcmpeqd(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x76, (0xC0 | encode)); @@ -5122,7 +5122,7 @@ void Assembler::pcmpgtq(XMMRegister dst, XMMRegister src) { } void Assembler::pmovmskb(Register dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16((unsigned char)0xD7, (0xC0 | encode)); @@ -5188,7 +5188,7 @@ void Assembler::pextrq(Address dst, XMMRegister src, int imm8) { } void Assembler::pextrw(Register dst, XMMRegister src, int imm8) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int24((unsigned char)0xC5, (0xC0 | encode), imm8); @@ -5274,14 +5274,14 @@ void Assembler::vpinsrq(XMMRegister dst, XMMRegister nds, Register src, int imm8 } void Assembler::pinsrw(XMMRegister dst, Register src, int imm8) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes, true); emit_int24((unsigned char)0xC4, (0xC0 | encode), imm8); } void Assembler::pinsrw(XMMRegister dst, Address src, int imm8) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); @@ -8712,7 +8712,7 @@ void Assembler::pmulld(XMMRegister dst, XMMRegister src) { } void Assembler::pmuludq(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16((unsigned char)0xF4, (0xC0 | encode)); @@ -8813,7 +8813,7 @@ void Assembler::vpminsb(XMMRegister dst, XMMRegister nds, XMMRegister src, int v } void Assembler::pminsw(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16((unsigned char)0xEA, (0xC0 | encode)); @@ -8892,7 +8892,7 @@ void Assembler::vpmaxsb(XMMRegister dst, XMMRegister nds, XMMRegister src, int v } void Assembler::pmaxsw(XMMRegister dst, XMMRegister src) { - assert(VM_Version::supports_sse2(), ""); + NOT_LP64(assert(VM_Version::supports_sse2(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16((unsigned char)0xEE, (0xC0 | encode)); @@ -12401,7 +12401,7 @@ void Assembler::evpternlogq(XMMRegister dst, int imm8, KRegister mask, XMMRegist void Assembler::gf2p8affineqb(XMMRegister dst, XMMRegister src, int imm8) { assert(VM_Version::supports_gfni(), ""); - assert(VM_Version::supports_sse(), ""); + NOT_LP64(assert(VM_Version::supports_sse(), "");) InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24((unsigned char)0xCE, (unsigned char)(0xC0 | encode), imm8); @@ -12409,7 +12409,7 @@ void Assembler::gf2p8affineqb(XMMRegister dst, XMMRegister src, int imm8) { void Assembler::vgf2p8affineqb(XMMRegister dst, XMMRegister src2, XMMRegister src3, int imm8, int vector_len) { assert(VM_Version::supports_gfni(), "requires GFNI support"); - assert(VM_Version::supports_sse(), ""); + NOT_LP64(assert(VM_Version::supports_sse(), "");) InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src3->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24((unsigned char)0xCE, (unsigned char)(0xC0 | encode), imm8); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index c08e535ee0e..a798dea08cc 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5445,7 +5445,6 @@ void MacroAssembler::vallones(XMMRegister dst, int vector_len) { } else if (VM_Version::supports_avx()) { vpcmpeqd(dst, dst, dst, vector_len); } else { - assert(VM_Version::supports_sse2(), ""); pcmpeqd(dst, dst); } } From d6a5f1bafb879258cf5f1d4cd89e9cc272b0c01f Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Mon, 2 Dec 2024 19:30:16 +0000 Subject: [PATCH 051/171] 8344768: Consider removing "sun.security.krb5.autodeducerealm" system property Reviewed-by: mullan --- .../sun/security/krb5/PrincipalName.java | 21 +---- .../classes/sun/security/krb5/Realm.java | 3 - .../sun/security/krb5/name/Constructors.java | 78 +++++++++---------- 3 files changed, 39 insertions(+), 63 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java index 130f2c23f86..9bee88eab9b 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java @@ -99,7 +99,6 @@ public class PrincipalName implements Cloneable { * TGS Name */ public static final String TGS_DEFAULT_SRV_NAME = "krbtgt"; - public static final int TGS_DEFAULT_NT = KRB_NT_SRV_INST; public static final char NAME_COMPONENT_SEPARATOR = '/'; public static final char NAME_REALM_SEPARATOR = '@'; @@ -107,7 +106,6 @@ public class PrincipalName implements Cloneable { public static final String NAME_COMPONENT_SEPARATOR_STR = "/"; public static final String NAME_REALM_SEPARATOR_STR = "@"; - public static final String REALM_COMPONENT_SEPARATOR_STR = "."; private static final boolean NAME_CASE_SENSITIVE_IN_MATCH = "true".equalsIgnoreCase( @@ -135,12 +133,6 @@ public class PrincipalName implements Cloneable { private final Realm nameRealm; // not null - /** - * When constructing a PrincipalName, whether the realm is included in - * the input, or deduced from default realm or domain-realm mapping. - */ - private final boolean realmDeduced; - // cached default salt, not used in clone private transient String salt = null; @@ -161,7 +153,6 @@ public PrincipalName(int nameType, String[] nameStrings, Realm nameRealm) { this.nameType = nameType; this.nameStrings = nameStrings.clone(); this.nameRealm = nameRealm; - this.realmDeduced = false; } // Warning: called by NativeCreds.c @@ -251,7 +242,6 @@ public PrincipalName(DerValue encoding, Realm realm) if (realm == null) { throw new IllegalArgumentException("Null realm not allowed"); } - realmDeduced = false; nameRealm = realm; DerValue der; if (encoding == null) { @@ -405,9 +395,6 @@ public PrincipalName(String name, int type, String realm) realm = Realm.parseRealmAtSeparator(name); } - // No realm info from parameter and string, must deduce later - realmDeduced = realm == null; - switch (type) { case KRB_NT_SRV_HST: if (nameParts.length >= 2) { @@ -437,8 +424,8 @@ public PrincipalName(String name, int type, String realm) hostName.toLowerCase(Locale.ENGLISH) + ".")) { hostName = canonicalized; } - } catch (UnknownHostException | SecurityException e) { - // not canonicalized or no permission to do so, use old + } catch (UnknownHostException e) { + // not canonicalized, use old } if (hostName.endsWith(".")) { hostName = hostName.substring(0, hostName.length() - 1); @@ -726,8 +713,4 @@ static String mapHostToRealm(String name) { } return result; } - - public boolean isRealmDeduced() { - return realmDeduced; - } } diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/Realm.java b/src/java.security.jgss/share/classes/sun/security/krb5/Realm.java index 93dbfe2b237..758c0ce4f64 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/Realm.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Realm.java @@ -47,9 +47,6 @@ */ public class Realm implements Cloneable { - public static final boolean AUTODEDUCEREALM = - Boolean.getBoolean("sun.security.krb5.autodeducerealm"); - private final String realm; // not null nor empty public Realm(String name) throws RealmException { diff --git a/test/jdk/sun/security/krb5/name/Constructors.java b/test/jdk/sun/security/krb5/name/Constructors.java index f3943cc88ef..733a9e421f8 100644 --- a/test/jdk/sun/security/krb5/name/Constructors.java +++ b/test/jdk/sun/security/krb5/name/Constructors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -41,23 +41,22 @@ public static void main(String[] args) throws Exception { // Good ones type = PrincipalName.KRB_NT_UNKNOWN; - checkName("a", type, "R", "R", false, "a"); - checkName("a@R2", type, "R", "R", false, "a"); - checkName("a/b", type, "R", "R", false, "a", "b"); - checkName("a/b@R2", type, "R", "R", false, "a", "b"); - checkName("a/b/c", type, "R", "R", false, "a", "b", "c"); - checkName("a/b/c@R2", type, "R", "R", false, "a", "b", "c"); + checkName("a", type, "R", "R", "a"); + checkName("a@R2", type, "R", "R", "a"); + checkName("a/b", type, "R", "R", "a", "b"); + checkName("a/b@R2", type, "R", "R", "a", "b"); + checkName("a/b/c", type, "R", "R", "a", "b", "c"); + checkName("a/b/c@R2", type, "R", "R", "a", "b", "c"); // Weird ones - checkName("a\\/b", type, "R", "R", false, "a/b"); - checkName("a\\/b\\/c", type, "R", "R", false, "a/b/c"); - checkName("a\\/b\\@R2", type, "R", "R", false, "a/b@R2"); + checkName("a\\/b", type, "R", "R", "a/b"); + checkName("a\\/b\\/c", type, "R", "R", "a/b/c"); + checkName("a\\/b\\@R2", type, "R", "R", "a/b@R2"); // Bad ones - checkName("a", type, "", null, false); - checkName("a/", type, "R", null, false); - checkName("/a", type, "R", null, false); - checkName("a//b", type, "R", null, false); - checkName("a@", type, null, null, false); - type = PrincipalName.KRB_NT_SRV_HST; + checkName("a", type, "", null); + checkName("a/", type, "R", null); + checkName("/a", type, "R", null); + checkName("a//b", type, "R", null); + checkName("a@", type, null, null); // Part 2: on realm choices @@ -78,17 +77,17 @@ public static void main(String[] args) throws Exception { if (testNoDefaultDomain) { type = PrincipalName.KRB_NT_UNKNOWN; - checkName("a", type, "R1", "R1", false, "a"); // arg - checkName("a@R1", type, null, "R1", false, "a"); // or r in name - checkName("a@R2", type, "R1", "R1", false, "a"); // arg over r - checkName("a", type, null, null, false); // fail if none - checkName("a/b@R1", type, null, "R1", false, "a", "b"); + checkName("a", type, "R1", "R1", "a"); // arg + checkName("a@R1", type, null, "R1", "a"); // or r in name + checkName("a@R2", type, "R1", "R1", "a"); // arg over r + checkName("a", type, null, null); // fail if none + checkName("a/b@R1", type, null, "R1", "a", "b"); type = PrincipalName.KRB_NT_SRV_HST; // Let's pray "b.h" won't be canonicalized - checkName("a/b.h", type, "R1", "R1", false, "a", "b.h"); // arg - checkName("a/b.h@R1", type, null, "R1", false, "a", "b.h"); // or r in name - checkName("a/b.h@R1", type, "R2", "R2", false, "a", "b.h"); // arg over r - checkName("a/b.h", type, null, null, false); // fail if none + checkName("a/b.h", type, "R1", "R1", "a", "b.h"); // arg + checkName("a/b.h@R1", type, null, "R1", "a", "b.h"); // or r in name + checkName("a/b.h@R1", type, "R2", "R2", "a", "b.h"); // arg over r + checkName("a/b.h", type, null, null); // fail if none } // When there is default realm @@ -97,25 +96,25 @@ public static void main(String[] args) throws Exception { Config.refresh(); type = PrincipalName.KRB_NT_UNKNOWN; - checkName("a", type, "R1", "R1", false, "a"); // arg - checkName("a@R1", type, null, "R1", false, "a"); // or r in name - checkName("a@R2", type, "R1", "R1", false, "a"); // arg over r - checkName("a", type, null, "R", true, "a"); // default - checkName("a/b", type, null, "R", true, "a", "b"); + checkName("a", type, "R1", "R1", "a"); // arg + checkName("a@R1", type, null, "R1", "a"); // or r in name + checkName("a@R2", type, "R1", "R1", "a"); // arg over r + checkName("a", type, null, "R", "a"); // default + checkName("a/b", type, null, "R", "a", "b"); type = PrincipalName.KRB_NT_SRV_HST; - checkName("a/b.h3", type, "R1", "R1", false, "a", "b.h3"); // arg - checkName("a/b.h@R1", type, null, "R1", false, "a", "b.h"); // or r in name - checkName("a/b.h3@R2", type, "R1", "R1", false, "a", "b.h3"); // arg over r - checkName("a/b.h2", type, "R1", "R1", false, "a", "b.h2"); // arg over map - checkName("a/b.h2@R1", type, null, "R1", false, "a", "b.h2"); // r over map - checkName("a/b.h2", type, null, "R2", true, "a", "b.h2"); // map - checkName("a/b.h", type, null, "R", true, "a", "b.h"); // default + checkName("a/b.h3", type, "R1", "R1", "a", "b.h3"); // arg + checkName("a/b.h@R1", type, null, "R1", "a", "b.h"); // or r in name + checkName("a/b.h3@R2", type, "R1", "R1", "a", "b.h3"); // arg over r + checkName("a/b.h2", type, "R1", "R1", "a", "b.h2"); // arg over map + checkName("a/b.h2@R1", type, null, "R1", "a", "b.h2"); // r over map + checkName("a/b.h2", type, null, "R2", "a", "b.h2"); // map + checkName("a/b.h", type, null, "R", "a", "b.h"); // default } // Check if the creation matches the expected output. // Note: realm == null means creation failure static void checkName(String n, int t, String s, - String realm, boolean deduced, String... parts) + String realm, String... parts) throws Exception { PrincipalName pn = null; try { @@ -132,8 +131,5 @@ static void checkName(String n, int t, String s, throw new Exception(pn.toString() + " vs " + Arrays.toString(parts) + "@" + realm); } - if (deduced != pn.isRealmDeduced()) { - throw new Exception("pn.realmDeduced is " + pn.isRealmDeduced()); - } } } From 525f33baaea2cc559ddd2396611a7734a64a9d66 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 2 Dec 2024 19:43:47 +0000 Subject: [PATCH 052/171] 8345324: Update comment in SourceVersion for language evolution history for changes in 24 Reviewed-by: iris --- .../share/classes/javax/lang/model/SourceVersion.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java index 231a5e34c62..59461b54a2b 100644 --- a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java +++ b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java @@ -77,7 +77,10 @@ public enum SourceVersion { * switch in preview, module Import Declarations in preview, * implicitly declared classes and instance main in third * preview, flexible constructor bodies in second preview) - * 24: tbd + * 24: no changes (primitive Types in Patterns, instanceof, and + * switch in second preview, module Import Declarations in second + * preview, simple source files and instance main in fourth + * preview, flexible constructor bodies in third preview) */ /** From 3d0d0e62900653c4e395166a9ac48578b3dbc1f8 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Mon, 2 Dec 2024 20:54:58 +0000 Subject: [PATCH 053/171] 8345012: os::build_agent_function_name potentially wastes a byte when allocating the buffer Reviewed-by: stuefe, shade --- src/hotspot/os/posix/os_posix.cpp | 46 --------------------- src/hotspot/os/windows/os_windows.cpp | 51 ----------------------- src/hotspot/share/runtime/os.cpp | 58 +++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 97 deletions(-) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 3ae692b9e88..61214a42969 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -941,51 +941,6 @@ void os::naked_yield() { sched_yield(); } -// Builds a platform dependent Agent_OnLoad_ function name -// which is used to find statically linked in agents. -// Parameters: -// sym_name: Symbol in library we are looking for -// lib_name: Name of library to look in, null for shared libs. -// is_absolute_path == true if lib_name is absolute path to agent -// such as "/a/b/libL.so" -// == false if only the base name of the library is passed in -// such as "L" -char* os::build_agent_function_name(const char *sym_name, const char *lib_name, - bool is_absolute_path) { - char *agent_entry_name; - size_t len; - size_t name_len; - size_t prefix_len = strlen(JNI_LIB_PREFIX); - size_t suffix_len = strlen(JNI_LIB_SUFFIX); - const char *start; - - if (lib_name != nullptr) { - name_len = strlen(lib_name); - if (is_absolute_path) { - // Need to strip path, prefix and suffix - if ((start = strrchr(lib_name, *os::file_separator())) != nullptr) { - lib_name = ++start; - } - if (strlen(lib_name) <= (prefix_len + suffix_len)) { - return nullptr; - } - lib_name += prefix_len; - name_len = strlen(lib_name) - suffix_len; - } - } - len = (lib_name != nullptr ? name_len : 0) + strlen(sym_name) + 2; - agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread); - if (agent_entry_name == nullptr) { - return nullptr; - } - strcpy(agent_entry_name, sym_name); - if (lib_name != nullptr) { - strcat(agent_entry_name, "_"); - strncat(agent_entry_name, lib_name, name_len); - } - return agent_entry_name; -} - // Sleep forever; naked call to OS-specific sleep; use with CAUTION void os::infinite_sleep() { while (true) { // sleep forever ... @@ -2231,4 +2186,3 @@ const void* os::get_saved_assert_context(const void** sigInfo) { *sigInfo = nullptr; return nullptr; } - diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 45163765184..849fc0c29f0 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -5835,57 +5835,6 @@ void* os::get_default_process_handle() { return (void*)GetModuleHandle(nullptr); } -// Builds a platform dependent Agent_OnLoad_ function name -// which is used to find statically linked in agents. -// Parameters: -// sym_name: Symbol in library we are looking for -// lib_name: Name of library to look in, null for shared libs. -// is_absolute_path == true if lib_name is absolute path to agent -// such as "C:/a/b/L.dll" -// == false if only the base name of the library is passed in -// such as "L" -char* os::build_agent_function_name(const char *sym_name, const char *lib_name, - bool is_absolute_path) { - char *agent_entry_name; - size_t len; - size_t name_len; - size_t prefix_len = strlen(JNI_LIB_PREFIX); - size_t suffix_len = strlen(JNI_LIB_SUFFIX); - const char *start; - - if (lib_name != nullptr) { - len = name_len = strlen(lib_name); - if (is_absolute_path) { - // Need to strip path, prefix and suffix - if ((start = strrchr(lib_name, *os::file_separator())) != nullptr) { - lib_name = ++start; - } else { - // Need to check for drive prefix - if ((start = strchr(lib_name, ':')) != nullptr) { - lib_name = ++start; - } - } - if (len <= (prefix_len + suffix_len)) { - return nullptr; - } - lib_name += prefix_len; - name_len = strlen(lib_name) - suffix_len; - } - } - len = (lib_name != nullptr ? name_len : 0) + strlen(sym_name) + 2; - agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread); - if (agent_entry_name == nullptr) { - return nullptr; - } - - strcpy(agent_entry_name, sym_name); - if (lib_name != nullptr) { - strcat(agent_entry_name, "_"); - strncat(agent_entry_name, lib_name, name_len); - } - return agent_entry_name; -} - /* All the defined signal names for Windows. diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 8094261fe56..f631556858f 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2481,3 +2481,61 @@ jint os::set_minimum_stack_sizes() { } return JNI_OK; } + +// Builds a platform dependent Agent_OnLoad_ function name +// which is used to find statically linked in agents. +// Parameters: +// sym_name: Symbol in library we are looking for +// lib_name: Name of library to look in, null for shared libs. +// is_absolute_path == true if lib_name is absolute path to agent +// such as "C:/a/b/L.dll" or "/a/b/libL.so" +// == false if only the base name of the library is passed in +// such as "L" +char* os::build_agent_function_name(const char *sym_name, const char *lib_name, + bool is_absolute_path) { + char *agent_entry_name; + size_t len = 0; + size_t name_len = 0; + size_t prefix_len = strlen(JNI_LIB_PREFIX); + size_t suffix_len = strlen(JNI_LIB_SUFFIX); + size_t underscore_len = 0; // optional underscore if lib_name is set + const char *start; + + if (lib_name != nullptr) { + if (is_absolute_path) { + // Need to strip path, prefix and suffix + if ((start = strrchr(lib_name, *os::file_separator())) != nullptr) { + lib_name = ++start; + } +#ifdef WINDOWS + else { // Need to check for drive prefix e.g. C:L.dll + if ((start = strchr(lib_name, ':')) != nullptr) { + lib_name = ++start; + } + } +#endif + name_len = strlen(lib_name); + if (name_len <= (prefix_len + suffix_len)) { + return nullptr; + } + lib_name += prefix_len; + name_len = strlen(lib_name) - suffix_len; + } else { + name_len = strlen(lib_name); + } + underscore_len = 1; + } + // Total buffer length to allocate - includes null terminator. + len = strlen(sym_name) + underscore_len + name_len + 1; + agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread); + if (agent_entry_name == nullptr) { + return nullptr; + } + + strcpy(agent_entry_name, sym_name); + if (lib_name != nullptr) { + strcat(agent_entry_name, "_"); + strncat(agent_entry_name, lib_name, name_len); + } + return agent_entry_name; +} From 940aa7c4cf1bf770690660c8bb21fb3ddc5186e4 Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Mon, 2 Dec 2024 21:30:53 +0000 Subject: [PATCH 054/171] 8344397: Remove Security Manager dependencies from java.security and sun.security packages Reviewed-by: rriggs, hchao, weijun, alanb --- .../share/classes/java/lang/Class.java | 4 +- .../java/security/AccessControlContext.java | 8 - .../share/classes/java/security/Provider.java | 57 ++---- .../classes/java/security/SecureRandom.java | 6 +- .../share/classes/java/security/Security.java | 42 +--- .../sun/security/action/GetBooleanAction.java | 96 ---------- .../sun/security/action/GetIntegerAction.java | 165 ---------------- .../sun/security/action/GetLongAction.java | 114 ----------- .../security/action/GetPropertyAction.java | 99 +--------- .../sun/security/action/PutAllAction.java | 56 ------ .../share/classes/sun/security/ec/SunEC.java | 12 +- .../TlsRsaPremasterSecretParameterSpec.java | 8 +- .../sun/security/jca/ProviderConfig.java | 179 +++++++----------- .../sun/security/jca/ProviderList.java | 11 +- .../sun/security/provider/ConfigFile.java | 69 ++----- .../classes/sun/security/provider/DRBG.java | 7 +- .../provider/FileInputStreamPool.java | 8 +- .../classes/sun/security/provider/MD4.java | 10 +- .../sun/security/provider/SeedGenerator.java | 165 +++++++--------- .../classes/sun/security/provider/Sun.java | 23 +-- .../sun/security/provider/SunEntries.java | 38 ++-- .../provider/VerificationProvider.java | 19 +- .../sun/security/provider/certpath/OCSP.java | 6 +- .../provider/certpath/URICertStore.java | 6 +- .../sun/security/rsa/RSAKeyFactory.java | 5 +- .../classes/sun/security/rsa/SunRsaSign.java | 19 +- .../util/AbstractAlgorithmConstraints.java | 13 +- .../classes/sun/security/util/Debug.java | 6 +- .../classes/sun/security/util/DomainName.java | 23 +-- .../sun/security/util/FilePermCompat.java | 4 +- .../sun/security/util/HostnameChecker.java | 4 +- .../LazyCodeSourcePermissionCollection.java | 128 ------------- .../sun/security/util/LocalizedMessage.java | 6 +- .../sun/security/util/SecurityConstants.java | 72 +------ .../sun/security/util/SecurityProperties.java | 111 +++++++++-- .../util/SecurityProviderConstants.java | 6 +- .../security/util/SignatureFileVerifier.java | 4 +- .../sun/security/provider/NativePRNG.java | 138 ++++++-------- .../internal/ObjectFactoriesFilter.java | 4 +- .../classes/sun/security/krb5/Config.java | 4 +- .../sun/security/krb5/Credentials.java | 2 +- .../sun/security/krb5/PrincipalName.java | 2 +- .../internal/ccache/FileCredentialsCache.java | 2 +- test/jdk/sun/security/action/Generify.java | 29 --- .../GetLongAction/ReturnNullIfNoDefault.java | 54 ------ 45 files changed, 407 insertions(+), 1437 deletions(-) delete mode 100644 src/java.base/share/classes/sun/security/action/GetBooleanAction.java delete mode 100644 src/java.base/share/classes/sun/security/action/GetIntegerAction.java delete mode 100644 src/java.base/share/classes/sun/security/action/GetLongAction.java delete mode 100644 src/java.base/share/classes/sun/security/action/PutAllAction.java delete mode 100644 src/java.base/share/classes/sun/security/util/LazyCodeSourcePermissionCollection.java delete mode 100644 test/jdk/sun/security/action/GetLongAction/ReturnNullIfNoDefault.java diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 771084384c8..23b8ac3fb90 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -53,6 +53,7 @@ import java.lang.reflect.TypeVariable; import java.lang.constant.Constable; import java.net.URL; +import java.security.AllPermission; import java.security.Permissions; import java.security.ProtectionDomain; import java.util.ArrayList; @@ -89,7 +90,6 @@ import sun.reflect.generics.repository.MethodRepository; import sun.reflect.generics.repository.ConstructorRepository; import sun.reflect.generics.scope.ClassScope; -import sun.security.util.SecurityConstants; import sun.reflect.annotation.*; import sun.reflect.misc.ReflectUtil; @@ -2720,7 +2720,7 @@ private static class Holder { private static final ProtectionDomain allPermDomain; static { Permissions perms = new Permissions(); - perms.add(SecurityConstants.ALL_PERMISSION); + perms.add(new AllPermission()); allPermDomain = new ProtectionDomain(null, perms); } } diff --git a/src/java.base/share/classes/java/security/AccessControlContext.java b/src/java.base/share/classes/java/security/AccessControlContext.java index 6c73e6339fb..01c7244fe2c 100644 --- a/src/java.base/share/classes/java/security/AccessControlContext.java +++ b/src/java.base/share/classes/java/security/AccessControlContext.java @@ -44,14 +44,6 @@ public final class AccessControlContext { private ProtectionDomain[] context; - // isPrivileged and isAuthorized are referenced by the VM - do not remove - // or change their names - private boolean isPrivileged; - private boolean isAuthorized = false; - - // Note: This field is directly used by the virtual machine - // native codes. Don't touch it. - private AccessControlContext privilegedContext; @SuppressWarnings("removal") private DomainCombiner combiner = null; diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index 7012bcc9eeb..da3f53b9632 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -345,12 +345,6 @@ public String toString() { return name + " version " + versionStr; } - /* - * override the following methods to ensure that provider - * information can only be changed if the caller has the appropriate - * permissions. - */ - /** * Clears this {@code Provider} so that it no longer contains the properties * used to look up facilities implemented by the {@code Provider}. @@ -359,7 +353,7 @@ public String toString() { */ @Override public synchronized void clear() { - check("clearProviderProperties."+name); + checkInitialized(); if (debug != null) { debug.println("Remove " + name + " provider properties"); } @@ -376,7 +370,7 @@ public synchronized void clear() { */ @Override public synchronized void load(InputStream inStream) throws IOException { - check("putProviderProperty."+name); + checkInitialized(); if (debug != null) { debug.println("Load " + name + " provider properties"); } @@ -394,7 +388,7 @@ public synchronized void load(InputStream inStream) throws IOException { */ @Override public synchronized void putAll(Map t) { - check("putProviderProperty."+name); + checkInitialized(); if (debug != null) { debug.println("Put all " + name + " provider properties"); } @@ -461,7 +455,7 @@ public Collection values() { */ @Override public synchronized Object put(Object key, Object value) { - check("putProviderProperty."+name); + checkInitialized(); if (debug != null) { debug.println("Set " + name + " provider property [" + key + "/" + value +"]"); @@ -478,7 +472,7 @@ public synchronized Object put(Object key, Object value) { */ @Override public synchronized Object putIfAbsent(Object key, Object value) { - check("putProviderProperty."+name); + checkInitialized(); if (debug != null) { debug.println("Set " + name + " provider property [" + key + "/" + value +"]"); @@ -494,7 +488,7 @@ public synchronized Object putIfAbsent(Object key, Object value) { */ @Override public synchronized Object remove(Object key) { - check("removeProviderProperty."+name); + checkInitialized(); if (debug != null) { debug.println("Remove " + name + " provider property " + key); } @@ -509,7 +503,7 @@ public synchronized Object remove(Object key) { */ @Override public synchronized boolean remove(Object key, Object value) { - check("removeProviderProperty."+name); + checkInitialized(); if (debug != null) { debug.println("Remove " + name + " provider property " + key); } @@ -525,7 +519,7 @@ public synchronized boolean remove(Object key, Object value) { @Override public synchronized boolean replace(Object key, Object oldValue, Object newValue) { - check("putProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("Replace " + name + " provider property " + key); } @@ -540,7 +534,7 @@ public synchronized boolean replace(Object key, Object oldValue, */ @Override public synchronized Object replace(Object key, Object value) { - check("putProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("Replace " + name + " provider property " + key); } @@ -558,7 +552,7 @@ public synchronized Object replace(Object key, Object value) { @Override public synchronized void replaceAll(BiFunction function) { - check("putProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("ReplaceAll " + name + " provider property "); } @@ -575,8 +569,7 @@ public synchronized void replaceAll(BiFunction remappingFunction) { - check("putProviderProperty." + name); - check("removeProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("Compute " + name + " provider property " + key); } @@ -594,8 +587,7 @@ public synchronized Object compute(Object key, BiFunction mappingFunction) { - check("putProviderProperty." + name); - check("removeProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("ComputeIfAbsent " + name + " provider property " + key); @@ -613,8 +605,7 @@ public synchronized Object computeIfAbsent(Object key, public synchronized Object computeIfPresent(Object key, BiFunction remappingFunction) { - check("putProviderProperty." + name); - check("removeProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("ComputeIfPresent " + name + " provider property " + key); @@ -635,8 +626,7 @@ public synchronized Object computeIfPresent(Object key, public synchronized Object merge(Object key, Object value, BiFunction remappingFunction) { - check("putProviderProperty." + name); - check("removeProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println("Merge " + name + " provider property " + key); } @@ -694,15 +684,6 @@ private void checkInitialized() { } } - private void check(String directive) { - checkInitialized(); - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkSecurityAccess(directive); - } - } - // legacyMap changed since last call to getServices() private transient volatile boolean legacyChanged; // serviceMap changed since last call to getServices() @@ -789,8 +770,6 @@ private static boolean checkLegacy(Object key) { /** * Copies all the mappings from the specified Map to this provider. - * Internal method to be called AFTER the security check has been - * performed. */ private void implPutAll(Map t) { for (Map.Entry e : t.entrySet()) { @@ -1239,7 +1218,7 @@ public Set getServices() { * @since 1.5 */ protected void putService(Service s) { - check("putProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println(name + ".putService(): " + s); } @@ -1303,7 +1282,7 @@ Service getDefaultSecureRandomService() { private void putPropertyStrings(Service s) { String type = s.getType(); String algorithm = s.getAlgorithm(); - // use super() to avoid permission check and other processing + // use super() to avoid other processing super.put(type + "." + algorithm, s.getClassName()); for (String alias : s.getAliases()) { super.put(ALIAS_PREFIX + type + "." + alias, algorithm); @@ -1321,7 +1300,7 @@ private void putPropertyStrings(Service s) { private void removePropertyStrings(Service s) { String type = s.getType(); String algorithm = s.getAlgorithm(); - // use super() to avoid permission check and other processing + // use super() to avoid other processing super.remove(type + "." + algorithm); for (String alias : s.getAliases()) { super.remove(ALIAS_PREFIX + type + "." + alias); @@ -1346,7 +1325,7 @@ private void removePropertyStrings(Service s) { * @since 1.5 */ protected void removeService(Service s) { - check("removeProviderProperty." + name); + checkInitialized(); if (debug != null) { debug.println(name + ".removeService(): " + s); } diff --git a/src/java.base/share/classes/java/security/SecureRandom.java b/src/java.base/share/classes/java/security/SecureRandom.java index 734f25e6615..e6cc1134c09 100644 --- a/src/java.base/share/classes/java/security/SecureRandom.java +++ b/src/java.base/share/classes/java/security/SecureRandom.java @@ -942,11 +942,7 @@ private static final class StrongPatternHolder { public static SecureRandom getInstanceStrong() throws NoSuchAlgorithmException { - @SuppressWarnings("removal") - String property = AccessController.doPrivileged( - (PrivilegedAction) () -> Security.getProperty( - "securerandom.strongAlgorithms")); - + String property = Security.getProperty("securerandom.strongAlgorithms"); if (property == null || property.isEmpty()) { throw new NoSuchAlgorithmException( "Null/empty securerandom.strongAlgorithms Security Property"); diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index aab793b98e9..6969fe8a8e1 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -312,14 +312,7 @@ private static void debugLoad(boolean start, Object source) { } static { - // doPrivileged here because there are multiple - // things in initialize that might require privs. - // (the FileInputStream call and the File.exists call, etc) - @SuppressWarnings("removal") - var dummy = AccessController.doPrivileged((PrivilegedAction) () -> { - initialize(); - return null; - }); + initialize(); // Set up JavaSecurityPropertiesAccess in SharedSecrets SharedSecrets.setJavaSecurityPropertiesAccess(new JavaSecurityPropertiesAccess() { @Override @@ -475,15 +468,13 @@ public static String getAlgorithmProperty(String algName, */ public static synchronized int insertProviderAt(Provider provider, int position) { - String providerName = provider.getName(); - checkInsertProvider(providerName); ProviderList list = Providers.getFullProviderList(); ProviderList newList = ProviderList.insertAt(list, provider, position - 1); if (list == newList) { return -1; } Providers.setProviderList(newList); - return newList.getIndex(providerName) + 1; + return newList.getIndex(provider.getName()) + 1; } /** @@ -527,7 +518,6 @@ public static int addProvider(Provider provider) { * @see #addProvider */ public static synchronized void removeProvider(String name) { - check("removeProvider." + name); ProviderList list = Providers.getFullProviderList(); ProviderList newList = ProviderList.remove(list, name); Providers.setProviderList(newList); @@ -822,7 +812,6 @@ static Object[] getImpl(String algorithm, String type, Provider provider, */ public static String getProperty(String key) { SecPropLoader.checkReservedKey(key); - check("getProperty." + key); String name = props.getProperty(key); if (name != null) name = name.trim(); // could be a class name with trailing ws @@ -845,7 +834,6 @@ public static String getProperty(String key) { */ public static void setProperty(String key, String datum) { SecPropLoader.checkReservedKey(key); - check("setProperty." + key); props.put(key, datum); SecurityPropertyModificationEvent spe = new SecurityPropertyModificationEvent(); @@ -859,32 +847,6 @@ public static void setProperty(String key, String datum) { } } - private static void check(String directive) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkSecurityAccess(directive); - } - } - - private static void checkInsertProvider(String name) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - try { - security.checkSecurityAccess("insertProvider"); - } catch (SecurityException se1) { - try { - security.checkSecurityAccess("insertProvider." + name); - } catch (SecurityException se2) { - // throw first exception, but add second to suppressed - se1.addSuppressed(se2); - throw se1; - } - } - } - } - private static class Criteria { private final String serviceName; private final String algName; diff --git a/src/java.base/share/classes/sun/security/action/GetBooleanAction.java b/src/java.base/share/classes/sun/security/action/GetBooleanAction.java deleted file mode 100644 index d41954601e8..00000000000 --- a/src/java.base/share/classes/sun/security/action/GetBooleanAction.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 1998, 2022, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.security.action; - -import java.security.AccessController; - -/** - * A convenience class for retrieving the boolean value of a system property - * as a privileged action. - * - *

An instance of this class can be used as the argument of - * AccessController.doPrivileged. - * - *

The following code retrieves the boolean value of the system - * property named "prop" as a privileged action: - * - *

- * boolean b = java.security.AccessController.doPrivileged
- *              (new GetBooleanAction("prop")).booleanValue();
- * 
- * - * @author Roland Schemers - * @see java.security.PrivilegedAction - * @see java.security.AccessController - * @since 1.2 - */ - -public class GetBooleanAction - implements java.security.PrivilegedAction { - private final String theProp; - - /** - * Constructor that takes the name of the system property whose boolean - * value needs to be determined. - * - * @param theProp the name of the system property. - */ - public GetBooleanAction(String theProp) { - this.theProp = theProp; - } - - /** - * Determines the boolean value of the system property whose name was - * specified in the constructor. - * - * @return the Boolean value of the system property. - */ - public Boolean run() { - return Boolean.getBoolean(theProp); - } - - /** - * Convenience method to get a property without going through doPrivileged - * if no security manager is present. This is unsafe for inclusion in a - * public API but allowable here since this class is now encapsulated. - * - * Note that this method performs a privileged action using caller-provided - * inputs. The caller of this method should take care to ensure that the - * inputs are not tainted and the returned property is not made accessible - * to untrusted code if it contains sensitive information. - * - * @param theProp the name of the system property. - */ - @SuppressWarnings("removal") - public static boolean privilegedGetProperty(String theProp) { - if (System.getSecurityManager() == null) { - return Boolean.getBoolean(theProp); - } else { - return AccessController.doPrivileged( - new GetBooleanAction(theProp)); - } - } -} diff --git a/src/java.base/share/classes/sun/security/action/GetIntegerAction.java b/src/java.base/share/classes/sun/security/action/GetIntegerAction.java deleted file mode 100644 index 2d9a598149c..00000000000 --- a/src/java.base/share/classes/sun/security/action/GetIntegerAction.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 1998, 2022, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.security.action; - -import java.security.AccessController; - -/** - * A convenience class for retrieving the integer value of a system property - * as a privileged action. - * - *

An instance of this class can be used as the argument of - * AccessController.doPrivileged. - * - *

The following code retrieves the integer value of the system - * property named "prop" as a privileged action. Since it does - * not pass a default value to be used in case the property - * "prop" is not defined, it has to check the result for - * null: - * - *

- * Integer tmp = java.security.AccessController.doPrivileged
- *     (new sun.security.action.GetIntegerAction("prop"));
- * int i;
- * if (tmp != null) {
- *     i = tmp.intValue();
- * }
- * 
- * - *

The following code retrieves the integer value of the system - * property named "prop" as a privileged action, and also passes - * a default value to be used in case the property "prop" is not - * defined: - * - *

- * int i = ((Integer)java.security.AccessController.doPrivileged(
- *                         new GetIntegerAction("prop", 3))).intValue();
- * 
- * - * @author Roland Schemers - * @see java.security.PrivilegedAction - * @see java.security.AccessController - * @since 1.2 - */ - -public class GetIntegerAction - implements java.security.PrivilegedAction { - private final String theProp; - private final int defaultVal; - private final boolean defaultSet; - - /** - * Constructor that takes the name of the system property whose integer - * value needs to be determined. - * - * @param theProp the name of the system property. - */ - public GetIntegerAction(String theProp) { - this.theProp = theProp; - this.defaultVal = 0; - this.defaultSet = false; - } - - /** - * Constructor that takes the name of the system property and the default - * value of that property. - * - * @param theProp the name of the system property. - * @param defaultVal the default value. - */ - public GetIntegerAction(String theProp, int defaultVal) { - this.theProp = theProp; - this.defaultVal = defaultVal; - this.defaultSet = true; - } - - /** - * Determines the integer value of the system property whose name was - * specified in the constructor. - * - *

If there is no property of the specified name, or if the property - * does not have the correct numeric format, then an Integer - * object representing the default value that was specified in the - * constructor is returned, or null if no default value was - * specified. - * - * @return the Integer value of the property. - */ - public Integer run() { - Integer value = Integer.getInteger(theProp); - if ((value == null) && defaultSet) - return defaultVal; - return value; - } - - /** - * Convenience method to get a property without going through doPrivileged - * if no security manager is present. This is unsafe for inclusion in a - * public API but allowable here since this class is now encapsulated. - * - * Note that this method performs a privileged action using caller-provided - * inputs. The caller of this method should take care to ensure that the - * inputs are not tainted and the returned property is not made accessible - * to untrusted code if it contains sensitive information. - * - * @param theProp the name of the system property. - */ - @SuppressWarnings("removal") - public static Integer privilegedGetProperty(String theProp) { - if (System.getSecurityManager() == null) { - return Integer.getInteger(theProp); - } else { - return AccessController.doPrivileged( - new GetIntegerAction(theProp)); - } - } - - /** - * Convenience method to get a property without going through doPrivileged - * if no security manager is present. This is unsafe for inclusion in a - * public API but allowable here since this class is now encapsulated. - * - * Note that this method performs a privileged action using caller-provided - * inputs. The caller of this method should take care to ensure that the - * inputs are not tainted and the returned property is not made accessible - * to untrusted code if it contains sensitive information. - * - * @param theProp the name of the system property. - * @param defaultVal the default value. - */ - @SuppressWarnings("removal") - public static Integer privilegedGetProperty(String theProp, - int defaultVal) { - Integer value; - if (System.getSecurityManager() == null) { - value = Integer.getInteger(theProp); - } else { - value = AccessController.doPrivileged( - new GetIntegerAction(theProp)); - } - return (value != null) ? value : defaultVal; - } -} diff --git a/src/java.base/share/classes/sun/security/action/GetLongAction.java b/src/java.base/share/classes/sun/security/action/GetLongAction.java deleted file mode 100644 index 795f3af239e..00000000000 --- a/src/java.base/share/classes/sun/security/action/GetLongAction.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 1998, 2022, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.security.action; - -/** - * A convenience class for retrieving the Long value of a system - * property as a privileged action. - * - *

An instance of this class can be used as the argument of - * AccessController.doPrivileged. - * - *

The following code retrieves the Long value of the system - * property named "prop" as a privileged action. Since it does - * not pass a default value to be used in case the property - * "prop" is not defined, it has to check the result for - * null: - * - *

- * Long tmp = java.security.AccessController.doPrivileged
- *     (new sun.security.action.GetLongAction("prop"));
- * long l;
- * if (tmp != null) {
- *     l = tmp.longValue();
- * }
- * 
- * - *

The following code retrieves the Long value of the system - * property named "prop" as a privileged action, and also passes - * a default value to be used in case the property "prop" is not - * defined: - * - *

- * long l = java.security.AccessController.doPrivileged
- *      (new GetLongAction("prop")).longValue();
- * 
- * - * @author Roland Schemers - * @see java.security.PrivilegedAction - * @see java.security.AccessController - * @since 1.2 - */ - -public class GetLongAction implements java.security.PrivilegedAction { - private final String theProp; - private final long defaultVal; - private final boolean defaultSet; - - /** - * Constructor that takes the name of the system property whose - * Long value needs to be determined. - * - * @param theProp the name of the system property. - */ - public GetLongAction(String theProp) { - this.theProp = theProp; - this.defaultVal = 0; - this.defaultSet = false; - } - - /** - * Constructor that takes the name of the system property and the default - * value of that property. - * - * @param theProp the name of the system property. - * @param defaultVal the default value. - */ - public GetLongAction(String theProp, long defaultVal) { - this.theProp = theProp; - this.defaultVal = defaultVal; - this.defaultSet = true; - } - - /** - * Determines the Long value of the system property whose - * name was specified in the constructor. - * - *

If there is no property of the specified name, or if the property - * does not have the correct numeric format, then a Long - * object representing the default value that was specified in the - * constructor is returned, or null if no default value was - * specified. - * - * @return the Long value of the property. - */ - public Long run() { - Long value = Long.getLong(theProp); - if ((value == null) && defaultSet) - return defaultVal; - return value; - } -} diff --git a/src/java.base/share/classes/sun/security/action/GetPropertyAction.java b/src/java.base/share/classes/sun/security/action/GetPropertyAction.java index 347072de9f9..8954c615cba 100644 --- a/src/java.base/share/classes/sun/security/action/GetPropertyAction.java +++ b/src/java.base/share/classes/sun/security/action/GetPropertyAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -27,9 +27,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.Locale; import java.util.Properties; -import sun.security.util.Debug; /** * A convenience class for retrieving the string value of a system @@ -162,99 +160,4 @@ public Properties run() { ); } } - - /** - * Convenience method for fetching System property values that are timeouts. - * Accepted timeout values may be purely numeric, a numeric value - * followed by "s" (both interpreted as seconds), or a numeric value - * followed by "ms" (interpreted as milliseconds). - * - * @param prop the name of the System property - * @param def a default value (in milliseconds) - * @param dbg a Debug object, if null no debug messages will be sent - * - * @return an integer value corresponding to the timeout value in the System - * property in milliseconds. If the property value is empty, negative, - * or contains non-numeric characters (besides a trailing "s" or "ms") - * then the default value will be returned. If a negative value for - * the "def" parameter is supplied, zero will be returned if the - * property's value does not conform to the allowed syntax. - */ - public static int privilegedGetTimeoutProp(String prop, int def, Debug dbg) { - if (def < 0) { - def = 0; - } - - String rawPropVal = privilegedGetProperty(prop, "").trim(); - if (rawPropVal.length() == 0) { - return def; - } - - // Determine if "ms" or just "s" is on the end of the string. - // We may do a little surgery on the value so we'll retain - // the original value in rawPropVal for debug messages. - boolean isMillis = false; - String propVal = rawPropVal; - if (rawPropVal.toLowerCase(Locale.ROOT).endsWith("ms")) { - propVal = rawPropVal.substring(0, rawPropVal.length() - 2); - isMillis = true; - } else if (rawPropVal.toLowerCase(Locale.ROOT).endsWith("s")) { - propVal = rawPropVal.substring(0, rawPropVal.length() - 1); - } - - // Next check to make sure the string is built only from digits - if (propVal.matches("^\\d+$")) { - try { - int timeout = Integer.parseInt(propVal); - return isMillis ? timeout : timeout * 1000; - } catch (NumberFormatException nfe) { - if (dbg != null) { - dbg.println("Warning: Unexpected " + nfe + - " for timeout value " + rawPropVal + - ". Using default value of " + def + " msec."); - } - return def; - } - } else { - if (dbg != null) { - dbg.println("Warning: Incorrect syntax for timeout value " + - rawPropVal + ". Using default value of " + def + - " msec."); - } - return def; - } - } - - /** - * Convenience method for fetching System property values that are booleans. - * - * @param prop the name of the System property - * @param def a default value - * @param dbg a Debug object, if null no debug messages will be sent - * - * @return a boolean value corresponding to the value in the System property. - * If the property value is neither "true" or "false", the default value - * will be returned. - */ - public static boolean privilegedGetBooleanProp(String prop, boolean def, Debug dbg) { - String rawPropVal = privilegedGetProperty(prop, ""); - if ("".equals(rawPropVal)) { - return def; - } - - String lower = rawPropVal.toLowerCase(Locale.ROOT); - if ("true".equals(lower)) { - return true; - } else if ("false".equals(lower)) { - return false; - } else { - if (dbg != null) { - dbg.println("Warning: Unexpected value for " + prop + - ": " + rawPropVal + - ". Using default value: " + def); - } - return def; - } - } - } diff --git a/src/java.base/share/classes/sun/security/action/PutAllAction.java b/src/java.base/share/classes/sun/security/action/PutAllAction.java deleted file mode 100644 index d8b17993213..00000000000 --- a/src/java.base/share/classes/sun/security/action/PutAllAction.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2003, 2011, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.security.action; - -import java.util.Map; - -import java.security.Provider; -import java.security.PrivilegedAction; - -/** - * A convenience PrivilegedAction class for setting the properties of - * a provider. See the SunRsaSign provider for a usage example. - * - * @see sun.security.rsa.SunRsaSign - * @author Andreas Sterbenz - * @since 1.5 - */ -public class PutAllAction implements PrivilegedAction { - - private final Provider provider; - private final Map map; - - public PutAllAction(Provider provider, Map map) { - this.provider = provider; - this.map = map; - } - - public Void run() { - provider.putAll(map); - return null; - } - -} diff --git a/src/java.base/share/classes/sun/security/ec/SunEC.java b/src/java.base/share/classes/sun/security/ec/SunEC.java index 7f8c4dba002..ce38ffe9f53 100644 --- a/src/java.base/share/classes/sun/security/ec/SunEC.java +++ b/src/java.base/share/classes/sun/security/ec/SunEC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -25,10 +25,8 @@ package sun.security.ec; -import java.security.AccessController; import java.security.InvalidParameterException; import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedAction; import java.security.Provider; import java.security.ProviderException; import java.util.HashMap; @@ -180,15 +178,9 @@ public Object newInstance(Object ctrParamObj) } } - @SuppressWarnings("removal") public SunEC() { super("SunEC", PROVIDER_VER, "Sun Elliptic Curve provider"); - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - putEntries(); - return null; - } - }); + putEntries(); } void putEntries() { diff --git a/src/java.base/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java b/src/java.base/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java index 21b9db97909..fa19acfaeb6 100644 --- a/src/java.base/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java +++ b/src/java.base/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -25,8 +25,6 @@ package sun.security.internal.spec; -import sun.security.action.GetBooleanAction; - import java.security.spec.AlgorithmParameterSpec; /** @@ -54,8 +52,8 @@ public class TlsRsaPremasterSecretParameterSpec * Default is "false" (old behavior) for compatibility reasons in * SSLv3/TLSv1. Later protocols (TLSv1.1+) do not use this property. */ - private static final boolean rsaPreMasterSecretFix = GetBooleanAction - .privilegedGetProperty("com.sun.net.ssl.rsaPreMasterSecretFix"); + private static final boolean rsaPreMasterSecretFix = + Boolean.getBoolean("com.sun.net.ssl.rsaPreMasterSecretFix"); private final int clientVersion; private final int serverVersion; diff --git a/src/java.base/share/classes/sun/security/jca/ProviderConfig.java b/src/java.base/share/classes/sun/security/jca/ProviderConfig.java index ace87630dac..ce954b3b6a5 100644 --- a/src/java.base/share/classes/sun/security/jca/ProviderConfig.java +++ b/src/java.base/share/classes/sun/security/jca/ProviderConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -94,23 +94,11 @@ final class ProviderConfig { // avoid if not available (pre Solaris 10) to reduce startup time // or if disabled via system property private void checkSunPKCS11Solaris() { - @SuppressWarnings("removal") - Boolean o = AccessController.doPrivileged( - new PrivilegedAction() { - public Boolean run() { - File file = new File("/usr/lib/libpkcs11.so"); - if (file.exists() == false) { - return Boolean.FALSE; - } - if ("false".equalsIgnoreCase(System.getProperty - ("sun.security.pkcs11.enable-solaris"))) { - return Boolean.FALSE; - } - return Boolean.TRUE; - } - }); - if (o == Boolean.FALSE) { - tries = MAX_LOAD_TRIES; + File file = new File("/usr/lib/libpkcs11.so"); + if (file.exists() == false || + ("false".equalsIgnoreCase(System.getProperty + ("sun.security.pkcs11.enable-solaris")))) { + tries = MAX_LOAD_TRIES; } } @@ -190,28 +178,22 @@ Provider getProvider() { case "Apple", "apple.security.AppleProvider" -> { // Reflection is needed for compile time as the class // is not available for non-macosx systems - @SuppressWarnings("removal") - var tmp = AccessController.doPrivileged( - new PrivilegedAction() { - public Provider run() { - try { - Class c = Class.forName( - "apple.security.AppleProvider"); - if (Provider.class.isAssignableFrom(c)) { - @SuppressWarnings("deprecation") - Object tmp = c.newInstance(); - return (Provider) tmp; - } - } catch (Exception ex) { - if (debug != null) { - debug.println("Error loading provider Apple"); - ex.printStackTrace(); - } - } - return null; - } - }); - yield tmp; + Provider ap = null; + try { + Class c = Class.forName( + "apple.security.AppleProvider"); + if (Provider.class.isAssignableFrom(c)) { + @SuppressWarnings("deprecation") + Object tmp = c.newInstance(); + ap = (Provider) tmp; + } + } catch (Exception ex) { + if (debug != null) { + debug.println("Error loading provider Apple"); + ex.printStackTrace(); + } + } + yield ap; } default -> { if (isLoading) { @@ -240,83 +222,69 @@ public Provider run() { /** * Load and instantiate the Provider described by this class. * - * NOTE use of doPrivileged(). - * * @return null if the Provider could not be loaded * * @throws ProviderException if executing the Provider's constructor * throws a ProviderException. All other Exceptions are ignored. */ - @SuppressWarnings("removal") private Provider doLoadProvider() { - return AccessController.doPrivileged(new PrivilegedAction() { - public Provider run() { + if (debug != null) { + debug.println("Loading provider " + ProviderConfig.this); + } + try { + Provider p = ProviderLoader.INSTANCE.load(provName); + if (p != null) { + if (hasArgument()) { + p = p.configure(argument); + } if (debug != null) { - debug.println("Loading provider " + ProviderConfig.this); + debug.println("Loaded provider " + p.getName()); } - try { - Provider p = ProviderLoader.INSTANCE.load(provName); - if (p != null) { - if (hasArgument()) { - p = p.configure(argument); - } - if (debug != null) { - debug.println("Loaded provider " + p.getName()); - } - } else { - if (debug != null) { - debug.println("Error loading provider " + - ProviderConfig.this); - } - disableLoad(); - } - return p; - } catch (Exception e) { - if (e instanceof ProviderException) { - // pass up - throw e; - } else { - if (debug != null) { - debug.println("Error loading provider " + - ProviderConfig.this); - e.printStackTrace(); - } - disableLoad(); - return null; - } - } catch (ExceptionInInitializerError err) { - // no sufficient permission to initialize provider class - if (debug != null) { - debug.println("Error loading provider " + ProviderConfig.this); - err.printStackTrace(); - } - disableLoad(); - return null; + } else { + if (debug != null) { + debug.println("Error loading provider " + + ProviderConfig.this); } + disableLoad(); + } + return p; + } catch (Exception e) { + if (e instanceof ProviderException) { + // pass up + throw e; + } else { + if (debug != null) { + debug.println("Error loading provider " + + ProviderConfig.this); + e.printStackTrace(); + } + disableLoad(); + return null; } - }); + } catch (ExceptionInInitializerError err) { + // unable to initialize provider class + if (debug != null) { + debug.println("Error loading provider " + ProviderConfig.this); + err.printStackTrace(); + } + disableLoad(); + return null; + } } /** * Perform property expansion of the provider value. - * - * NOTE use of doPrivileged(). */ - @SuppressWarnings("removal") private static String expand(final String value) { // shortcut if value does not contain any properties if (value.contains("${") == false) { return value; } - return AccessController.doPrivileged(new PrivilegedAction() { - public String run() { - try { - return PropertyExpander.expand(value); - } catch (GeneralSecurityException e) { - throw new ProviderException(e); - } - } - }); + try { + return PropertyExpander.expand(value); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } } // Inner class for loading security providers listed in java.security file @@ -356,9 +324,9 @@ public Provider load(String pn) { if (pName.equals(pn)) { return p; } - } catch (SecurityException | ServiceConfigurationError | + } catch (ServiceConfigurationError | InvalidParameterException ex) { - // if provider loading fail due to security permission, + // if provider loading failed // log it and move on to next provider if (debug != null) { debug.println("Encountered " + ex + @@ -385,6 +353,7 @@ public Provider load(String pn) { } } + @SuppressWarnings("deprecation") // Class.newInstance private Provider legacyLoad(String classname) { if (debug != null) { @@ -403,15 +372,7 @@ private Provider legacyLoad(String classname) { return null; } - @SuppressWarnings("removal") - Provider p = AccessController.doPrivileged - (new PrivilegedExceptionAction() { - @SuppressWarnings("deprecation") // Class.newInstance - public Provider run() throws Exception { - return (Provider) provClass.newInstance(); - } - }); - return p; + return (Provider) provClass.newInstance(); } catch (Exception e) { Throwable t; if (e instanceof InvocationTargetException) { @@ -429,7 +390,7 @@ public Provider run() throws Exception { } return null; } catch (ExceptionInInitializerError | NoClassDefFoundError err) { - // no sufficient permission to access/initialize provider class + // unable to access/initialize provider class if (debug != null) { debug.println("Error loading legacy provider " + classname); err.printStackTrace(); diff --git a/src/java.base/share/classes/sun/security/jca/ProviderList.java b/src/java.base/share/classes/sun/security/jca/ProviderList.java index 033ad2e9210..b8357140543 100644 --- a/src/java.base/share/classes/sun/security/jca/ProviderList.java +++ b/src/java.base/share/classes/sun/security/jca/ProviderList.java @@ -27,8 +27,6 @@ import java.util.*; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.Provider; import java.security.Provider.Service; import java.security.Security; @@ -87,15 +85,8 @@ public Service getService(String type, String algorithm) { // construct a ProviderList from the security properties // (static provider configuration in the java.security file) - @SuppressWarnings("removal") static ProviderList fromSecurityProperties() { - // doPrivileged() because of Security.getProperty() - return AccessController.doPrivileged( - new PrivilegedAction() { - public ProviderList run() { - return new ProviderList(); - } - }); + return new ProviderList(); } public static ProviderList add(ProviderList providerList, Provider p) { diff --git a/src/java.base/share/classes/sun/security/provider/ConfigFile.java b/src/java.base/share/classes/sun/security/provider/ConfigFile.java index 775e36c61ba..3642463ade8 100644 --- a/src/java.base/share/classes/sun/security/provider/ConfigFile.java +++ b/src/java.base/share/classes/sun/security/provider/ConfigFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -29,15 +29,10 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.security.Security; import java.security.URIParameter; import java.text.MessageFormat; import java.util.*; -import javax.security.auth.AuthPermission; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; import javax.security.auth.login.Configuration; @@ -159,34 +154,18 @@ public Spi(URI uri) { } } - @SuppressWarnings("removal") public Spi(final Configuration.Parameters params) throws IOException { - // call in a doPrivileged - // - // We have already passed the Configuration.getInstance - // security check. Also, this class is not freely accessible - // (it is in the "sun" package). - - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Void run() throws IOException { - if (params == null) { - init(); - } else { - if (!(params instanceof URIParameter)) { - throw new IllegalArgumentException - ("Unrecognized parameter: " + params); - } - URIParameter uriParam = (URIParameter)params; - url = uriParam.getURI().toURL(); - init(); - } - return null; - } - }); - } catch (PrivilegedActionException pae) { - throw (IOException)pae.getException(); + if (params == null) { + init(); + } else { + if (!(params instanceof URIParameter)) { + throw new IllegalArgumentException + ("Unrecognized parameter: " + params); + } + URIParameter uriParam = (URIParameter)params; + url = uriParam.getURI().toURL(); + init(); } // if init() throws some other RuntimeException, @@ -198,8 +177,6 @@ public Void run() throws IOException { * configured URL. * * @throws IOException if the Configuration can not be initialized - * @throws SecurityException if the caller does not have permission - * to initialize the Configuration */ private void init() throws IOException { @@ -377,31 +354,15 @@ private void init(URL config, /** * Refresh and reload the Configuration by re-reading all the * login configurations. - * - * @throws SecurityException if the caller does not have permission - * to refresh the Configuration. */ - @SuppressWarnings("removal") @Override public synchronized void engineRefresh() { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission( - new AuthPermission("refreshLoginConfiguration")); + try { + init(); + } catch (IOException ioe) { + throw new SecurityException(ioe.getLocalizedMessage(), ioe); } - - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - try { - init(); - } catch (IOException ioe) { - throw new SecurityException(ioe.getLocalizedMessage(), - ioe); - } - return null; - } - }); } private void readConfig(Reader reader, diff --git a/src/java.base/share/classes/sun/security/provider/DRBG.java b/src/java.base/share/classes/sun/security/provider/DRBG.java index 01958285e43..a340a866068 100644 --- a/src/java.base/share/classes/sun/security/provider/DRBG.java +++ b/src/java.base/share/classes/sun/security/provider/DRBG.java @@ -27,9 +27,7 @@ import java.io.IOException; import java.io.InvalidObjectException; -import java.security.AccessController; import java.security.DrbgParameters; -import java.security.PrivilegedAction; import java.security.SecureRandomParameters; import java.security.SecureRandomSpi; import java.security.Security; @@ -93,10 +91,7 @@ public DRBG(SecureRandomParameters params) { byte[] nonce = null; // Can be configured with a security property - - @SuppressWarnings("removal") - String config = AccessController.doPrivileged((PrivilegedAction) - () -> Security.getProperty(PROP_NAME)); + String config = Security.getProperty(PROP_NAME); if (config != null && !config.isEmpty()) { for (String part : config.split(",")) { diff --git a/src/java.base/share/classes/sun/security/provider/FileInputStreamPool.java b/src/java.base/share/classes/sun/security/provider/FileInputStreamPool.java index 67ad6946806..a0aae674d69 100644 --- a/src/java.base/share/classes/sun/security/provider/FileInputStreamPool.java +++ b/src/java.base/share/classes/sun/security/provider/FileInputStreamPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -65,9 +65,6 @@ class FileInputStreamPool { * @throws FileNotFoundException if the file does not exist, is a directory * rather than a regular file, or for some * other reason cannot be opened for reading. - * @throws SecurityException if a security manager exists and its - * checkRead method denies read - * access to the file. */ static InputStream getInputStream(File file) throws IOException { @@ -78,9 +75,6 @@ static InputStream getInputStream(File file) throws IOException { } // canonicalize the path - // (this also checks the read permission on the file if SecurityManager - // is present, so no checking is needed later when we just return the - // already opened stream) File cfile = file.getCanonicalFile(); // check if it exists in pool diff --git a/src/java.base/share/classes/sun/security/provider/MD4.java b/src/java.base/share/classes/sun/security/provider/MD4.java index 80cd85a8378..97b8a39c0d6 100644 --- a/src/java.base/share/classes/sun/security/provider/MD4.java +++ b/src/java.base/share/classes/sun/security/provider/MD4.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -69,13 +69,7 @@ public final class MD4 extends DigestBase { @java.io.Serial private static final long serialVersionUID = -8850464997518327965L; }; - @SuppressWarnings("removal") - var dummy = AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - md4Provider.put("MessageDigest.MD4", "sun.security.provider.MD4"); - return null; - } - }); + md4Provider.put("MessageDigest.MD4", "sun.security.provider.MD4"); } public static MessageDigest getInstance() { diff --git a/src/java.base/share/classes/sun/security/provider/SeedGenerator.java b/src/java.base/share/classes/sun/security/provider/SeedGenerator.java index b4c9355bccf..892afced83e 100644 --- a/src/java.base/share/classes/sun/security/provider/SeedGenerator.java +++ b/src/java.base/share/classes/sun/security/provider/SeedGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -149,7 +149,6 @@ public static void generateSeed(byte[] result) { /** * Retrieve some system information, hashed. */ - @SuppressWarnings("removal") static byte[] getSystemEntropy() { final MessageDigest md; @@ -164,57 +163,48 @@ static byte[] getSystemEntropy() { byte b =(byte)System.currentTimeMillis(); md.update(b); - java.security.AccessController.doPrivileged - (new java.security.PrivilegedAction<>() { - @Override - public Void run() { - try { - // System properties can change from machine to machine - Properties p = System.getProperties(); - for (String s: p.stringPropertyNames()) { - md.update(s.getBytes()); - md.update(p.getProperty(s).getBytes()); - } + try { + // System properties can change from machine to machine + Properties p = System.getProperties(); + for (String s: p.stringPropertyNames()) { + md.update(s.getBytes()); + md.update(p.getProperty(s).getBytes()); + } - // Include network adapter names (and a Mac address) - addNetworkAdapterInfo(md); - - // The temporary dir - File f = new File(p.getProperty("java.io.tmpdir")); - int count = 0; - try ( - DirectoryStream stream = - Files.newDirectoryStream(f.toPath())) { - // We use a Random object to choose what file names - // should be used. Otherwise, on a machine with too - // many files, the same first 1024 files always get - // used. Any, We make sure the first 512 files are - // always used. - Random r = new Random(); - for (Path entry: stream) { - if (count < 512 || r.nextBoolean()) { - md.update(entry.getFileName() - .toString().getBytes()); - } - if (count++ > 1024) { - break; - } - } - } - } catch (Exception ex) { - md.update((byte)ex.hashCode()); + // Include network adapter names (and a Mac address) + addNetworkAdapterInfo(md); + + // The temporary dir + File f = new File(p.getProperty("java.io.tmpdir")); + int count = 0; + try (DirectoryStream stream = + Files.newDirectoryStream(f.toPath())) { + // We use a Random object to choose what file names + // should be used. Otherwise, on a machine with too + // many files, the same first 1024 files always get + // used. Any, We make sure the first 512 files are + // always used. + Random r = new Random(); + for (Path entry: stream) { + if (count < 512 || r.nextBoolean()) { + md.update(entry.getFileName().toString().getBytes()); } + if (count++ > 1024) { + break; + } + } + } + } catch (Exception ex) { + md.update((byte)ex.hashCode()); + } - // get Runtime memory stats - Runtime rt = Runtime.getRuntime(); - byte[] memBytes = longToByteArray(rt.totalMemory()); - md.update(memBytes, 0, memBytes.length); - memBytes = longToByteArray(rt.freeMemory()); - md.update(memBytes, 0, memBytes.length); + // get Runtime memory stats + Runtime rt = Runtime.getRuntime(); + byte[] memBytes = longToByteArray(rt.totalMemory()); + md.update(memBytes, 0, memBytes.length); + memBytes = longToByteArray(rt.freeMemory()); + md.update(memBytes, 0, memBytes.length); - return null; - } - }); return md.digest(); } @@ -293,29 +283,19 @@ private static class ThreadedSeedGenerator extends SeedGenerator , e); } - final ThreadGroup[] finalsg = new ThreadGroup[1]; - @SuppressWarnings("removal") - Thread t = java.security.AccessController.doPrivileged - (new java.security.PrivilegedAction<>() { - @Override - public Thread run() { - ThreadGroup parent, group = - Thread.currentThread().getThreadGroup(); - while ((parent = group.getParent()) != null) { - group = parent; - } - finalsg[0] = new ThreadGroup - (group, "SeedGenerator ThreadGroup"); - Thread newT = new Thread(finalsg[0], - ThreadedSeedGenerator.this, - "SeedGenerator Thread", - 0, - false); - newT.setPriority(Thread.MIN_PRIORITY); - newT.setDaemon(true); - return newT; - } - }); + ThreadGroup[] finalsg = new ThreadGroup[1]; + ThreadGroup parent, group = Thread.currentThread().getThreadGroup(); + while ((parent = group.getParent()) != null) { + group = parent; + } + finalsg[0] = new ThreadGroup(group, "SeedGenerator ThreadGroup"); + Thread t = new Thread(finalsg[0], + ThreadedSeedGenerator.this, + "SeedGenerator Thread", + 0, + false); + t.setPriority(Thread.MIN_PRIORITY); + t.setDaemon(true); seedGroup = finalsg[0]; t.start(); } @@ -502,34 +482,25 @@ static class URLSeedGenerator extends SeedGenerator { init(); } - @SuppressWarnings("removal") private void init() throws IOException { @SuppressWarnings("deprecation") - final URL device = new URL(deviceName); + URL device = new URL(deviceName); try { - seedStream = java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction<>() { - @Override - public InputStream run() throws IOException { - /* - * return a shared InputStream for file URLs and - * avoid buffering. - * The URL.openStream() call wraps InputStream in a - * BufferedInputStream which - * can buffer up to 8K bytes. This read is a - * performance issue for entropy sources which - * can be slow to replenish. - */ - if (device.getProtocol().equalsIgnoreCase("file")) { - File deviceFile = - SunEntries.getDeviceFile(device); - return FileInputStreamPool - .getInputStream(deviceFile); - } else { - return device.openStream(); - } - } - }); + /* + * return a shared InputStream for file URLs and + * avoid buffering. + * The URL.openStream() call wraps InputStream in a + * BufferedInputStream which + * can buffer up to 8K bytes. This read is a + * performance issue for entropy sources which + * can be slow to replenish. + */ + if (device.getProtocol().equalsIgnoreCase("file")) { + File deviceFile = SunEntries.getDeviceFile(device); + seedStream = FileInputStreamPool.getInputStream(deviceFile); + } else { + seedStream = device.openStream(); + } } catch (Exception e) { throw new IOException( "Failed to open " + deviceName, e.getCause()); diff --git a/src/java.base/share/classes/sun/security/provider/Sun.java b/src/java.base/share/classes/sun/security/provider/Sun.java index 9c441216cee..fbd19ef633e 100644 --- a/src/java.base/share/classes/sun/security/provider/Sun.java +++ b/src/java.base/share/classes/sun/security/provider/Sun.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -47,7 +47,6 @@ public final class Sun extends Provider { "PKIX CertPathBuilder; LDAP, Collection CertStores, JavaPolicy Policy; " + "JavaLoginConfig Configuration)"; - @SuppressWarnings("removal") public Sun() { /* We are the SUN provider */ super("SUN", PROVIDER_VER, INFO); @@ -55,24 +54,8 @@ public Sun() { Provider p = this; Iterator serviceIter = new SunEntries(p).iterator(); - // if there is no security manager installed, put directly into - // the provider - if (System.getSecurityManager() == null) { - putEntries(serviceIter); - } else { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - putEntries(serviceIter); - return null; - } - }); - } - } - - void putEntries(Iterator i) { - while (i.hasNext()) { - putService(i.next()); + while (serviceIter.hasNext()) { + putService(serviceIter.next()); } } } diff --git a/src/java.base/share/classes/sun/security/provider/SunEntries.java b/src/java.base/share/classes/sun/security/provider/SunEntries.java index 36278ae445f..9f5e3447aeb 100644 --- a/src/java.base/share/classes/sun/security/provider/SunEntries.java +++ b/src/java.base/share/classes/sun/security/provider/SunEntries.java @@ -30,8 +30,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.Provider; import java.security.Security; import java.util.HashMap; @@ -39,7 +37,6 @@ import java.util.LinkedHashSet; import jdk.internal.util.StaticProperty; -import sun.security.action.GetBooleanAction; import static sun.security.util.SecurityProviderConstants.getAliases; @@ -345,29 +342,24 @@ private void addWithAlias(Provider p, String type, String algo, String cn, private static final String PROP_RNDSOURCE = "securerandom.source"; private static final boolean useLegacyDSA = - GetBooleanAction.privilegedGetProperty - ("jdk.security.legacyDSAKeyPairGenerator"); + Boolean.getBoolean("jdk.security.legacyDSAKeyPairGenerator"); static final String URL_DEV_RANDOM = "file:/dev/random"; static final String URL_DEV_URANDOM = "file:/dev/urandom"; - @SuppressWarnings("removal") - private static final String seedSource = AccessController.doPrivileged( - new PrivilegedAction() { - - @Override - public String run() { - String egdSource = System.getProperty(PROP_EGD, ""); - if (egdSource.length() != 0) { - return egdSource; - } - egdSource = Security.getProperty(PROP_RNDSOURCE); - if (egdSource == null) { - return ""; - } - return egdSource; - } - }); + private static final String seedSource = getOverridableSeedSource(); + + private static String getOverridableSeedSource() { + String egdSource = System.getProperty(PROP_EGD, ""); + if (egdSource.length() != 0) { + return egdSource; + } + egdSource = Security.getProperty(PROP_RNDSOURCE); + if (egdSource == null) { + return ""; + } + return egdSource; + } static { DEF_SECURE_RANDOM_ALGO = (NativePRNG.isAvailable() && @@ -386,8 +378,6 @@ static String getSeedSource() { * URISyntaxException we make a best effort for backwards * compatibility. e.g. space character in deviceName string. * - * Method called within PrivilegedExceptionAction block. - * * Moved from SeedGenerator to avoid initialization problems with * signed providers. */ diff --git a/src/java.base/share/classes/sun/security/provider/VerificationProvider.java b/src/java.base/share/classes/sun/security/provider/VerificationProvider.java index 5734a6f4c3b..7f5959b8e77 100644 --- a/src/java.base/share/classes/sun/security/provider/VerificationProvider.java +++ b/src/java.base/share/classes/sun/security/provider/VerificationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -61,7 +61,6 @@ public final class VerificationProvider extends Provider { ACTIVE = b; } - @SuppressWarnings("removal") public VerificationProvider() { super("SunJarVerification", PROVIDER_VER, "Jar Verification Provider"); // register all algorithms normally registered by the Sun and SunRsaSign @@ -75,20 +74,8 @@ public VerificationProvider() { Iterator rsaIter = new SunRsaSignEntries(p).iterator(); - // if there is no security manager installed, put directly into - // the provider - if (System.getSecurityManager() == null) { - putEntries(sunIter); - putEntries(rsaIter); - } else { - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - putEntries(sunIter); - putEntries(rsaIter); - return null; - } - }); - } + putEntries(sunIter); + putEntries(rsaIter); } void putEntries(Iterator i) { diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java index 6f1f7b6ad73..a32d88605c5 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java @@ -38,10 +38,10 @@ import java.util.List; import java.util.Map; -import sun.security.action.GetPropertyAction; import sun.security.util.Debug; import sun.security.util.Event; import sun.security.util.IOUtils; +import sun.security.util.SecurityProperties; import sun.security.x509.AccessDescription; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.GeneralName; @@ -114,7 +114,7 @@ public final class OCSP { */ private static int initializeTimeout(String prop, int def) { int timeoutVal = - GetPropertyAction.privilegedGetTimeoutProp(prop, def, debug); + SecurityProperties.getTimeoutSystemProp(prop, def, debug); if (debug != null) { debug.println(prop + " set to " + timeoutVal + " milliseconds"); } @@ -123,7 +123,7 @@ private static int initializeTimeout(String prop, int def) { private static boolean initializeBoolean(String prop, boolean def) { boolean value = - GetPropertyAction.privilegedGetBooleanProp(prop, def, debug); + SecurityProperties.getBooleanSystemProp(prop, def, debug); if (debug != null) { debug.println(prop + " set to " + value); } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java index 44f11cb0985..28729a56dbd 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -51,12 +51,12 @@ import java.util.List; import java.util.Locale; -import sun.security.action.GetPropertyAction; import sun.security.x509.AccessDescription; import sun.security.x509.GeneralNameInterface; import sun.security.x509.URIName; import sun.security.util.Cache; import sun.security.util.Debug; +import sun.security.util.SecurityProperties; /** * A CertStore that retrieves Certificates or @@ -175,7 +175,7 @@ class URICertStore extends CertStoreSpi { */ private static int initializeTimeout(String prop, int def) { int timeoutVal = - GetPropertyAction.privilegedGetTimeoutProp(prop, def, debug); + SecurityProperties.getTimeoutSystemProp(prop, def, debug); if (debug != null) { debug.println(prop + " set to " + timeoutVal + " milliseconds"); } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java index 6a9cf6edbf8..c6fa1cf8980 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -32,7 +32,6 @@ import java.security.spec.*; import java.util.Arrays; -import sun.security.action.GetPropertyAction; import sun.security.rsa.RSAUtil.KeyType; /** @@ -91,7 +90,7 @@ public class RSAKeyFactory extends KeyFactorySpi { public static final int MAX_RESTRICTED_EXPLEN = 64; private static final boolean restrictExpLen = - "true".equalsIgnoreCase(GetPropertyAction.privilegedGetProperty( + "true".equalsIgnoreCase(System.getProperty( "sun.security.rsa.restrictRSAExponent", "true")); static RSAKeyFactory getInstance(KeyType type) { diff --git a/src/java.base/share/classes/sun/security/rsa/SunRsaSign.java b/src/java.base/share/classes/sun/security/rsa/SunRsaSign.java index 642b97933d5..664881ba6af 100644 --- a/src/java.base/share/classes/sun/security/rsa/SunRsaSign.java +++ b/src/java.base/share/classes/sun/security/rsa/SunRsaSign.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -43,26 +43,11 @@ public final class SunRsaSign extends Provider { @java.io.Serial private static final long serialVersionUID = 866040293550393045L; - @SuppressWarnings("removal") public SunRsaSign() { super("SunRsaSign", PROVIDER_VER, "Sun RSA signature provider"); Provider p = this; - Iterator serviceIter = new SunRsaSignEntries(p).iterator(); - - if (System.getSecurityManager() == null) { - putEntries(serviceIter); - } else { - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - putEntries(serviceIter); - return null; - } - }); - } - } - void putEntries(Iterator i) { + Iterator i = new SunRsaSignEntries(p).iterator(); while (i.hasNext()) { putService(i.next()); } diff --git a/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java index 28f32742a61..dc5b1aafb20 100644 --- a/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -25,9 +25,7 @@ package sun.security.util; -import java.security.AccessController; import java.security.AlgorithmConstraints; -import java.security.PrivilegedAction; import java.security.Security; import java.util.Arrays; import java.util.Collections; @@ -48,14 +46,7 @@ protected AbstractAlgorithmConstraints(AlgorithmDecomposer decomposer) { // Get algorithm constraints from the specified security property. static Set getAlgorithms(String propertyName) { - @SuppressWarnings("removal") - String property = AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public String run() { - return Security.getProperty(propertyName); - } - }); + String property = Security.getProperty(propertyName); String[] algorithmsInProperty = null; if (property != null && !property.isEmpty()) { diff --git a/src/java.base/share/classes/sun/security/util/Debug.java b/src/java.base/share/classes/sun/security/util/Debug.java index 9f344601e7e..59bc810ca57 100644 --- a/src/java.base/share/classes/sun/security/util/Debug.java +++ b/src/java.base/share/classes/sun/security/util/Debug.java @@ -34,7 +34,6 @@ import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.Locale; -import sun.security.action.GetPropertyAction; /** * A utility class for debugging. @@ -54,10 +53,9 @@ public class Debug { private static final String THREAD_OPTION = "+thread"; static { - args = GetPropertyAction.privilegedGetProperty("java.security.debug"); + args = System.getProperty("java.security.debug"); - String args2 = GetPropertyAction - .privilegedGetProperty("java.security.auth.debug"); + String args2 = System.getProperty("java.security.auth.debug"); if (args == null) { args = args2; diff --git a/src/java.base/share/classes/sun/security/util/DomainName.java b/src/java.base/share/classes/sun/security/util/DomainName.java index 5182ad1b5ca..53a646c8102 100644 --- a/src/java.base/share/classes/sun/security/util/DomainName.java +++ b/src/java.base/share/classes/sun/security/util/DomainName.java @@ -32,8 +32,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; @@ -205,21 +203,12 @@ private static Rules createRules(String tld) { } private static InputStream getPubSuffixStream() { - @SuppressWarnings("removal") - InputStream is = AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public InputStream run() { - File f = new File(StaticProperty.javaHome(), - "lib/security/public_suffix_list.dat"); - try { - return new FileInputStream(f); - } catch (FileNotFoundException e) { - return null; - } - } - } - ); + InputStream is = null; + File f = new File(System.getProperty("java.home"), + "lib/security/public_suffix_list.dat"); + try { + is = new FileInputStream(f); + } catch (FileNotFoundException e) { } if (is == null) { if (SSLLogger.isOn && SSLLogger.isOn("ssl") && SSLLogger.isOn("trustmanager")) { diff --git a/src/java.base/share/classes/sun/security/util/FilePermCompat.java b/src/java.base/share/classes/sun/security/util/FilePermCompat.java index 1bba80df544..72db4eb93bc 100644 --- a/src/java.base/share/classes/sun/security/util/FilePermCompat.java +++ b/src/java.base/share/classes/sun/security/util/FilePermCompat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -40,7 +40,7 @@ public class FilePermCompat { public static final boolean compat; static { - String flag = SecurityProperties.privilegedGetOverridable( + String flag = SecurityProperties.getOverridableProperty( "jdk.io.permissionsUseCanonicalPath"); if (flag == null) { flag = "false"; diff --git a/src/java.base/share/classes/sun/security/util/HostnameChecker.java b/src/java.base/share/classes/sun/security/util/HostnameChecker.java index 71a491813eb..1374bc6d535 100644 --- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java +++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -153,7 +153,7 @@ private static void matchIP(String expectedIP, X509Certificate cert) InetAddress.getByName(ipAddress))) { return; } - } catch (UnknownHostException | SecurityException e) {} + } catch (UnknownHostException e) {} } } } diff --git a/src/java.base/share/classes/sun/security/util/LazyCodeSourcePermissionCollection.java b/src/java.base/share/classes/sun/security/util/LazyCodeSourcePermissionCollection.java deleted file mode 100644 index 68a1f70bb01..00000000000 --- a/src/java.base/share/classes/sun/security/util/LazyCodeSourcePermissionCollection.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2019, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.security.util; - -import java.io.File; -import java.io.FilePermission; -import java.io.IOException; -import java.net.URL; -import java.security.CodeSource; -import java.security.Permission; -import java.security.PermissionCollection; -import java.util.Enumeration; - -/** - * This {@code PermissionCollection} implementation delegates to another - * {@code PermissionCollection}, taking care to lazily add the permission needed - * to read from the given {@code CodeSource} at first use, i.e., when either of - * {@link #elements}, {@link #implies} or {@link #toString} is called, or when - * the collection is serialized. - */ -public final class LazyCodeSourcePermissionCollection - extends PermissionCollection -{ - @java.io.Serial - private static final long serialVersionUID = -6727011328946861783L; - private final PermissionCollection perms; - private final CodeSource cs; - private volatile boolean permissionAdded; - - public LazyCodeSourcePermissionCollection(PermissionCollection perms, - CodeSource cs) { - this.perms = perms; - this.cs = cs; - } - - private void ensureAdded() { - if (!permissionAdded) { - synchronized(perms) { - if (permissionAdded) - return; - - // open connection to determine the permission needed - URL location = cs.getLocation(); - if (location != null) { - try { - Permission p = location.openConnection().getPermission(); - if (p != null) { - // for directories then need recursive access - if (p instanceof FilePermission) { - String path = p.getName(); - if (path.endsWith(File.separator)) { - path += "-"; - p = new FilePermission(path, - SecurityConstants.FILE_READ_ACTION); - } - } - perms.add(p); - } - } catch (IOException ioe) { - } - } - if (isReadOnly()) { - perms.setReadOnly(); - } - permissionAdded = true; - } - } - } - - @Override - public void add(Permission permission) { - if (isReadOnly()) - throw new SecurityException( - "attempt to add a Permission to a readonly PermissionCollection"); - perms.add(permission); - } - - @Override - public boolean implies(Permission permission) { - ensureAdded(); - return perms.implies(permission); - } - - @Override - public Enumeration elements() { - ensureAdded(); - return perms.elements(); - } - - @Override - public String toString() { - ensureAdded(); - return perms.toString(); - } - - /** - * On serialization, initialize and replace with the underlying - * permissions. This removes the laziness on deserialization. - */ - @java.io.Serial - private Object writeReplace() { - ensureAdded(); - return perms; - } -} diff --git a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java index 99742b3b80f..ffe092a6b6d 100644 --- a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java +++ b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -49,8 +49,8 @@ public class LocalizedMessage { /** * A LocalizedMessage can be instantiated with a key and formatted with * arguments later in the style of MessageFormat. This organization - * allows the actual formatting (and associated permission checks) to be - * avoided unless the resulting string is needed. + * allows the actual formatting to be avoided unless the resulting string + * is needed. * @param key */ public LocalizedMessage(String key) { diff --git a/src/java.base/share/classes/sun/security/util/SecurityConstants.java b/src/java.base/share/classes/sun/security/util/SecurityConstants.java index 9d49bbba0a1..34f80faa3ab 100644 --- a/src/java.base/share/classes/sun/security/util/SecurityConstants.java +++ b/src/java.base/share/classes/sun/security/util/SecurityConstants.java @@ -25,12 +25,7 @@ package sun.security.util; -import java.lang.reflect.ReflectPermission; -import java.net.NetPermission; -import java.net.SocketPermission; import java.security.AllPermission; -import java.security.SecurityPermission; -import sun.security.action.GetPropertyAction; /** * Permission constants and string constants used to create permissions @@ -63,72 +58,9 @@ private SecurityConstants () { // Permission constants used in the various checkPermission() calls in JDK. - // java.lang.Class, java.lang.SecurityManager, java.lang.System, - // java.net.URLConnection, java.security.AllPermission, java.security.Policy, - // sun.security.provider.PolicyFile + // java.net.URLConnection, java.security.AllPermission public static final AllPermission ALL_PERMISSION = new AllPermission(); - // java.net.URL - public static final NetPermission SPECIFY_HANDLER_PERMISSION = - new NetPermission("specifyStreamHandler"); - - // java.net.ServerSocket, java.net.Socket - public static final NetPermission SET_SOCKETIMPL_PERMISSION = - new NetPermission("setSocketImpl"); - - // java.lang.SecurityManager, sun.applet.AppletPanel - public static final RuntimePermission CREATE_CLASSLOADER_PERMISSION = - new RuntimePermission("createClassLoader"); - - // java.lang.SecurityManager - public static final RuntimePermission CHECK_MEMBER_ACCESS_PERMISSION = - new RuntimePermission("accessDeclaredMembers"); - - // java.lang.SecurityManager, sun.applet.AppletSecurity - public static final RuntimePermission MODIFY_THREAD_PERMISSION = - new RuntimePermission("modifyThread"); - - // java.lang.SecurityManager, sun.applet.AppletSecurity - public static final RuntimePermission MODIFY_THREADGROUP_PERMISSION = - new RuntimePermission("modifyThreadGroup"); - - // java.lang.Class - public static final RuntimePermission GET_PD_PERMISSION = - new RuntimePermission("getProtectionDomain"); - - // java.lang.Thread - public static final RuntimePermission GET_STACK_TRACE_PERMISSION = - new RuntimePermission("getStackTrace"); - - // java.lang.Thread - public static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION = - new RuntimePermission("enableContextClassLoaderOverride"); - - // java.security.AccessControlContext - public static final SecurityPermission CREATE_ACC_PERMISSION = - new SecurityPermission("createAccessControlContext"); - - // java.security.AccessControlContext - public static final SecurityPermission GET_COMBINER_PERMISSION = - new SecurityPermission("getDomainCombiner"); - - // java.security.Policy, java.security.ProtectionDomain - public static final SecurityPermission GET_POLICY_PERMISSION = - new SecurityPermission ("getPolicy"); - - // java.lang.SecurityManager - public static final SocketPermission LOCAL_LISTEN_PERMISSION = - new SocketPermission("localhost:0", SOCKET_LISTEN_ACTION); - public static final String PROVIDER_VER = - GetPropertyAction.privilegedGetProperty("java.specification.version"); - - // java.lang.reflect.AccessibleObject - public static final ReflectPermission ACCESS_PERMISSION = - new ReflectPermission("suppressAccessChecks"); - - // sun.reflect.ReflectionFactory - public static final RuntimePermission REFLECTION_FACTORY_ACCESS_PERMISSION = - new RuntimePermission("reflectionFactoryAccess"); - + System.getProperty("java.specification.version"); } diff --git a/src/java.base/share/classes/sun/security/util/SecurityProperties.java b/src/java.base/share/classes/sun/security/util/SecurityProperties.java index a07c9b743fc..98bc71d829b 100644 --- a/src/java.base/share/classes/sun/security/util/SecurityProperties.java +++ b/src/java.base/share/classes/sun/security/util/SecurityProperties.java @@ -26,10 +26,12 @@ package sun.security.util; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.security.Security; +import java.util.Locale; +/** + * Utility methods for retrieving security and system properties. + */ public class SecurityProperties { public static final boolean INCLUDE_JAR_NAME_IN_EXCEPTIONS @@ -42,15 +44,6 @@ public class SecurityProperties { * @param propName the name of the system or security property * @return the value of the system or security property */ - @SuppressWarnings("removal") - public static String privilegedGetOverridable(String propName) { - if (System.getSecurityManager() == null) { - return getOverridableProperty(propName); - } else { - return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(propName)); - } - } - public static String getOverridableProperty(String propName) { String val = System.getProperty(propName); if (val == null) { @@ -69,7 +62,7 @@ public static String getOverridableProperty(String propName) { * contains refName, false otherwise */ public static boolean includedInExceptions(String refName) { - String val = privilegedGetOverridable("jdk.includeInExceptions"); + String val = getOverridableProperty("jdk.includeInExceptions"); if (val == null) { return false; } @@ -83,4 +76,98 @@ public static boolean includedInExceptions(String refName) { } return false; } + + /** + * Convenience method for fetching System property values that are timeouts. + * Accepted timeout values may be purely numeric, a numeric value + * followed by "s" (both interpreted as seconds), or a numeric value + * followed by "ms" (interpreted as milliseconds). + * + * @param prop the name of the System property + * @param def a default value (in milliseconds) + * @param dbg a Debug object, if null no debug messages will be sent + * + * @return an integer value corresponding to the timeout value in the System + * property in milliseconds. If the property value is empty, negative, + * or contains non-numeric characters (besides a trailing "s" or "ms") + * then the default value will be returned. If a negative value for + * the "def" parameter is supplied, zero will be returned if the + * property's value does not conform to the allowed syntax. + */ + public static int getTimeoutSystemProp(String prop, int def, Debug dbg) { + if (def < 0) { + def = 0; + } + + String rawPropVal = System.getProperty(prop, "").trim(); + if (rawPropVal.length() == 0) { + return def; + } + + // Determine if "ms" or just "s" is on the end of the string. + // We may do a little surgery on the value so we'll retain + // the original value in rawPropVal for debug messages. + boolean isMillis = false; + String propVal = rawPropVal; + if (rawPropVal.toLowerCase(Locale.ROOT).endsWith("ms")) { + propVal = rawPropVal.substring(0, rawPropVal.length() - 2); + isMillis = true; + } else if (rawPropVal.toLowerCase(Locale.ROOT).endsWith("s")) { + propVal = rawPropVal.substring(0, rawPropVal.length() - 1); + } + + // Next check to make sure the string is built only from digits + if (propVal.matches("^\\d+$")) { + try { + int timeout = Integer.parseInt(propVal); + return isMillis ? timeout : timeout * 1000; + } catch (NumberFormatException nfe) { + if (dbg != null) { + dbg.println("Warning: Unexpected " + nfe + + " for timeout value " + rawPropVal + + ". Using default value of " + def + " msec."); + } + return def; + } + } else { + if (dbg != null) { + dbg.println("Warning: Incorrect syntax for timeout value " + + rawPropVal + ". Using default value of " + def + + " msec."); + } + return def; + } + } + + /** + * Convenience method for fetching System property values that are booleans. + * + * @param prop the name of the System property + * @param def a default value + * @param dbg a Debug object, if null no debug messages will be sent + * + * @return a boolean value corresponding to the value in the System property. + * If the property value is neither "true" or "false", the default value + * will be returned. + */ + public static boolean getBooleanSystemProp(String prop, boolean def, Debug dbg) { + String rawPropVal = System.getProperty(prop, ""); + if ("".equals(rawPropVal)) { + return def; + } + + String lower = rawPropVal.toLowerCase(Locale.ROOT); + if ("true".equals(lower)) { + return true; + } else if ("false".equals(lower)) { + return false; + } else { + if (dbg != null) { + dbg.println("Warning: Unexpected value for " + prop + + ": " + rawPropVal + + ". Using default value: " + def); + } + return def; + } + } } diff --git a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java index 66c88cd63a9..3ae9375fae1 100644 --- a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java +++ b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -34,7 +34,6 @@ import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.spec.DHParameterSpec; -import sun.security.action.GetPropertyAction; /** * Various constants such as version number, default key length, used by @@ -175,8 +174,7 @@ public static final int getDefAESKeySize() { "jdk.security.defaultKeySize"; static { - String keyLengthStr = GetPropertyAction.privilegedGetProperty - (KEY_LENGTH_PROP); + String keyLengthStr = System.getProperty(KEY_LENGTH_PROP); int dsaKeySize = 2048; int rsaKeySize = 3072; int rsaSsaPssKeySize = rsaKeySize; // default to same value as RSA diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index 1576388b653..7accd3cbf10 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -37,7 +37,6 @@ import java.util.jar.Manifest; import jdk.internal.util.ArraysSupport; -import sun.security.action.GetIntegerAction; import sun.security.jca.Providers; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; @@ -847,8 +846,7 @@ private static int initializeMaxSigFileSize() { * the maximum allowed number of bytes for the signature-related files * in a JAR file. */ - int tmp = GetIntegerAction.privilegedGetProperty( - "jdk.jar.maxSignatureFileSize", 16000000); + int tmp = Integer.getInteger("jdk.jar.maxSignatureFileSize", 16000000); if (tmp < 0 || tmp > MAX_ARRAY_SIZE) { if (debug != null) { debug.println("The default signature file size of 16000000 bytes " + diff --git a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java index edc5197df2c..a814746d960 100644 --- a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java +++ b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java @@ -126,75 +126,68 @@ private static URL getEgdUrl() { /** * Create a RandomIO object for all I/O of this Variant type. */ - @SuppressWarnings("removal") private static RandomIO initIO(final Variant v) { - return AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public RandomIO run() { - - File seedFile; - File nextFile; - - switch(v) { - case MIXED: - URL egdUrl; - File egdFile = null; - - if ((egdUrl = getEgdUrl()) != null) { - try { - egdFile = SunEntries.getDeviceFile(egdUrl); - } catch (IOException e) { - // Swallow, seedFile is still null - } - } - // Try egd first. - if ((egdFile != null) && egdFile.canRead()) { - seedFile = egdFile; - } else { - // fall back to /dev/random. - seedFile = new File(NAME_RANDOM); - } - nextFile = new File(NAME_URANDOM); - break; - - case BLOCKING: - seedFile = new File(NAME_RANDOM); - nextFile = new File(NAME_RANDOM); - break; - - case NONBLOCKING: - seedFile = new File(NAME_URANDOM); - nextFile = new File(NAME_URANDOM); - break; - - default: - // Shouldn't happen! - return null; - } + File seedFile; + File nextFile; - if (debug != null) { - debug.println("NativePRNG." + v + - " seedFile: " + seedFile + - " nextFile: " + nextFile); - } + switch(v) { + case MIXED: + URL egdUrl; + File egdFile = null; - if (!seedFile.canRead() || !nextFile.canRead()) { - if (debug != null) { - debug.println("NativePRNG." + v + - " Couldn't read Files."); - } - return null; - } - - try { - return new RandomIO(seedFile, nextFile); - } catch (Exception e) { - return null; - } + if ((egdUrl = getEgdUrl()) != null) { + try { + egdFile = SunEntries.getDeviceFile(egdUrl); + } catch (IOException e) { + // Swallow, seedFile is still null } - }); + } + + // Try egd first. + if ((egdFile != null) && egdFile.canRead()) { + seedFile = egdFile; + } else { + // fall back to /dev/random. + seedFile = new File(NAME_RANDOM); + } + nextFile = new File(NAME_URANDOM); + break; + + case BLOCKING: + seedFile = new File(NAME_RANDOM); + nextFile = new File(NAME_RANDOM); + break; + + case NONBLOCKING: + seedFile = new File(NAME_URANDOM); + nextFile = new File(NAME_URANDOM); + break; + + default: + // Shouldn't happen! + return null; + } + + if (debug != null) { + debug.println("NativePRNG." + v + + " seedFile: " + seedFile + + " nextFile: " + nextFile); + } + + if (!seedFile.canRead() || !nextFile.canRead()) { + if (debug != null) { + debug.println("NativePRNG." + v + + " Couldn't read Files."); + } + return null; + } + + try { + return new RandomIO(seedFile, nextFile); + } catch (Exception e) { + return null; + } } // return whether the NativePRNG is available @@ -457,22 +450,15 @@ private byte[] implGenerateSeed(int numBytes) { // supply random bytes to the OS // write to "seed" if possible // always add the seed to our mixing random - @SuppressWarnings("removal") private void implSetSeed(byte[] seed) { synchronized (LOCK_SET_SEED) { if (seedOutInitialized == false) { seedOutInitialized = true; - seedOut = AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public OutputStream run() { - try { - return new FileOutputStream(seedFile, true); - } catch (Exception e) { - return null; - } - } - }); + try { + seedOut = new FileOutputStream(seedFile, true); + } catch (Exception e) { + seedOut = null; + } } if (seedOut != null) { try { diff --git a/src/java.naming/share/classes/com/sun/naming/internal/ObjectFactoriesFilter.java b/src/java.naming/share/classes/com/sun/naming/internal/ObjectFactoriesFilter.java index a80b59927d3..c7d50778d7b 100644 --- a/src/java.naming/share/classes/com/sun/naming/internal/ObjectFactoriesFilter.java +++ b/src/java.naming/share/classes/com/sun/naming/internal/ObjectFactoriesFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -220,7 +220,7 @@ private static ConfiguredFilter initializeFilter(String filterPropertyName, // Get security or system property value private static String getFilterPropertyValue(String propertyName, String defaultValue) { - String propVal = SecurityProperties.privilegedGetOverridable(propertyName); + String propVal = SecurityProperties.getOverridableProperty(propertyName); return propVal != null ? propVal : defaultValue; } } diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/Config.java b/src/java.security.jgss/share/classes/sun/security/krb5/Config.java index a9ea9d23eb1..c92a106850b 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/Config.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Config.java @@ -72,7 +72,7 @@ public class Config { static { String disableReferralsProp = - SecurityProperties.privilegedGetOverridable( + SecurityProperties.getOverridableProperty( "sun.security.krb5.disableReferrals"); if (disableReferralsProp != null) { DISABLE_REFERRALS = "true".equalsIgnoreCase(disableReferralsProp); @@ -82,7 +82,7 @@ public class Config { int maxReferralsValue = 5; String maxReferralsProp = - SecurityProperties.privilegedGetOverridable( + SecurityProperties.getOverridableProperty( "sun.security.krb5.maxReferrals"); try { maxReferralsValue = Integer.parseInt(maxReferralsProp); diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java b/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java index 9482177c174..f9076a9b0dd 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java @@ -67,7 +67,7 @@ public class Credentials { private static boolean alreadyTried = false; public static final boolean S4U2PROXY_ACCEPT_NON_FORWARDABLE - = "true".equalsIgnoreCase(SecurityProperties.privilegedGetOverridable( + = "true".equalsIgnoreCase(SecurityProperties.getOverridableProperty( "jdk.security.krb5.s4u2proxy.acceptNonForwardableServiceTicket")); private Credentials proxy = null; diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java index 9bee88eab9b..400843ff417 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java @@ -109,7 +109,7 @@ public class PrincipalName implements Cloneable { private static final boolean NAME_CASE_SENSITIVE_IN_MATCH = "true".equalsIgnoreCase( - SecurityProperties.privilegedGetOverridable( + SecurityProperties.getOverridableProperty( "jdk.security.krb5.name.case.sensitive")); diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java index 8599ffd81b8..a5b128f7129 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java @@ -358,7 +358,7 @@ public sun.security.krb5.Credentials getInitialCreds() { } boolean force; - String prop = SecurityProperties.privilegedGetOverridable( + String prop = SecurityProperties.getOverridableProperty( "jdk.security.krb5.default.initiate.credential"); if (prop == null) { prop = "always-impersonate"; diff --git a/test/jdk/sun/security/action/Generify.java b/test/jdk/sun/security/action/Generify.java index 09cbb3fbb42..4ddbc299a47 100644 --- a/test/jdk/sun/security/action/Generify.java +++ b/test/jdk/sun/security/action/Generify.java @@ -35,37 +35,8 @@ public class Generify { public static void main(String[] args) throws Exception { - long larg = 1234567890L; - - System.setProperty("boolean", "true"); - System.setProperty("integer", "9"); - System.setProperty("long", Long.toString(larg)); System.setProperty("property", "propertyvalue"); - Boolean b = AccessController.doPrivileged - (new GetBooleanAction("boolean")); - if (b.booleanValue() == true) { - System.out.println("boolean test passed"); - } else { - throw new SecurityException("boolean test failed"); - } - - Integer i = AccessController.doPrivileged - (new GetIntegerAction("integer")); - if (i.intValue() == 9) { - System.out.println("integer test passed"); - } else { - throw new SecurityException("integer test failed"); - } - - Long l = AccessController.doPrivileged - (new GetLongAction("long")); - if (l.longValue() == larg) { - System.out.println("long test passed"); - } else { - throw new SecurityException("long test failed"); - } - String prop = AccessController.doPrivileged (new GetPropertyAction("property")); if (prop.equals("propertyvalue")) { diff --git a/test/jdk/sun/security/action/GetLongAction/ReturnNullIfNoDefault.java b/test/jdk/sun/security/action/GetLongAction/ReturnNullIfNoDefault.java deleted file mode 100644 index be465575019..00000000000 --- a/test/jdk/sun/security/action/GetLongAction/ReturnNullIfNoDefault.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 1998, 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 4173993 - * @summary Make sure "null" is returned if property does not exist (or has - * wrong numeric format) and no default has been specified. - * @modules java.base/sun.security.action - */ - -import sun.security.action.*; - -public class ReturnNullIfNoDefault { - - public static void main(String[] args) throws Exception { - long larg = 1234567890L; - - GetLongAction ac = new GetLongAction("test"); - if (ac.run() != null) - throw new Exception("Returned value is not null"); - - ac = new GetLongAction("test", larg); - long ret = ((Long)ac.run()).longValue(); - if (ret != larg) - throw new Exception("Returned value differs from default"); - - System.setProperty("test", Long.toString(larg)); - ac = new GetLongAction("test"); - ret = ((Long)ac.run()).longValue(); - if (ret != larg) - throw new Exception("Returned value differs from property"); - } -} From 3a3bcd53d0b9aa55dcbc15de4d8278ce3258b31e Mon Sep 17 00:00:00 2001 From: Joe Wang Date: Mon, 2 Dec 2024 22:26:20 +0000 Subject: [PATCH 055/171] 8344800: Add W3C DTDs and XSDs to the JDK built-in Catalog Reviewed-by: lancea, rriggs --- make/modules/java.xml/Java.gmk | 2 +- .../internal/impl/XMLEntityManager.java | 6 +- .../xml/internal/jdkcatalog/JDKCatalog.xml | 119 +- .../jdkcatalog/w3c/dtd/schema10/XMLSchema.dtd | 513 +++ .../jdkcatalog/w3c/dtd/schema10/datatypes.dtd | 222 ++ .../jdkcatalog/w3c/dtd/xhtml10/xhtml-lat1.ent | 196 ++ .../w3c/dtd/xhtml10/xhtml-special.ent | 80 + .../w3c/dtd/xhtml10/xhtml-symbol.ent | 237 ++ .../w3c/dtd/xhtml10/xhtml1-frameset.dtd | 1235 +++++++ .../w3c/dtd/xhtml10/xhtml1-strict.dtd | 978 ++++++ .../w3c/dtd/xhtml10/xhtml1-transitional.dtd | 1201 +++++++ .../w3c/dtd/xhtml11/xhtml-attribs-1.mod | 142 + .../w3c/dtd/xhtml11/xhtml-base-1.mod | 53 + .../w3c/dtd/xhtml11/xhtml-bdo-1.mod | 47 + .../w3c/dtd/xhtml11/xhtml-blkphras-1.mod | 164 + .../w3c/dtd/xhtml11/xhtml-blkpres-1.mod | 40 + .../w3c/dtd/xhtml11/xhtml-blkstruct-1.mod | 57 + .../w3c/dtd/xhtml11/xhtml-charent-1.mod | 39 + .../w3c/dtd/xhtml11/xhtml-csismap-1.mod | 114 + .../w3c/dtd/xhtml11/xhtml-datatypes-1.mod | 103 + .../w3c/dtd/xhtml11/xhtml-edit-1.mod | 66 + .../w3c/dtd/xhtml11/xhtml-events-1.mod | 135 + .../w3c/dtd/xhtml11/xhtml-form-1.mod | 292 ++ .../w3c/dtd/xhtml11/xhtml-framework-1.mod | 97 + .../w3c/dtd/xhtml11/xhtml-hypertext-1.mod | 54 + .../w3c/dtd/xhtml11/xhtml-image-1.mod | 51 + .../w3c/dtd/xhtml11/xhtml-inlphras-1.mod | 203 ++ .../w3c/dtd/xhtml11/xhtml-inlpres-1.mod | 138 + .../w3c/dtd/xhtml11/xhtml-inlstruct-1.mod | 62 + .../w3c/dtd/xhtml11/xhtml-inlstyle-1.mod | 34 + .../w3c/dtd/xhtml11/xhtml-legacy-1.mod | 400 +++ .../w3c/dtd/xhtml11/xhtml-link-1.mod | 59 + .../w3c/dtd/xhtml11/xhtml-list-1.mod | 129 + .../w3c/dtd/xhtml11/xhtml-meta-1.mod | 47 + .../w3c/dtd/xhtml11/xhtml-object-1.mod | 60 + .../w3c/dtd/xhtml11/xhtml-param-1.mod | 48 + .../w3c/dtd/xhtml11/xhtml-pres-1.mod | 38 + .../w3c/dtd/xhtml11/xhtml-qname-1.mod | 318 ++ .../w3c/dtd/xhtml11/xhtml-ruby-1.mod | 242 ++ .../w3c/dtd/xhtml11/xhtml-script-1.mod | 67 + .../w3c/dtd/xhtml11/xhtml-ssismap-1.mod | 32 + .../w3c/dtd/xhtml11/xhtml-struct-1.mod | 136 + .../w3c/dtd/xhtml11/xhtml-style-1.mod | 48 + .../w3c/dtd/xhtml11/xhtml-table-1.mod | 333 ++ .../w3c/dtd/xhtml11/xhtml-text-1.mod | 52 + .../w3c/dtd/xhtml11/xhtml11-model-1.mod | 250 ++ .../jdkcatalog/w3c/dtd/xhtml11/xhtml11.dtd | 323 ++ .../w3c/dtd/xmlspec2_10/xmlspec.dtd | 2778 ++++++++++++++++ .../w3c/xsd/schema10/XMLSchema-datatypes.xsd | 154 + .../jdkcatalog/w3c/xsd/schema10/XMLSchema.xsd | 2364 ++++++++++++++ .../w3c/xsd/xhtml10/xhtml1-frameset.xsd | 2847 +++++++++++++++++ .../w3c/xsd/xhtml10/xhtml1-strict.xsd | 2211 +++++++++++++ .../w3c/xsd/xhtml10/xhtml1-transitional.xsd | 2755 ++++++++++++++++ .../w3c/xsd/xhtml11/xhtml-attribs-1.xsd | 73 + .../w3c/xsd/xhtml11/xhtml-base-1.xsd | 36 + .../w3c/xsd/xhtml11/xhtml-bdo-1.xsd | 71 + .../w3c/xsd/xhtml11/xhtml-blkphras-1.xsd | 160 + .../w3c/xsd/xhtml11/xhtml-blkpres-1.xsd | 37 + .../w3c/xsd/xhtml11/xhtml-blkstruct-1.xsd | 49 + .../w3c/xsd/xhtml11/xhtml-copyright-1.xsd | 29 + .../w3c/xsd/xhtml11/xhtml-csismap-1.xsd | 96 + .../w3c/xsd/xhtml11/xhtml-datatypes-1.xsd | 242 ++ .../w3c/xsd/xhtml11/xhtml-edit-1.xsd | 39 + .../w3c/xsd/xhtml11/xhtml-events-1.xsd | 130 + .../w3c/xsd/xhtml11/xhtml-form-1.xsd | 327 ++ .../w3c/xsd/xhtml11/xhtml-framework-1.xsd | 66 + .../w3c/xsd/xhtml11/xhtml-hypertext-1.xsd | 47 + .../w3c/xsd/xhtml11/xhtml-image-1.xsd | 46 + .../w3c/xsd/xhtml11/xhtml-inlphras-1.xsd | 163 + .../w3c/xsd/xhtml11/xhtml-inlpres-1.xsd | 39 + .../w3c/xsd/xhtml11/xhtml-inlstruct-1.xsd | 50 + .../w3c/xsd/xhtml11/xhtml-inlstyle-1.xsd | 27 + .../w3c/xsd/xhtml11/xhtml-link-1.xsd | 45 + .../w3c/xsd/xhtml11/xhtml-list-1.xsd | 99 + .../w3c/xsd/xhtml11/xhtml-meta-1.xsd | 54 + .../w3c/xsd/xhtml11/xhtml-notations-1.xsd | 69 + .../w3c/xsd/xhtml11/xhtml-object-1.xsd | 76 + .../w3c/xsd/xhtml11/xhtml-param-1.xsd | 51 + .../w3c/xsd/xhtml11/xhtml-pres-1.xsd | 51 + .../w3c/xsd/xhtml11/xhtml-ruby-1.xsd | 170 + .../w3c/xsd/xhtml11/xhtml-script-1.xsd | 71 + .../w3c/xsd/xhtml11/xhtml-ssismap-1.xsd | 43 + .../w3c/xsd/xhtml11/xhtml-struct-1.xsd | 130 + .../w3c/xsd/xhtml11/xhtml-style-1.xsd | 53 + .../w3c/xsd/xhtml11/xhtml-table-1.xsd | 272 ++ .../w3c/xsd/xhtml11/xhtml-target-1.xsd | 49 + .../w3c/xsd/xhtml11/xhtml-text-1.xsd | 67 + .../w3c/xsd/xhtml11/xhtml11-model-1.xsd | 716 +++++ .../w3c/xsd/xhtml11/xhtml11-modules-1.xsd | 605 ++++ .../jdkcatalog/w3c/xsd/xhtml11/xhtml11.xsd | 104 + .../w3c/xsd/xhtml11/xml-events-1.xsd | 73 + .../xsd/xhtml11/xml-events-copyright-1.xsd | 34 + .../jdkcatalog/w3c/xsd/xmlNS2001/xml.xsd | 117 + src/java.xml/share/classes/module-info.java | 96 +- src/java.xml/share/legal/schema10part1.md | 51 + src/java.xml/share/legal/schema10part2.md | 50 + src/java.xml/share/legal/xhtml10.md | 57 + src/java.xml/share/legal/xhtml10schema.md | 150 + src/java.xml/share/legal/xhtml11.md | 68 + src/java.xml/share/legal/xhtml11schema.md | 60 + src/java.xml/share/legal/xmlspec.md | 63 + src/java.xml/share/legal/xmlxsd.md | 43 + .../unittest/catalog/CatalogSupport2.java | 4 +- .../unittest/catalog/CatalogSupport3.java | 4 +- .../unittest/catalog/CatalogSupportBase.java | 11 +- .../jaxp/unittest/catalog/val_test_dtd.xsd | 19 + 106 files changed, 27706 insertions(+), 17 deletions(-) create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/XMLSchema.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/datatypes.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-lat1.ent create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-special.ent create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-symbol.ent create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-frameset.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-strict.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-transitional.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-attribs-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-base-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-bdo-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkphras-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkpres-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkstruct-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-charent-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-csismap-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-datatypes-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-edit-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-events-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-form-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-framework-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-hypertext-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-image-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlphras-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlpres-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstruct-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstyle-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-legacy-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-link-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-list-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-meta-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-object-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-param-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-pres-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-qname-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ruby-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-script-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ssismap-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-struct-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-style-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-table-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-text-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11-model-1.mod create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xmlspec2_10/xmlspec.dtd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema-datatypes.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-frameset.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-strict.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-transitional.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-attribs-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-base-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-bdo-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkphras-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkpres-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkstruct-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-copyright-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-csismap-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-datatypes-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-edit-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-events-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-form-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-framework-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-hypertext-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-image-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlphras-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlpres-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstruct-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstyle-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-link-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-list-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-meta-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-notations-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-object-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-param-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-pres-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ruby-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-script-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ssismap-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-struct-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-style-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-table-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-target-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-text-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-model-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-modules-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-copyright-1.xsd create mode 100644 src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xmlNS2001/xml.xsd create mode 100644 src/java.xml/share/legal/schema10part1.md create mode 100644 src/java.xml/share/legal/schema10part2.md create mode 100644 src/java.xml/share/legal/xhtml10.md create mode 100644 src/java.xml/share/legal/xhtml10schema.md create mode 100644 src/java.xml/share/legal/xhtml11.md create mode 100644 src/java.xml/share/legal/xhtml11schema.md create mode 100644 src/java.xml/share/legal/xmlspec.md create mode 100644 src/java.xml/share/legal/xmlxsd.md create mode 100644 test/jaxp/javax/xml/jaxp/unittest/catalog/val_test_dtd.xsd diff --git a/make/modules/java.xml/Java.gmk b/make/modules/java.xml/Java.gmk index 22c1dde2c2b..0c174f2113e 100644 --- a/make/modules/java.xml/Java.gmk +++ b/make/modules/java.xml/Java.gmk @@ -27,5 +27,5 @@ DISABLED_WARNINGS_java += dangling-doc-comments lossy-conversions this-escape DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:$(call CommaList, javax.xml.catalog javax.xml.datatype \ javax.xml.transform javax.xml.validation javax.xml.xpath)' -COPY += .dtd .xsd .xml +COPY += .dtd .xsd .xml .ent .mod CLEAN += .properties diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java index aa7b62151d1..3b08dd7e89b 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java @@ -1209,11 +1209,12 @@ public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) th // Step 1: custom Entity resolver XMLInputSource xmlInputSource = null; - + boolean resolveByResolver = false; if (fEntityResolver != null) { resourceIdentifier.setBaseSystemId(baseSystemId); resourceIdentifier.setExpandedSystemId(expandedSystemId); xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier); + resolveByResolver = xmlInputSource != null; } // Step 2: custom catalog if specified @@ -1229,7 +1230,8 @@ public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) th } // Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue - if (xmlInputSource == null + if ((xmlInputSource == null || (!resolveByResolver && xmlInputSource.getSystemId() != null + && xmlInputSource.getSystemId().equals(literalSystemId))) && (publicId != null || literalSystemId != null) && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) { initJdkCatalogResolver(); diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml index 3919dd4981d..74ba0032501 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/XMLSchema.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/XMLSchema.dtd new file mode 100644 index 00000000000..64aa2d97019 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/XMLSchema.dtd @@ -0,0 +1,513 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%xs-datatypes; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/datatypes.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/datatypes.dtd new file mode 100644 index 00000000000..f9352bae1c4 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/schema10/datatypes.dtd @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-lat1.ent b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-lat1.ent new file mode 100644 index 00000000000..ffee223eb10 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-lat1.ent @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-special.ent b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-special.ent new file mode 100644 index 00000000000..ca358b2fec7 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-special.ent @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-symbol.ent b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-symbol.ent new file mode 100644 index 00000000000..63c2abfa6f4 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml-symbol.ent @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-frameset.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-frameset.dtd new file mode 100644 index 00000000000..d814a6f37ed --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-frameset.dtd @@ -0,0 +1,1235 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-strict.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-strict.dtd new file mode 100644 index 00000000000..b449108daf9 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-strict.dtd @@ -0,0 +1,978 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-transitional.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-transitional.dtd new file mode 100644 index 00000000000..c62c02a5fa0 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml10/xhtml1-transitional.dtd @@ -0,0 +1,1201 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-attribs-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-attribs-1.mod new file mode 100644 index 00000000000..178da950ed3 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-attribs-1.mod @@ -0,0 +1,142 @@ + + + + + + + + + +]]> + + + + +]]> + + + + +]]> + + + + + + + + +]]> + + + + + + + + + + + +]]> + + +]]> + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-base-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-base-1.mod new file mode 100644 index 00000000000..ba47b40fdd8 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-base-1.mod @@ -0,0 +1,53 @@ + + + + + + + + + + + + +]]> + + + +]]> + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-bdo-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-bdo-1.mod new file mode 100644 index 00000000000..062de5cb8dc --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-bdo-1.mod @@ -0,0 +1,47 @@ + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkphras-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkphras-1.mod new file mode 100644 index 00000000000..9172463960c --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkphras-1.mod @@ -0,0 +1,164 @@ + + + + + + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkpres-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkpres-1.mod new file mode 100644 index 00000000000..c342f8b2016 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkpres-1.mod @@ -0,0 +1,40 @@ + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkstruct-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkstruct-1.mod new file mode 100644 index 00000000000..ce6a95c2e53 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-blkstruct-1.mod @@ -0,0 +1,57 @@ + + + + + + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-charent-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-charent-1.mod new file mode 100644 index 00000000000..824c3ed262a --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-charent-1.mod @@ -0,0 +1,39 @@ + + + + + + + +%xhtml-lat1; + + +%xhtml-symbol; + + +%xhtml-special; + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-csismap-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-csismap-1.mod new file mode 100644 index 00000000000..2bae98cce1f --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-csismap-1.mod @@ -0,0 +1,114 @@ + + + + + + + + + + +]]> + + + + + + +]]> + + + + + + + + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-datatypes-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-datatypes-1.mod new file mode 100644 index 00000000000..dde43e83e19 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-datatypes-1.mod @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-edit-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-edit-1.mod new file mode 100644 index 00000000000..b2a328e474e --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-edit-1.mod @@ -0,0 +1,66 @@ + + + + + + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-events-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-events-1.mod new file mode 100644 index 00000000000..03fd46cbb5c --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-events-1.mod @@ -0,0 +1,135 @@ + + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-form-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-form-1.mod new file mode 100644 index 00000000000..de68a757dcb --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-form-1.mod @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-framework-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-framework-1.mod new file mode 100644 index 00000000000..7d9d9729dac --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-framework-1.mod @@ -0,0 +1,97 @@ + + + + + + + + +%xhtml-arch.mod;]]> + + + +%xhtml-notations.mod;]]> + + + +%xhtml-datatypes.mod;]]> + + + +%xhtml-xlink.mod; + + + +%xhtml-qname.mod;]]> + + + +%xhtml-events.mod;]]> + + + +%xhtml-attribs.mod;]]> + + + +%xhtml-model.redecl; + + + +%xhtml-model.mod;]]> + + + +%xhtml-charent.mod;]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-hypertext-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-hypertext-1.mod new file mode 100644 index 00000000000..7a7d8caebd6 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-hypertext-1.mod @@ -0,0 +1,54 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-image-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-image-1.mod new file mode 100644 index 00000000000..856176181a9 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-image-1.mod @@ -0,0 +1,51 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlphras-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlphras-1.mod new file mode 100644 index 00000000000..d749b2e38be --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlphras-1.mod @@ -0,0 +1,203 @@ + + + + + + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlpres-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlpres-1.mod new file mode 100644 index 00000000000..8717d54ed44 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlpres-1.mod @@ -0,0 +1,138 @@ + + + + + + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstruct-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstruct-1.mod new file mode 100644 index 00000000000..3d43d287563 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstruct-1.mod @@ -0,0 +1,62 @@ + + + + + + + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstyle-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstyle-1.mod new file mode 100644 index 00000000000..305680ff60c --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-inlstyle-1.mod @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-legacy-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-legacy-1.mod new file mode 100644 index 00000000000..c4eee72790a --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-legacy-1.mod @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%xhtml-frames.mod;]]> + + + + + +%xhtml-iframe.mod;]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-link-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-link-1.mod new file mode 100644 index 00000000000..2b4f92c7d85 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-link-1.mod @@ -0,0 +1,59 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-list-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-list-1.mod new file mode 100644 index 00000000000..6c85f205f82 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-list-1.mod @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-meta-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-meta-1.mod new file mode 100644 index 00000000000..24a0b228061 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-meta-1.mod @@ -0,0 +1,47 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-object-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-object-1.mod new file mode 100644 index 00000000000..0d14cc51d66 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-object-1.mod @@ -0,0 +1,60 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-param-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-param-1.mod new file mode 100644 index 00000000000..c101bedd3d6 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-param-1.mod @@ -0,0 +1,48 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-pres-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-pres-1.mod new file mode 100644 index 00000000000..6a2f34de325 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-pres-1.mod @@ -0,0 +1,38 @@ + + + + + + + + +%xhtml-inlpres.mod;]]> + + + +%xhtml-blkpres.mod;]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-qname-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-qname-1.mod new file mode 100644 index 00000000000..c7586a53c70 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-qname-1.mod @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + +%xhtml-qname-extra.mod; + + + + + + + + + +]]> + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + +]]> + + + + +%xhtml-qname.redecl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ruby-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ruby-1.mod new file mode 100644 index 00000000000..68931769c2e --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ruby-1.mod @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + +]]> + +]]> + + + +]]> + + + + + + + + + + + + + +]]> + + + + + + +]]> + + + + + + +]]> +]]> + + + + + + + +]]> + + + + + + + + +]]> + + + + +]]> +]]> + + + + + + +]]> +]]> + + + + + + + + + + +]]> + + + + + +]]> + + + + + +]]> +]]> + + + + + +]]> + + + + + +]]> + + + + + +]]> +]]> +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-script-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-script-1.mod new file mode 100644 index 00000000000..aa702f1f014 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-script-1.mod @@ -0,0 +1,67 @@ + + + + + + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ssismap-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ssismap-1.mod new file mode 100644 index 00000000000..4e2b8d7ed63 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-ssismap-1.mod @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-struct-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-struct-1.mod new file mode 100644 index 00000000000..4bb420ee7b1 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-struct-1.mod @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-style-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-style-1.mod new file mode 100644 index 00000000000..3105b2a4730 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-style-1.mod @@ -0,0 +1,48 @@ + + + + + + + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-table-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-table-1.mod new file mode 100644 index 00000000000..3fc03608f1d --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-table-1.mod @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-text-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-text-1.mod new file mode 100644 index 00000000000..07ccb81a7fe --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml-text-1.mod @@ -0,0 +1,52 @@ + + + + + + + + +%xhtml-inlstruct.mod;]]> + + + +%xhtml-inlphras.mod;]]> + + + +%xhtml-blkstruct.mod;]]> + + + +%xhtml-blkphras.mod;]]> + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11-model-1.mod b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11-model-1.mod new file mode 100644 index 00000000000..229e9a9d0a2 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11-model-1.mod @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11.dtd new file mode 100644 index 00000000000..8f14c406cfb --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xhtml11/xhtml11.dtd @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + +%xhtml-inlstyle.mod;]]> + + + + + + + + +%xhtml-datatypes.mod;]]> + + + + + + +%xhtml-framework.mod;]]> + + + + +]]> + + + + +%xhtml-text.mod;]]> + + + + +%xhtml-hypertext.mod;]]> + + + + +%xhtml-list.mod;]]> + + + + + + +%xhtml-edit.mod;]]> + + + + +%xhtml-bdo.mod;]]> + + + + + + +%xhtml-ruby.mod;]]> + + + + +%xhtml-pres.mod;]]> + + + + +%xhtml-link.mod;]]> + + + + +%xhtml-meta.mod;]]> + + + + +%xhtml-base.mod;]]> + + + + +%xhtml-script.mod;]]> + + + + +%xhtml-style.mod;]]> + + + + +%xhtml-image.mod;]]> + + + + +%xhtml-csismap.mod;]]> + + + + +%xhtml-ssismap.mod;]]> + + + + +%xhtml-param.mod;]]> + + + + +%xhtml-object.mod;]]> + + + + +%xhtml-table.mod;]]> + + + + +%xhtml-form.mod;]]> + + + + +%xhtml-legacy.mod;]]> + + + + +%xhtml-struct.mod;]]> + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xmlspec2_10/xmlspec.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xmlspec2_10/xmlspec.dtd new file mode 100644 index 00000000000..f720913be44 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/dtd/xmlspec2_10/xmlspec.dtd @@ -0,0 +1,2778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +]]> + + + + + + + + + +]]> + + + + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + +]]> + + + + +]]> + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + + + + +]]> + + +]]> + + + + + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + + + +]]> + + +]]> + + + + + +]]> + + + +]]> + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + + + +]]> + + +]]> + + + + + + + +]]> + + +]]> + + + + + +]]> + + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + + + +]]> + + + + +]]> + + + + + + + + + + +]]> + + +]]> + + + + + + +]]> + + + + +]]> + + + + + + +]]> + + + + +]]> + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + + + + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + + + + + +]]> + + +]]> + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + + + +]]> + + + +]]> + + + + + + +]]> + + +]]> + + + + + + +]]> + + +]]> + + + + + + + + + + + + + + + + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +]]> + + + + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + + + +]]> + + +]]> + + + + + + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + + +]]> + + + +]]> + + +]]> + + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + +]]> + + +]]> + + + + + + +]]> + + + +]]> + + + + +]]> + + + +]]> + + + + + + +]]> + + +]]> + + + + + +]]> + + +]]> + + + + + + +]]> + + +]]> + + + + +]]> + + + +]]> + + + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + +]]> + + + + +]]> + + + + +]]> + + + + +]]> + + + +]]> + + + + + + +]]> + + + + +]]> + + + + + + +]]> + + +]]> + + + + + + +]]> + + + + +]]> + + + + + + +]]> + + + + +]]> + + + + + + +]]> + + + + +]]> + + + + + + + + + + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + +]]> + + + +]]> + + +]]> + + +]]> + + +]]> + + + +]]> + + +]]> + + +]]> + + +]]> + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema-datatypes.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema-datatypes.xsd new file mode 100644 index 00000000000..574949db19c --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema-datatypes.xsd @@ -0,0 +1,154 @@ + + + + +]> + + + Note this schema is NOT a normative schema - - + It contains types derived from all the builtin simple type definitions + with the same local name but in a distinct namespace, for use + by applications which do no wish to import the full XMLSchema + schema. Since derivation is not symmetric, unexpected results may + follow from mixing references to these definitions with references + to the definitions in the XMLSchema namespace. For example, + although dt:positiveInteger is derived from xs:integer, the converse + does not hold. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema.xsd new file mode 100644 index 00000000000..169ade09254 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/schema10/XMLSchema.xsd @@ -0,0 +1,2364 @@ + + + + + + + + + + + Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp + Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp + + + + + + The schema corresponding to this document is normative, + with respect to the syntactic constraints it expresses in the + XML Schema language. The documentation (within <documentation> elements) + below, is not normative, but rather highlights important aspects of + the W3C Recommendation of which this is a part + + + + + The simpleType element and all of its members are defined + towards the end of this schema document + + + + + + Get access to the xml: attribute groups for xml:lang + as declared on 'schema' and 'documentation' below + + + + + + + + This type is extended by almost all schema types + to allow attributes from other namespaces to be + added to user schemas. + + + + + + + + + + + + + This type is extended by all types which allow annotation + other than <schema> itself + + + + + + + + + + + + + + + + This group is for the + elements which occur freely at the top level of schemas. + All of their types are based on the "annotated" type by extension. + + + + + + + + + + + + + This group is for the + elements which can self-redefine (see <redefine> below). + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction} + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction, list, union} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for maxOccurs + + + + + + + + + + + + for all particles + + + + + + + for element, group and attributeGroup, + which both define and reference + + + + + + + + 'complexType' uses this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This branch is short for + <complexContent> + <restriction base="xs:anyType"> + ... + </restriction> + </complexContent> + + + + + + + + + + + + + + + Will be restricted to required or forbidden + + + + + + Not allowed if simpleContent child is chosen. + May be overriden by setting on complexContent child. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This choice is added simply to + make this a valid restriction per the REC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overrides any setting on complexType parent. + + + + + + + + + + + + + + + This choice is added simply to + make this a valid restriction per the REC + + + + + + + + + + + + + + + + + No typeDefParticle group reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {substitution, extension, + restriction} + + + + + + + + + + + + + + + + + + + + + + + + + The element element can be used either + at the top level to define an element-type binding globally, + or within a content model to either reference a globally-defined + element or type or declare an element-type binding locally. + The ref form is not allowed at the top level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for explicit groups, named top-level groups and + group references + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for the three kinds of group + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This choice with min/max is here to + avoid a pblm with the Elt:All/Choice/Seq + Particle derivation constraint + + + + + + + + + + restricted max/min + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Only elements allowed inside + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simple type for the value of the 'namespace' attr of + 'any' and 'anyAttribute' + + + + Value is + ##any - - any non-conflicting WFXML/attribute at all + + ##other - - any non-conflicting WFXML/attribute from + namespace other than targetNS + + ##local - - any unqualified non-conflicting WFXML/attribute + + one or - - any non-conflicting WFXML/attribute from + more URI the listed namespaces + references + (space separated) + + ##targetNamespace or ##local may appear in the above list, to + refer to the targetNamespace of the enclosing + schema or an absent targetNamespace respectively + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in selectors + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the following EBNF: + Selector ::= Path ( '|' Path )* + Path ::= ('.//')? Step ( '/' Step )* + Step ::= '.' | NameTest + NameTest ::= QName | '*' | NCName ':' '*' + child:: is also allowed + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in fields + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the same EBNF as for selector, + with the following change: + Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) + + + + + + + + + + + + + + + + + + + + + + + + + + + The three kinds of identity constraints, all with + type of or derived from 'keybase'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + A public identifier, per ISO 8879 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + notations for use within XML Schema schemas + + + + + + + + + Not the real urType, but as close an approximation as we can + get in the XML representation + + + + + + + + + + First the built-in primitive datatypes. These definitions are for + information only, the real built-in definitions are magic. + + + + For each built-in datatype in this schema (both primitive and + derived) can be uniquely addressed via a URI constructed + as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype + + For example, to address the int datatype, the URI is: + + http://www.w3.org/2001/XMLSchema#int + + Additionally, each facet definition element can be uniquely + addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the facet + + For example, to address the maxInclusive facet, the URI is: + + http://www.w3.org/2001/XMLSchema#maxInclusive + + Additionally, each facet usage in a built-in datatype definition + can be uniquely addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype, followed + by a period (".") followed by the name of the facet + + For example, to address the usage of the maxInclusive facet in + the definition of int, the URI is: + + http://www.w3.org/2001/XMLSchema#int.maxInclusive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOTATION cannot be used directly in a schema; rather a type + must be derived from it by specifying at least one enumeration + facet whose value is the name of a NOTATION declared in the + schema. + + + + + + + + + + Now the derived primitive types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern specifies the content of section 2.12 of XML 1.0e2 + and RFC 3066 (Revised version of RFC 1766). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 7 from the XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 5 from the XML spec + + + + + + + + + + + + + + + pattern matches production 4 from the Namespaces in XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + #all or (possibly empty) subset of {restriction, union, list} + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Can be restricted to required or forbidden + + + + + + + + + + + + + + + + + + Required at the top level + + + + + + + + + + + + + + + + + + + Forbidden when nested + + + + + + + + + + + + + + + + + + + We should use a substitution group for facets, but + that's ruled out because it would allow users to + add their own, which we're not ready for yet. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + base attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + itemType attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + + + memberTypes attribute must be non-empty or there must be + at least one simpleType child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-frameset.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-frameset.xsd new file mode 100644 index 00000000000..0518a6145dd --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-frameset.xsd @@ -0,0 +1,2847 @@ + + + + + + XHTML 1.0 (Second Edition) Frameset in XML Schema + + This is the same as HTML 4 Frameset except for + changes due to the differences between XML and SGML. + + Namespace = http://www.w3.org/1999/xhtml + + For further information, see: http://www.w3.org/TR/xhtml1 + + Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio), + All Rights Reserved. + + The DTD version is identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd" + + $Id: xhtml1-frameset.xsd,v 1.5 2002/08/28 09:53:29 mimasa Exp $ + + + + + + + + ================ Character mnemonic entities ========================= + + XHTML entity sets are identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent" + + PUBLIC "-//W3C//ENTITIES Special for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent" + + PUBLIC "-//W3C//ENTITIES Symbols for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent" + + + + + + ================== Imported Names ==================================== + + + + + + + media type, as per [RFC2045] + + + + + + + + + comma-separated list of media types, as per [RFC2045] + + + + + + + + + a character encoding, as per [RFC2045] + + + + + + + + + a space separated list of character encodings, as per [RFC2045] + + + + + + + + + a language code, as per [RFC3066] + + + + + + + + + a single character, as per section 2.2 of [XML] + + + + + + + + + + + one or more digits + + + + + + + + + + + tabindex attribute specifies the position of the current element + in the tabbing order for the current document. This value must be + a number between 0 and 32767. User agents should ignore leading zeros. + + + + + + + + + + + + space-separated list of link types + + + + + + + + + single or comma-separated list of media descriptors + + + + + + + + + + + a Uniform Resource Identifier, see [RFC2396] + + + + + + + + + a space separated list of Uniform Resource Identifiers + + + + + + + + + date and time information. ISO date format + + + + + + + + + script expression + + + + + + + + + style sheet data + + + + + + + + + used for titles etc. + + + + + + + + + render in this frame + + + + + + + + + + + nn for pixels or nn% for percentage length + + + + + + + + + + + pixel, percentage, or relative + + + + + + + + + + + comma-separated list of MultiLength + + + + + + + + + + + integer representing length in pixels + + + + + + + + these are used for image maps + + + + + + + + + + + + + + + + comma separated list of lengths + + + + + + + + + + + used for object, applet, img, input and iframe + + + + + + + + + + + + + + + a color using sRGB: #RRGGBB as Hex values + + There are also 16 widely known color names with their sRGB values: + + Black = #000000 Green = #008000 + Silver = #C0C0C0 Lime = #00FF00 + Gray = #808080 Olive = #808000 + White = #FFFFFF Yellow = #FFFF00 + Maroon = #800000 Navy = #000080 + Red = #FF0000 Blue = #0000FF + Purple = #800080 Teal = #008080 + Fuchsia= #FF00FF Aqua = #00FFFF + + + + + + + + + + =================== Generic Attributes =============================== + + + + + + + core attributes common to most elements + id document-wide unique id + class space separated list of classes + style associated style info + title advisory title/amplification + + + + + + + + + + + + internationalization attributes + lang language code (backwards compatible) + xml:lang language code (as per XML 1.0 spec) + dir direction for weak/neutral text + + + + + + + + + + + + + + + + + + attributes for common UI events + onclick a pointer button was clicked + ondblclick a pointer button was double clicked + onmousedown a pointer button was pressed down + onmouseup a pointer button was released + onmousemove a pointer was moved onto the element + onmouseout a pointer was moved away from the element + onkeypress a key was pressed and released + onkeydown a key was pressed down + onkeyup a key was released + + + + + + + + + + + + + + + + + + attributes for elements that can get the focus + accesskey accessibility key character + tabindex position in tabbing order + onfocus the element got the focus + onblur the element lost the focus + + + + + + + + + + + + + + + + + + text alignment for p, div, h1-h6. The default is + align="left" for ltr headings, "right" for rtl + + + + + + + + + + + + + + + + + =================== Text Elements ==================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + + + + + + + + + + "Inline" covers inline or "text-level" element + + + + + + + + + + + ================== Block level elements ============================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Flow" mixes block and inline and is used for list items etc. + + + + + + + + + + + + + ================== Content models for exclusions ===================== + + + + + + + a elements use "Inline" excluding a + + + + + + + + + + + + + + + pre uses "Inline" excluding img, object, applet, big, small, + sub, sup, font, or basefont + + + + + + + + + + + + + + + + form uses "Flow" excluding form + + + + + + + + + + + + + button uses "Flow" but excludes a, form, form controls, iframe + + + + + + + + + + + + + + + + + + + + + + + + + ================ Document Structure ================================== + + + + + + + + + + + + + + + + + ================ Document Head ======================================= + + + + + + + + + + + + + + + + + + + + content model is "head.misc" combined with a single + title and an optional base element in any order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The title element is not considered part of the flow of text. + It should be displayed, for example as the page header or + window title. Exactly one title is required per document. + + + + + + + + + + + + document base URI + + + + + + + + + + + + + generic metainformation + + + + + + + + + + + + + + + + Relationship values can be used in principle: + + a) for document specific toolbars/menus when used + with the link element in document head e.g. + start, contents, previous, next, index, end, help + b) to link to a separate style sheet (rel="stylesheet") + c) to make a link to a script (rel="script") + d) by stylesheets to control how collections of + html nodes are rendered into printed documents + e) to make a link to a printable version of this document + e.g. a PostScript or PDF version (rel="alternate" media="print") + + + + + + + + + + + + + + + + + + + style info, which may include CDATA sections + + + + + + + + + + + + + + + + script statements, which may include CDATA sections + + + + + + + + + + + + + + + + + + + + + + + alternate content container for non script-based rendering + + + + + + + + + + + + + + ======================= Frames ======================================= + + + + + + + only one noframes element permitted per document + + + + + + + + + + + + + + + + + + + reserved frame names start with "_" otherwise starts with letter + + + + + + + tiled window within frameset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inline subwindow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alternate content container for non frame-based rendering + + + + + + + + + + + + + =================== Document Body ==================================== + + + + + + + + + + + + + + + + + + + + + + + + + generic language/style container + + + + + + + + + + + + + + + =================== Paragraphs ======================================= + + + + + + + + + + + + + + + + + =================== Headings ========================================= + + There are six levels of headings from h1 (the most important) + to h6 (the least important). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Lists ============================================ + + + + + + + Unordered list bullet styles + + + + + + + + + + + + + Unordered list + + + + + + + + + + + + + + + + + + + + + + Ordered list numbering style + + 1 arabic numbers 1, 2, 3, ... + a lower alpha a, b, c, ... + A upper alpha A, B, C, ... + i lower roman i, ii, iii, ... + I upper roman I, II, III, ... + + The style is applied to the sequence number which by default + is reset to 1 for the first list item in an ordered list. + + + + + + + + + Ordered (numbered) list + + + + + + + + + + + + + + + + + + + + + + + single column list (DEPRECATED) + + + + + + + + + + + + + + + + + + + + + multiple column list (DEPRECATED) + + + + + + + + + + + + + + + + + + + + + LIStyle is constrained to: "(ULStyle|OLStyle)" + + + + + + + + + list item + + + + + + + + + + + + + + + + definition lists - dt for term, dd for its definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Address ========================================== + + + + + + + information on author + + + + + + + + + + + + + + + =================== Horizontal Rule ================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Preformatted Text ================================ + + + + + + + content is "Inline" excluding + "img|object|applet|big|small|sub|sup|font|basefont" + + + + + + + + + + + + + + + + =================== Block-like Quotes ================================ + + + + + + + + + + + + + + + + + =================== Text alignment =================================== + + + + + + + center content + + + + + + + + + + + + + + =================== Inserted/Deleted Text ============================ + + ins/del are allowed in block and inline content, but its + inappropriate to include block content within an ins element + occurring in inline content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================== The Anchor Element ================================ + + + + + + + content is "Inline" except that anchors shouldn't be nested + + + + + + + + + + + + + + + + + + + + + + + + + ===================== Inline Elements ================================ + + + + + + + generic language/style container + + + + + + + + + + + + + + + I18N BiDi over-ride + + + + + + + + + + + + + + + + + + + + + + + + + + forced line break + + + + + + + + + + + + + + + + + + + + + emphasis + + + + + + + + + + + + + + + strong emphasis + + + + + + + + + + + + + + + definitional + + + + + + + + + + + + + + + program code + + + + + + + + + + + + + + + sample + + + + + + + + + + + + + + + something user would type + + + + + + + + + + + + + + + variable + + + + + + + + + + + + + + + citation + + + + + + + + + + + + + + + abbreviation + + + + + + + + + + + + + + + acronym + + + + + + + + + + + + + + + inlined quote + + + + + + + + + + + + + + + + subscript + + + + + + + + + + + + + + + superscript + + + + + + + + + + + + + + + fixed pitch font + + + + + + + + + + + + + + + italic font + + + + + + + + + + + + + + + bold font + + + + + + + + + + + + + + + bigger font + + + + + + + + + + + + + + + smaller font + + + + + + + + + + + + + + + underline + + + + + + + + + + + + + + + strike-through + + + + + + + + + + + + + + + strike-through + + + + + + + + + + + + + + + base font size + + + + + + + + + + + + + + local change to font + + + + + + + + + + + + + + + + + + ==================== Object ====================================== + + object is used to embed objects as part of HTML pages. + param elements should precede other content. Parameters + can also be expressed as attribute/value pairs on the + object element itself when brevity is desired. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param is used to supply a named property value. + In XML it would seem natural to follow RDF and support an + abbreviated syntax where the param elements are replaced + by attribute value pairs on the object start tag. + + + + + + + + + + + + + + + + + + + + + + =================== Java applet ================================== + + One of code or object attributes must be present. + Place param elements before other content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Images =========================================== + + To avoid accessibility problems for people who aren't + able to see the image, you should provide a text + description using the alt and longdesc attributes. + In addition, avoid the use of server-side image maps. + + + + + + + + + + + + + + + + usemap points to a map element which may be in this document + or an external document, although the latter is not widely supported + + + + + + + + + + + + + + + + + + + + ================== Client-side image maps ============================ + + These can be placed in the same document or grouped in a + separate document although this isn't yet widely supported + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================ Forms =============================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each label must not contain more than ONE field + Label elements shouldn't be nested. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + form control + + + + + + + + + + the name attribute is required for all but submit & reset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option selector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option group + + + + + + + + + + + + + + + + + + + + + + selectable choice + + + + + + + + + + + + + + + + + + + + + + + + + + + multi-line text field + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The fieldset element is used to group form fields. + Only one legend element should occur in the content + and if present should only be preceded by whitespace. + + NOTE: this content model is different from the XHTML 1.0 DTD, + closer to the intended content model in HTML4 DTD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fieldset label + + + + + + + + + + + + + + + + + Content is "Flow" excluding a, form and form controls + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + single-line text input control (DEPRECATED) + + + + + + + + + + + + ======================= Tables ======================================= + + Derived from IETF HTML table standard, see [RFC1942] + + + + + + + The border attribute sets the thickness of the frame around the + table. The default units are screen pixels. + + The frame attribute specifies which parts of the frame around + the table should be rendered. The values are not the same as + CALS to avoid a name clash with the valign attribute. + + + + + + + + + + + + + + + + + + + The rules attribute defines which rules to draw between cells: + + If rules is absent then assume: + "none" if border is absent or border="0" otherwise "all" + + + + + + + + + + + + + + + horizontal placement of table relative to document + + + + + + + + + + + + + horizontal alignment attributes for cell contents + + char alignment char, e.g. char=":" + charoff offset for alignment char + + + + + + + + + + + + + + + + + + + + + vertical alignment attributes for cell contents + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use thead to duplicate headers when breaking table + across page boundaries, or for static headers when + tbody sections are rendered in scrolling panel. + + Use tfoot to duplicate footers when breaking table + across page boundaries, or for static footers when + tbody sections are rendered in scrolling panel. + + Use multiple tbody sections when rules are needed + between groups of table rows. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + colgroup groups a set of col elements. It allows you to group + several semantically related columns together. + + + + + + + + + + + + + + + + + + col elements define the alignment properties for cells in + one or more columns. + + The width attribute specifies the width of the columns, e.g. + + width=64 width in screen pixels + width=0.5* relative width of 0.5 + + The span attribute causes the attributes of one + col element to apply to more than one column. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scope is simpler than headers attribute for common tables + + + + + + + + + + + + + th is for headers, td for data and for cells acting as both + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-strict.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-strict.xsd new file mode 100644 index 00000000000..93b80b66788 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-strict.xsd @@ -0,0 +1,2211 @@ + + + + + + XHTML 1.0 (Second Edition) Strict in XML Schema + + This is the same as HTML 4 Strict except for + changes due to the differences between XML and SGML. + + Namespace = http://www.w3.org/1999/xhtml + + For further information, see: http://www.w3.org/TR/xhtml1 + + Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio), + All Rights Reserved. + + The DTD version is identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" + + $Id: xhtml1-strict.xsd,v 1.2 2002/08/28 08:05:44 mimasa Exp $ + + + + + + + + ================ Character mnemonic entities ========================= + + XHTML entity sets are identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent" + + PUBLIC "-//W3C//ENTITIES Special for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent" + + PUBLIC "-//W3C//ENTITIES Symbols for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent" + + + + + + ================== Imported Names ==================================== + + + + + + + media type, as per [RFC2045] + + + + + + + + + comma-separated list of media types, as per [RFC2045] + + + + + + + + + a character encoding, as per [RFC2045] + + + + + + + + + a space separated list of character encodings, as per [RFC2045] + + + + + + + + + a language code, as per [RFC3066] + + + + + + + + + a single character, as per section 2.2 of [XML] + + + + + + + + + + + one or more digits + + + + + + + + + + + tabindex attribute specifies the position of the current element + in the tabbing order for the current document. This value must be + a number between 0 and 32767. User agents should ignore leading zeros. + + + + + + + + + + + + space-separated list of link types + + + + + + + + + single or comma-separated list of media descriptors + + + + + + + + + + + a Uniform Resource Identifier, see [RFC2396] + + + + + + + + + a space separated list of Uniform Resource Identifiers + + + + + + + + + date and time information. ISO date format + + + + + + + + + script expression + + + + + + + + + style sheet data + + + + + + + + + used for titles etc. + + + + + + + + + nn for pixels or nn% for percentage length + + + + + + + + + + + pixel, percentage, or relative + + + + + + + + + + + integer representing length in pixels + + + + + + + + these are used for image maps + + + + + + + + + + + + + + + + comma separated list of lengths + + + + + + + + + + =================== Generic Attributes =============================== + + + + + + + core attributes common to most elements + id document-wide unique id + class space separated list of classes + style associated style info + title advisory title/amplification + + + + + + + + + + + + internationalization attributes + lang language code (backwards compatible) + xml:lang language code (as per XML 1.0 spec) + dir direction for weak/neutral text + + + + + + + + + + + + + + + + + + attributes for common UI events + onclick a pointer button was clicked + ondblclick a pointer button was double clicked + onmousedown a pointer button was pressed down + onmouseup a pointer button was released + onmousemove a pointer was moved onto the element + onmouseout a pointer was moved away from the element + onkeypress a key was pressed and released + onkeydown a key was pressed down + onkeyup a key was released + + + + + + + + + + + + + + + + + + attributes for elements that can get the focus + accesskey accessibility key character + tabindex position in tabbing order + onfocus the element got the focus + onblur the element lost the focus + + + + + + + + + + + + + + + + + =================== Text Elements ==================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + + + + + + + + + + "Inline" covers inline or "text-level" elements + + + + + + + + + + + ================== Block level elements ============================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Flow" mixes block and inline and is used for list items etc. + + + + + + + + + + + + + ================== Content models for exclusions ===================== + + + + + + + a elements use "Inline" excluding a + + + + + + + + + + + + + + + pre uses "Inline" excluding big, small, sup or sup + + + + + + + + + + + + + + + + form uses "Block" excluding form + + + + + + + + + + + + button uses "Flow" but excludes a, form and form controls + + + + + + + + + + + + + + + + + + + ================ Document Structure ================================== + + + + + + + + + + + + + + + + + ================ Document Head ======================================= + + + + + + + + + + + + + + + + + + + content model is "head.misc" combined with a single + title and an optional base element in any order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The title element is not considered part of the flow of text. + It should be displayed, for example as the page header or + window title. Exactly one title is required per document. + + + + + + + + + + + + document base URI + + + + + + + + + + + + generic metainformation + + + + + + + + + + + + + + + + Relationship values can be used in principle: + + a) for document specific toolbars/menus when used + with the link element in document head e.g. + start, contents, previous, next, index, end, help + b) to link to a separate style sheet (rel="stylesheet") + c) to make a link to a script (rel="script") + d) by stylesheets to control how collections of + html nodes are rendered into printed documents + e) to make a link to a printable version of this document + e.g. a PostScript or PDF version (rel="alternate" media="print") + + + + + + + + + + + + + + + + + + style info, which may include CDATA sections + + + + + + + + + + + + + + + + script statements, which may include CDATA sections + + + + + + + + + + + + + + + + + + + + + + alternate content container for non script-based rendering + + + + + + + + + + + + + + =================== Document Body ==================================== + + + + + + + + + + + + + + + + + + + generic language/style container + + + + + + + + + + + + + + =================== Paragraphs ======================================= + + + + + + + + + + + + + + + + =================== Headings ========================================= + + There are six levels of headings from h1 (the most important) + to h6 (the least important). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Lists ============================================ + + + + + + + Unordered list + + + + + + + + + + + + + + Ordered (numbered) list + + + + + + + + + + + + + + list item + + + + + + + + + + + + + + definition lists - dt for term, dd for its definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Address ========================================== + + + + + + + information on author + + + + + + + + + + + + + + =================== Horizontal Rule ================================== + + + + + + + + + + + + =================== Preformatted Text ================================ + + + + + + + content is "Inline" excluding "img|object|big|small|sub|sup" + + + + + + + + + + + + + + + =================== Block-like Quotes ================================ + + + + + + + + + + + + + + + + + =================== Inserted/Deleted Text ============================ + + ins/del are allowed in block and inline content, but its + inappropriate to include block content within an ins element + occurring in inline content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================== The Anchor Element ================================ + + + + + + + content is "Inline" except that anchors shouldn't be nested + + + + + + + + + + + + + + + + + + + + + + + + ===================== Inline Elements ================================ + + + + + + + generic language/style container + + + + + + + + + + + + + + + I18N BiDi over-ride + + + + + + + + + + + + + + + + + + + + + + + + + + forced line break + + + + + + + + + + + emphasis + + + + + + + + + + + + + + + strong emphasis + + + + + + + + + + + + + + + definitional + + + + + + + + + + + + + + + program code + + + + + + + + + + + + + + + sample + + + + + + + + + + + + + + + something user would type + + + + + + + + + + + + + + + variable + + + + + + + + + + + + + + + citation + + + + + + + + + + + + + + + abbreviation + + + + + + + + + + + + + + + acronym + + + + + + + + + + + + + + + inlined quote + + + + + + + + + + + + + + + + subscript + + + + + + + + + + + + + + + superscript + + + + + + + + + + + + + + + fixed pitch font + + + + + + + + + + + + + + + italic font + + + + + + + + + + + + + + + bold font + + + + + + + + + + + + + + + bigger font + + + + + + + + + + + + + + + smaller font + + + + + + + + + + + + + + ==================== Object ====================================== + + object is used to embed objects as part of HTML pages. + param elements should precede other content. Parameters + can also be expressed as attribute/value pairs on the + object element itself when brevity is desired. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param is used to supply a named property value. + In XML it would seem natural to follow RDF and support an + abbreviated syntax where the param elements are replaced + by attribute value pairs on the object start tag. + + + + + + + + + + + + + + + + + + + + + + =================== Images =========================================== + + To avoid accessibility problems for people who aren't + able to see the image, you should provide a text + description using the alt and longdesc attributes. + In addition, avoid the use of server-side image maps. + Note that in this DTD there is no name attribute. That + is only available in the transitional and frameset DTD. + + + + + + + + + + + + + + + usemap points to a map element which may be in this document + or an external document, although the latter is not widely supported + + + + + + + + + + + + + + + + ================== Client-side image maps ============================ + + These can be placed in the same document or grouped in a + separate document although this isn't yet widely supported + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================ Forms =============================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each label must not contain more than ONE field + Label elements shouldn't be nested. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + form control + + + + + + + + + + the name attribute is required for all but submit & reset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option selector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option group + + + + + + + + + + + + + + + + + + + + + + selectable choice + + + + + + + + + + + + + + + + + + + + + + + + + + + multi-line text field + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The fieldset element is used to group form fields. + Only one legend element should occur in the content + and if present should only be preceded by whitespace. + + NOTE: this content model is different from the XHTML 1.0 DTD, + closer to the intended content model in HTML4 DTD + + + + + + + + + + + + + + + + + + + + fieldset label + + + + + + + + + + + + + + + + Content is "Flow" excluding a, form and form controls + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ======================= Tables ======================================= + + Derived from IETF HTML table standard, see [RFC1942] + + + + + + + The border attribute sets the thickness of the frame around the + table. The default units are screen pixels. + + The frame attribute specifies which parts of the frame around + the table should be rendered. The values are not the same as + CALS to avoid a name clash with the valign attribute. + + + + + + + + + + + + + + + + + + + The rules attribute defines which rules to draw between cells: + + If rules is absent then assume: + "none" if border is absent or border="0" otherwise "all" + + + + + + + + + + + + + + + horizontal alignment attributes for cell contents + + char alignment char, e.g. char=':' + charoff offset for alignment char + + + + + + + + + + + + + + + + + + + + + vertical alignment attributes for cell contents + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use thead to duplicate headers when breaking table + across page boundaries, or for static headers when + tbody sections are rendered in scrolling panel. + + Use tfoot to duplicate footers when breaking table + across page boundaries, or for static footers when + tbody sections are rendered in scrolling panel. + + Use multiple tbody sections when rules are needed + between groups of table rows. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + colgroup groups a set of col elements. It allows you to group + several semantically related columns together. + + + + + + + + + + + + + + + + + + col elements define the alignment properties for cells in + one or more columns. + + The width attribute specifies the width of the columns, e.g. + + width=64 width in screen pixels + width=0.5* relative width of 0.5 + + The span attribute causes the attributes of one + col element to apply to more than one column. + + + + + + + + + + + + + + + + + + + + + + + + + + + Scope is simpler than headers attribute for common tables + + + + + + + + + + + + + th is for headers, td for data and for cells acting as both + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-transitional.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-transitional.xsd new file mode 100644 index 00000000000..8ce44fb9aff --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml10/xhtml1-transitional.xsd @@ -0,0 +1,2755 @@ + + + + + + XHTML 1.0 (Second Edition) Transitional in XML Schema + + This is the same as HTML 4 Transitional except for + changes due to the differences between XML and SGML. + + Namespace = http://www.w3.org/1999/xhtml + + For further information, see: http://www.w3.org/TR/xhtml1 + + Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio), + All Rights Reserved. + + The DTD version is identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" + + $Id: xhtml1-transitional.xsd,v 1.5 2002/08/28 09:53:29 mimasa Exp $ + + + + + + + + ================ Character mnemonic entities ========================= + + XHTML entity sets are identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent" + + PUBLIC "-//W3C//ENTITIES Special for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent" + + PUBLIC "-//W3C//ENTITIES Symbols for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent" + + + + + + ================== Imported Names ==================================== + + + + + + + media type, as per [RFC2045] + + + + + + + + + comma-separated list of media types, as per [RFC2045] + + + + + + + + + a character encoding, as per [RFC2045] + + + + + + + + + a space separated list of character encodings, as per [RFC2045] + + + + + + + + + a language code, as per [RFC3066] + + + + + + + + + a single character, as per section 2.2 of [XML] + + + + + + + + + + + one or more digits + + + + + + + + + + + tabindex attribute specifies the position of the current element + in the tabbing order for the current document. This value must be + a number between 0 and 32767. User agents should ignore leading zeros. + + + + + + + + + + + + space-separated list of link types + + + + + + + + + single or comma-separated list of media descriptors + + + + + + + + + + + a Uniform Resource Identifier, see [RFC2396] + + + + + + + + + a space separated list of Uniform Resource Identifiers + + + + + + + + + date and time information. ISO date format + + + + + + + + + script expression + + + + + + + + + style sheet data + + + + + + + + + used for titles etc. + + + + + + + + + render in this frame + + + + + + + + + + + nn for pixels or nn% for percentage length + + + + + + + + + + + pixel, percentage, or relative + + + + + + + + + + + integer representing length in pixels + + + + + + + + these are used for image maps + + + + + + + + + + + + + + + + comma separated list of lengths + + + + + + + + + + + used for object, applet, img, input and iframe + + + + + + + + + + + + + + + a color using sRGB: #RRGGBB as Hex values + + There are also 16 widely known color names with their sRGB values: + + Black = #000000 Green = #008000 + Silver = #C0C0C0 Lime = #00FF00 + Gray = #808080 Olive = #808000 + White = #FFFFFF Yellow = #FFFF00 + Maroon = #800000 Navy = #000080 + Red = #FF0000 Blue = #0000FF + Purple = #800080 Teal = #008080 + Fuchsia= #FF00FF Aqua = #00FFFF + + + + + + + + + + =================== Generic Attributes =============================== + + + + + + + core attributes common to most elements + id document-wide unique id + class space separated list of classes + style associated style info + title advisory title/amplification + + + + + + + + + + + + internationalization attributes + lang language code (backwards compatible) + xml:lang language code (as per XML 1.0 spec) + dir direction for weak/neutral text + + + + + + + + + + + + + + + + + + attributes for common UI events + onclick a pointer button was clicked + ondblclick a pointer button was double clicked + onmousedown a pointer button was pressed down + onmouseup a pointer button was released + onmousemove a pointer was moved onto the element + onmouseout a pointer was moved away from the element + onkeypress a key was pressed and released + onkeydown a key was pressed down + onkeyup a key was released + + + + + + + + + + + + + + + + + + attributes for elements that can get the focus + accesskey accessibility key character + tabindex position in tabbing order + onfocus the element got the focus + onblur the element lost the focus + + + + + + + + + + + + + + + + + + text alignment for p, div, h1-h6. The default is + align="left" for ltr headings, "right" for rtl + + + + + + + + + + + + + + + + + =================== Text Elements ==================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + + + + + + + + + + "Inline" covers inline or "text-level" element + + + + + + + + + + + ================== Block level elements ============================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Flow" mixes block and inline and is used for list items etc. + + + + + + + + + + + + + ================== Content models for exclusions ===================== + + + + + + + a elements use "Inline" excluding a + + + + + + + + + + + + + + + pre uses "Inline" excluding img, object, applet, big, small, + font, or basefont + + + + + + + + + + + + + + + + form uses "Flow" excluding form + + + + + + + + + + + + + button uses "Flow" but excludes a, form, form controls, iframe + + + + + + + + + + + + + + + + + + + + + + + + + ================ Document Structure ================================== + + + + + + + + + + + + + + + + + ================ Document Head ======================================= + + + + + + + + + + + + + + + + + + + + content model is "head.misc" combined with a single + title and an optional base element in any order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The title element is not considered part of the flow of text. + It should be displayed, for example as the page header or + window title. Exactly one title is required per document. + + + + + + + + + + + + document base URI + + + + + + + + + + + + + generic metainformation + + + + + + + + + + + + + + + + Relationship values can be used in principle: + + a) for document specific toolbars/menus when used + with the link element in document head e.g. + start, contents, previous, next, index, end, help + b) to link to a separate style sheet (rel="stylesheet") + c) to make a link to a script (rel="script") + d) by stylesheets to control how collections of + html nodes are rendered into printed documents + e) to make a link to a printable version of this document + e.g. a PostScript or PDF version (rel="alternate" media="print") + + + + + + + + + + + + + + + + + + + style info, which may include CDATA sections + + + + + + + + + + + + + + + + script statements, which may include CDATA sections + + + + + + + + + + + + + + + + + + + + + + + alternate content container for non script-based rendering + + + + + + + + + + + + + + ======================= Frames ======================================= + + + + + + + inline subwindow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alternate content container for non frame-based rendering + + + + + + + + + + + + + + =================== Document Body ==================================== + + + + + + + + + + + + + + + + + + + + + + + + + generic language/style container + + + + + + + + + + + + + + + =================== Paragraphs ======================================= + + + + + + + + + + + + + + + + + =================== Headings ========================================= + + There are six levels of headings from h1 (the most important) + to h6 (the least important). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Lists ============================================ + + + + + + + Unordered list bullet styles + + + + + + + + + + + + + Unordered list + + + + + + + + + + + + + + + + + + + + + + Ordered list numbering style + + 1 arabic numbers 1, 2, 3, ... + a lower alpha a, b, c, ... + A upper alpha A, B, C, ... + i lower roman i, ii, iii, ... + I upper roman I, II, III, ... + + The style is applied to the sequence number which by default + is reset to 1 for the first list item in an ordered list. + + + + + + + + + Ordered (numbered) list + + + + + + + + + + + + + + + + + + + + + + + single column list (DEPRECATED) + + + + + + + + + + + + + + + + + + + + + multiple column list (DEPRECATED) + + + + + + + + + + + + + + + + + + + + + LIStyle is constrained to: "(ULStyle|OLStyle)" + + + + + + + + + list item + + + + + + + + + + + + + + + + definition lists - dt for term, dd for its definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Address ========================================== + + + + + + + information on author + + + + + + + + + + + + + + + =================== Horizontal Rule ================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Preformatted Text ================================ + + + + + + + content is "Inline" excluding + "img|object|applet|big|small|sub|sup|font|basefont" + + + + + + + + + + + + + + + + =================== Block-like Quotes ================================ + + + + + + + + + + + + + + + + + =================== Text alignment =================================== + + + + + + + center content + + + + + + + + + + + + + + =================== Inserted/Deleted Text ============================ + + ins/del are allowed in block and inline content, but its + inappropriate to include block content within an ins element + occurring in inline content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================== The Anchor Element ================================ + + + + + + + content is "Inline" except that anchors shouldn't be nested + + + + + + + + + + + + + + + + + + + + + + + + + ===================== Inline Elements ================================ + + + + + + + generic language/style container + + + + + + + + + + + + + + + I18N BiDi over-ride + + + + + + + + + + + + + + + + + + + + + + + + + + forced line break + + + + + + + + + + + + + + + + + + + + + emphasis + + + + + + + + + + + + + + + strong emphasis + + + + + + + + + + + + + + + definitional + + + + + + + + + + + + + + + program code + + + + + + + + + + + + + + + sample + + + + + + + + + + + + + + + something user would type + + + + + + + + + + + + + + + variable + + + + + + + + + + + + + + + citation + + + + + + + + + + + + + + + abbreviation + + + + + + + + + + + + + + + acronym + + + + + + + + + + + + + + + inlined quote + + + + + + + + + + + + + + + + subscript + + + + + + + + + + + + + + + superscript + + + + + + + + + + + + + + + fixed pitch font + + + + + + + + + + + + + + + italic font + + + + + + + + + + + + + + + bold font + + + + + + + + + + + + + + + bigger font + + + + + + + + + + + + + + + smaller font + + + + + + + + + + + + + + + underline + + + + + + + + + + + + + + + strike-through + + + + + + + + + + + + + + + strike-through + + + + + + + + + + + + + + + base font size + + + + + + + + + + + + + + local change to font + + + + + + + + + + + + + + + + + + ==================== Object ====================================== + + object is used to embed objects as part of HTML pages. + param elements should precede other content. Parameters + can also be expressed as attribute/value pairs on the + object element itself when brevity is desired. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param is used to supply a named property value. + In XML it would seem natural to follow RDF and support an + abbreviated syntax where the param elements are replaced + by attribute value pairs on the object start tag. + + + + + + + + + + + + + + + + + + + + + + =================== Java applet ================================== + + One of code or object attributes must be present. + Place param elements before other content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Images =========================================== + + To avoid accessibility problems for people who aren't + able to see the image, you should provide a text + description using the alt and longdesc attributes. + In addition, avoid the use of server-side image maps. + + + + + + + + + + + + + + + + usemap points to a map element which may be in this document + or an external document, although the latter is not widely supported + + + + + + + + + + + + + + + + + + + + ================== Client-side image maps ============================ + + These can be placed in the same document or grouped in a + separate document although this isn't yet widely supported + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================ Forms =============================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each label must not contain more than ONE field + Label elements shouldn't be nested. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + form control + + + + + + + + + + the name attribute is required for all but submit & reset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option selector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option group + + + + + + + + + + + + + + + + + + + + + + selectable choice + + + + + + + + + + + + + + + + + + + + + + + + + + + multi-line text field + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The fieldset element is used to group form fields. + Only one legend element should occur in the content + and if present should only be preceded by whitespace. + + NOTE: this content model is different from the XHTML 1.0 DTD, + closer to the intended content model in HTML4 DTD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fieldset label + + + + + + + + + + + + + + + + + Content is "Flow" excluding a, form and form controls + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + single-line text input control (DEPRECATED) + + + + + + + + + + + + ======================= Tables ======================================= + + Derived from IETF HTML table standard, see [RFC1942] + + + + + + + The border attribute sets the thickness of the frame around the + table. The default units are screen pixels. + + The frame attribute specifies which parts of the frame around + the table should be rendered. The values are not the same as + CALS to avoid a name clash with the valign attribute. + + + + + + + + + + + + + + + + + + + The rules attribute defines which rules to draw between cells: + + If rules is absent then assume: + "none" if border is absent or border="0" otherwise "all" + + + + + + + + + + + + + + + horizontal placement of table relative to document + + + + + + + + + + + + + horizontal alignment attributes for cell contents + + char alignment char, e.g. char=':' + charoff offset for alignment char + + + + + + + + + + + + + + + + + + + + + vertical alignment attributes for cell contents + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use thead to duplicate headers when breaking table + across page boundaries, or for static headers when + tbody sections are rendered in scrolling panel. + + Use tfoot to duplicate footers when breaking table + across page boundaries, or for static footers when + tbody sections are rendered in scrolling panel. + + Use multiple tbody sections when rules are needed + between groups of table rows. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + colgroup groups a set of col elements. It allows you to group + several semantically related columns together. + + + + + + + + + + + + + + + + + + col elements define the alignment properties for cells in + one or more columns. + + The width attribute specifies the width of the columns, e.g. + + width=64 width in screen pixels + width=0.5* relative width of 0.5 + + The span attribute causes the attributes of one + col element to apply to more than one column. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scope is simpler than headers attribute for common tables + + + + + + + + + + + + + th is for headers, td for data and for cells acting as both + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-attribs-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-attribs-1.xsd new file mode 100644 index 00000000000..f07528a9ef1 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-attribs-1.xsd @@ -0,0 +1,73 @@ + + + + + + + This is the XML Schema common attributes module for XHTML + $Id: xhtml-attribs-1.xsd,v 1.9 2009/11/18 17:59:51 ahby Exp $ + + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-base-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-base-1.xsd new file mode 100644 index 00000000000..a23df79d4e1 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-base-1.xsd @@ -0,0 +1,36 @@ + + + + + + + Base element + This is the XML Schema Base Element module for XHTML + + * base + + This module declares the base element type and its attributes, + used to define a base URI against which relative URIs in the + document will be resolved. + + $Id: xhtml-base-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-bdo-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-bdo-1.xsd new file mode 100644 index 00000000000..15251c61cca --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-bdo-1.xsd @@ -0,0 +1,71 @@ + + + + + + Bidirectional Override (bdo) Element + This is the XML Schema BDO Element module for XHTML + + This modules declares the element 'bdo' and 'dir' attributes, + Used to override the Unicode bidirectional algorithm for selected + fragments of text. + Bidirectional text support includes both the bdo element and + the 'dir' attribute. + + $Id: xhtml-bdo-1.xsd,v 1.6 2009/11/18 17:59:51 ahby Exp $ + + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkphras-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkphras-1.xsd new file mode 100644 index 00000000000..9a795f8f0ce --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkphras-1.xsd @@ -0,0 +1,160 @@ + + + + + + + + + This is the XML Schema Block Phrasal support module for XHTML + $Id: xhtml-blkphras-1.xsd,v 1.7 2008/07/05 04:11:00 ahby Exp $ + + + + + + Block Phrasal + This module declares the elements and their attributes used to + support block-level phrasal markup. + This is the XML Schema block phrasal elements module for XHTML + + * address, blockquote, pre, h1, h2, h3, h4, h5, h6 + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkpres-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkpres-1.xsd new file mode 100644 index 00000000000..cf42303a686 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkpres-1.xsd @@ -0,0 +1,37 @@ + + + + + + This is the XML SchemaBlock presentation element module for XHTML + $Id: xhtml-blkpres-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + Block Presentational Elements + + * hr + + This module declares the elements and their attributes used to + support block-level presentational markup. + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkstruct-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkstruct-1.xsd new file mode 100644 index 00000000000..1e658580e7a --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-blkstruct-1.xsd @@ -0,0 +1,49 @@ + + + + + + Block Structural + + * div, p + + This module declares the elements and their attributes used to + support block-level structural markup. + + This is the XML Schema Block Structural module for XHTML + $Id: xhtml-blkstruct-1.xsd,v 1.3 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-copyright-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-copyright-1.xsd new file mode 100644 index 00000000000..24c84fd476d --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-copyright-1.xsd @@ -0,0 +1,29 @@ + + + + + + This is XHTML, a reformulation of HTML as a modular XML application + The Extensible HyperText Markup Language (XHTML) + Copyright ©1998-2005 World Wide Web Consortium + (Massachusetts Institute of Technology, European Research Consortium + for Informatics and Mathematics, Keio University). + All Rights Reserved. + + Permission to use, copy, modify and distribute the XHTML Schema + modules and their accompanying xs:documentation for any purpose + and without fee is hereby granted in perpetuity, provided that the above + copyright notice and this paragraph appear in all copies. + The copyright holders make no representation about the suitability of + these XML Schema modules for any purpose. + + They are provided "as is" without expressed or implied warranty. + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-csismap-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-csismap-1.xsd new file mode 100644 index 00000000000..42f802d0f79 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-csismap-1.xsd @@ -0,0 +1,96 @@ + + + + + + + Client-side Image Maps + This is the XML Schema Client-side Image Maps module for XHTML + + * area, map + + This module declares elements and attributes to support client-side + image maps. + + $Id: xhtml-csismap-1.xsd,v 1.3 2009/09/30 15:12:48 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-datatypes-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-datatypes-1.xsd new file mode 100644 index 00000000000..ab7bac0ee85 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-datatypes-1.xsd @@ -0,0 +1,242 @@ + + + + + XHTML Datatypes + This is the XML Schema datatypes module for XHTML + + Defines containers for the XHTML datatypes, many of + these imported from other specifications and standards. + + $Id: xhtml-datatypes-1.xsd,v 1.12 2009/09/30 15:12:48 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-edit-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-edit-1.xsd new file mode 100644 index 00000000000..44380b23e0e --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-edit-1.xsd @@ -0,0 +1,39 @@ + + + + + + + Editing Elements + This is the XML Schema Editing Markup module for XHTML + + * ins, del + + This module declares element types and attributes used to indicate + inserted and deleted content while editing a document. + + $Id: xhtml-edit-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-events-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-events-1.xsd new file mode 100644 index 00000000000..381f6dd5068 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-events-1.xsd @@ -0,0 +1,130 @@ + + + + + + + This is the XML Schema Intrinsic Events module for XHTML + $Id: xhtml-events-1.xsd,v 1.4 2005/09/26 22:54:53 ahby Exp $ + + + + + + Intrinsic Event Attributes + These are the event attributes defined in HTML 4, + Section 18.2.3 "Intrinsic Events". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-form-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-form-1.xsd new file mode 100644 index 00000000000..d7a970f4f23 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-form-1.xsd @@ -0,0 +1,327 @@ + + + + + + + Forms + This is the XML Schema Forms module for XHTML + + * form, label, input, select, optgroup, option, + textarea, fieldset, legend, button + + This module declares markup to provide support for online + forms, based on the features found in HTML 4.0 forms. + + + $Id: xhtml-form-1.xsd,v 1.4 2009/09/30 15:22:38 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-framework-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-framework-1.xsd new file mode 100644 index 00000000000..05b906d4480 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-framework-1.xsd @@ -0,0 +1,66 @@ + + + + + This is the XML Schema Modular Framework support module for XHTML + $Id: xhtml-framework-1.xsd,v 1.5 2005/09/26 23:37:47 ahby Exp $ + + + + + + XHTML Modular Framework + This required module instantiates the necessary modules + needed to support the XHTML modularization framework. + + The Schema modules instantiated are: + + notations + + datatypes + + common attributes + + character entities + + + + + + + + This module defines XHTML Attribute DataTypes + + + + + + + + This module defines Common attributes for XHTML + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-hypertext-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-hypertext-1.xsd new file mode 100644 index 00000000000..2f4c81bc895 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-hypertext-1.xsd @@ -0,0 +1,47 @@ + + + + + + + Hypertext Module + This is the XML Schema Hypertext module for XHTML + + * a + + This module declares the anchor ('a') element type, which + defines the source of a hypertext link. The destination + (or link 'target') is identified via its 'id' attribute + rather than the 'name' attribute as was used in HTML. + + $Id: xhtml-hypertext-1.xsd,v 1.4 2005/09/26 23:37:47 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-image-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-image-1.xsd new file mode 100644 index 00000000000..4d6e9ab1645 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-image-1.xsd @@ -0,0 +1,46 @@ + + + + + + + Images + This is the XML Schema Images module for XHTML + + * img + + This module provides markup to support basic image embedding. + + To avoid problems with text-only UAs as well as to make + image content understandable and navigable to users of + non-visual UAs, you need to provide a description with + the 'alt' attribute, and avoid server-side image maps. + + + $Id: xhtml-image-1.xsd,v 1.3 2009/09/30 15:22:38 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlphras-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlphras-1.xsd new file mode 100644 index 00000000000..919c59de3b3 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlphras-1.xsd @@ -0,0 +1,163 @@ + + + + + + + This is the XML Schema Inline Phrasal support module for XHTML + $Id: xhtml-inlphras-1.xsd,v 1.4 2005/09/26 22:54:53 ahby Exp $ + + + + + + Inline Phrasal. + This module declares the elements and their attributes used to + support inline-level phrasal markup. + This is the XML Schema Inline Phrasal module for XHTML + + * abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var + + $Id: xhtml-inlphras-1.xsd,v 1.4 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlpres-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlpres-1.xsd new file mode 100644 index 00000000000..a053447c284 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlpres-1.xsd @@ -0,0 +1,39 @@ + + + + + + This is the XML Schema Inline Presentation element module for XHTML + $Id: xhtml-inlpres-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + Inline Presentational Elements + + * b, big, i, small, sub, sup, tt + + This module declares the elements and their attributes used to + support inline-level presentational markup. + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstruct-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstruct-1.xsd new file mode 100644 index 00000000000..635eb5f193d --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstruct-1.xsd @@ -0,0 +1,50 @@ + + + + + + This is the XML Schema Inline Structural support module for XHTML + $Id: xhtml-inlstruct-1.xsd,v 1.4 2005/09/26 22:54:53 ahby Exp $ + + + + + + Inline Structural. + This module declares the elements and their attributes + used to support inline-level structural markup. + This is the XML Schema Inline Structural element module for XHTML + + * br, span + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstyle-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstyle-1.xsd new file mode 100644 index 00000000000..ef93c2dcefa --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-inlstyle-1.xsd @@ -0,0 +1,27 @@ + + + + + + Inline Style module + This is the XML Schema Inline Style module for XHTML + + * styloe attribute + + This module declares the 'style' attribute, used to support inline + style markup. + + $Id: xhtml-inlstyle-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-link-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-link-1.xsd new file mode 100644 index 00000000000..f210d84a11d --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-link-1.xsd @@ -0,0 +1,45 @@ + + + + + + This is the XML Schema Link Element module for XHTML + $Id: xhtml-link-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + Link element + + * link + + This module declares the link element type and its attributes, + which could (in principle) be used to define document-level links + to external resources. + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-list-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-list-1.xsd new file mode 100644 index 00000000000..cc22ba88f60 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-list-1.xsd @@ -0,0 +1,99 @@ + + + + + + List Module + This is the XML Schema Lists module for XHTML + List Module Elements + + * dl, dt, dd, ol, ul, li + + This module declares the list-oriented element types + and their attributes. + $Id: xhtml-list-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-meta-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-meta-1.xsd new file mode 100644 index 00000000000..010137b37da --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-meta-1.xsd @@ -0,0 +1,54 @@ + + + + + + + This is the XML Schema Metainformation module for XHTML + $Id: xhtml-meta-1.xsd,v 1.3 2008/07/05 04:11:00 ahby Exp $ + + + + + + Meta Information + + * meta + + This module declares the meta element type and its attributes, + used to provide declarative document metainformation. + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-notations-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-notations-1.xsd new file mode 100644 index 00000000000..8ca35cd6ff8 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-notations-1.xsd @@ -0,0 +1,69 @@ + + + + + + Notations module + This is the XML Schema module for data type notations for XHTML + $Id: xhtml-notations-1.xsd,v 1.5 2005/09/26 22:54:53 ahby Exp $ + + + + + + Notations module + Defines the XHTML notations, many of these imported from + other specifications and standards. When an existing FPI is + known, it is incorporated here. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-object-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-object-1.xsd new file mode 100644 index 00000000000..a32efb0fa96 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-object-1.xsd @@ -0,0 +1,76 @@ + + + + + + + This is the XML Schema Embedded Object module for XHTML + $Id: xhtml-object-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + This module declares the object element type and its attributes, + used to embed external objects as part of XHTML pages. In the + document, place param elements prior to the object elements + that require their content. + + Note that use of this module requires instantiation of the + Param Element Module prior to this module. + + Elements defined here: + + * object (param) + + + + + + + Param module + + Elements defined here: + * param + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-param-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-param-1.xsd new file mode 100644 index 00000000000..ba34ff41290 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-param-1.xsd @@ -0,0 +1,51 @@ + + + + + + This is the XML Schema Param Element module for XHTML + $Id: xhtml-param-1.xsd,v 1.3 2005/09/26 22:54:53 ahby Exp $ + + + + + + Parameters for Java Applets and Embedded Objects + + * param + + This module provides declarations for the param element, + used to provide named property values for the applet + and object elements. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-pres-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-pres-1.xsd new file mode 100644 index 00000000000..bc36fc48f77 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-pres-1.xsd @@ -0,0 +1,51 @@ + + + + + + This is the XML Schema Presentation module for XHTML + This is a REQUIRED module. + $Id: xhtml-pres-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + Presentational Elements + + This module defines elements and their attributes for + simple presentation-related markup. + + Elements defined here: + + * hr + * b, big, i, small, sub, sup, tt + + + + + + + Block Presentational module + Elements defined here: + + * hr + + + + + + + Inline Presentational module + Elements defined here: + + * b, big, i, small, sub, sup, tt + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ruby-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ruby-1.xsd new file mode 100644 index 00000000000..666f81bd2b9 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ruby-1.xsd @@ -0,0 +1,170 @@ + + + + + + This is the Ruby module for XHTML + $Id: xhtml-ruby-1.xsd,v 1.7 2010/05/02 17:22:08 ahby Exp $ + + + + + + + "Ruby" are short runs of text alongside the base text, typically + used in East Asian documents to indicate pronunciation or to + provide a short annotation. The full specification for Ruby is here: + + http://www.w3.org/TR/2001/REC-ruby-20010531/ + + This module defines "Ruby " or "complex Ruby" as described + in the specification: + + http://www.w3.org/TR/2001/REC-ruby-20010531/#complex + + Simple or Basic Ruby are defined in a separate module. + + This module declares the elements and their attributes used to + support complex ruby annotation markup. Elements defined here + * ruby, rbc, rtc, rb, rt, rp + + This module expects the document model to define the + following content models + + InlNoRuby.mix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-script-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-script-1.xsd new file mode 100644 index 00000000000..5ba6e20e036 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-script-1.xsd @@ -0,0 +1,71 @@ + + + + + + This is the XML Schema Scripting module for XHTML + $Id: xhtml-script-1.xsd,v 1.5 2006/09/11 08:50:41 ahby Exp $ + + + + + + Scripting + + * script, noscript + + This module declares element types and attributes used to provide + support for executable scripts as well as an alternate content + container where scripts are not supported. + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ssismap-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ssismap-1.xsd new file mode 100644 index 00000000000..4e120107eba --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-ssismap-1.xsd @@ -0,0 +1,43 @@ + + + + + + This is the XML Schema Server-side Image Maps module for XHTML + $Id: xhtml-ssismap-1.xsd,v 1.3 2005/09/26 22:54:53 ahby Exp $ + + + + + + Server-side Image Maps + + This adds the 'ismap' attribute to the img element to + support server-side processing of a user selection. + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-struct-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-struct-1.xsd new file mode 100644 index 00000000000..a0d21300e89 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-struct-1.xsd @@ -0,0 +1,130 @@ + + + + + + This is the XML Schema Document Structure module for XHTML + Document Structure + + * title, head, body, html + + The Structure Module defines the major structural elements and + their attributes. + + $Id: xhtml-struct-1.xsd,v 1.11 2009/09/30 14:13:35 ahby Exp $ + + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-style-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-style-1.xsd new file mode 100644 index 00000000000..c20da4a7088 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-style-1.xsd @@ -0,0 +1,53 @@ + + + + + + + This is the XML Schema Stylesheets module for XHTML + $Id: xhtml-style-1.xsd,v 1.5 2006/09/11 10:14:57 ahby Exp $ + + + + + + Stylesheets + + * style + + This module declares the style element type and its attributes, + used to embed stylesheet information in the document head element. + + + + + + + This import brings in the XML namespace attributes + The module itself does not provide the schemaLocation + and expects the driver schema to provide the + actual SchemaLocation. + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-table-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-table-1.xsd new file mode 100644 index 00000000000..ec76db3ca8b --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-table-1.xsd @@ -0,0 +1,272 @@ + + + + + + + This is the XML Schema Tables module for XHTML + $Id: xhtml-table-1.xsd,v 1.3 2005/09/26 22:54:53 ahby Exp $ + + + + + + Tables + + * table, caption, thead, tfoot, tbody, colgroup, col, tr, th, td + + This module declares element types and attributes used to provide + table markup similar to HTML 4.0, including features that enable + better accessibility for non-visual user agents. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-target-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-target-1.xsd new file mode 100644 index 00000000000..d8f2770d28e --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-target-1.xsd @@ -0,0 +1,49 @@ + + + + + + This is the XML Schema Target module for XHTML + $Id: xhtml-target-1.xsd,v 1.3 2007/04/03 18:27:01 ahby Exp $ + + + + + + + Target + + * target + + This module declares the 'target' attribute used for opening windows + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-text-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-text-1.xsd new file mode 100644 index 00000000000..432bdad7ac3 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml-text-1.xsd @@ -0,0 +1,67 @@ + + + + + + Textual Content + This is the XML Schema Text module for XHTML + + The Text module includes declarations for all core + text container elements and their attributes. + + + block phrasal + + block structural + + inline phrasal + + inline structural + + $Id: xhtml-text-1.xsd,v 1.2 2005/09/26 22:54:53 ahby Exp $ + + + + + + + + Block Phrasal module + Elements defined here: + + * address, blockquote, pre, h1, h2, h3, h4, h5, h6 + + + + + + + Block Structural module + Elements defined here: + + * div, p + + + + + + + Inline Phrasal module + Elements defined here: + + * abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var + + + + + + + Inline Structural module + Elements defined here: + + * br,span + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-model-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-model-1.xsd new file mode 100644 index 00000000000..a1c138d8488 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-model-1.xsd @@ -0,0 +1,716 @@ + + + + + + This is the XML Schema module of common content models for XHTML11 + + $Id: xhtml11-model-1.xsd,v 1.9 2009/02/03 15:14:49 ahby Exp $ + + + + + + XHTML Document Model + This module describes the groupings of elements/attributes + that make up common content models for XHTML elements. + XHTML has following basic content models: + xhtml.Inline.mix; character-level elements + xhtml.Block.mix; block-like elements, e.g., paragraphs and lists + xhtml.Flow.mix; any block or inline elements + xhtml.HeadOpts.mix; Head Elements + xhtml.InlinePre.mix; Special class for pre content model + xhtml.InlineNoAnchor.mix; Content model for Anchor + + Any groups declared in this module may be used to create + element content models, but the above are considered 'global' + (insofar as that term applies here). XHTML has the + following Attribute Groups + xhtml.Core.extra.attrib + xhtml.I18n.extra.attrib + xhtml.Common.extra + + The above attribute Groups are considered Global + + + + + Extended I18n attribute + + + + + "dir" Attribute from Bi Directional Text (bdo) Module + + + + + + + + Extended Common Attributes + + + + + "style" attribute from Inline Style Module + + + + + + + Attributes from Events Module + + + + + + + Extend Core Attributes + + + + + Extended Global Core Attributes + + + + + Extended Global I18n attributes + + + + + Extended Global Common Attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-modules-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-modules-1.xsd new file mode 100644 index 00000000000..676706760a5 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11-modules-1.xsd @@ -0,0 +1,605 @@ + + + + + + + This schema includes all modules for XHTML1.1 Document Type. + $Id: xhtml11-modules-1.xsd,v 1.10 2009/02/03 15:14:49 ahby Exp $ + + + + + + This schema includes all modules (and redefinitions) + for XHTML1.1 Document Type. + XHTML1.1 Document Type includes the following Modules + + XHTML Core modules (Required for XHTML Family Conformance) + + text + + hypertext + + lists + + structure + + Other XHTML modules + + Edit + + Bdo + + Presentational + + Link + + Meta + + Base + + Scripting + + Style + + Image + + Applet + + Object + + Param (Applet/Object modules require Param Module) + + Tables + + Target + + Forms + + Client side image maps + + Server side image maps + + + + + + + Schema Framework Component Modules: + + notations + + datatypes + + common attributes + + character entities + + + + + + + + Text module + + The Text module includes declarations for all core + text container elements and their attributes. + + + block phrasal + + block structural + + inline phrasal + + inline structural + + Elements defined here: + * address, blockquote, pre, h1, h2, h3, h4, h5, h6 + * div, p + * abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var + * br, span + + + + + + + + Hypertext module + + Elements defined here: + * a + + + + + + + + + Redefinition by Client Side Image Map Module + + + + + + + Redefinition by XHTML Event Attribute Module + + + + + + + Target Module - A Attribute Additions + + + + + + + + + Lists module + + Elements defined here: + * dt, dd, dl, ol, ul, li + + + + + + + + Structural module + + Elements defined here: + * title, head, body, html + + + + + + + Redefinition by the XHTML11 Markup (for value of version attr) + + + + + + + + + Original Body Attlist + + + + + + + Redefinition by XHTML Event Attribute Module + + + + + + + + + Edit module + + Elements defined here: + * ins, del + + + + + + + + Bidirectional element module + + Elements defined here: + * bdo + + + + + + + + Presentational module + + Elements defined here: + * hr, b, big, i, small,sub, sup, tt + + + + + + + + Link module + + Elements defined here: + * link + + + + + + + Changes to XHTML Link Attlist + + + + + + Original Link Attributes (declared in Link Module) + + + + + + + XHTML Target Module - Attribute additions + + + + + + + + + Meta module + + Elements defined here: + * meta + + + + + + + + Base module + + Elements defined here: + * base + + + + + + + Changes to XHTML base Attlist + + + + + + Original Base Attributes (declared in Base Module) + + + + + + + XHTML Target Module - Attribute additions + + + + + + + + + Scripting module + + Elements defined here: + * script, noscript + + + + + + + + Style module + + Elements defined here: + * style + + + + + + + + Style attribute module + + Attribute defined here: + * style + + + + + + + + Image module + + Elements defined here: + * img + + + + + + + + Original Image Attributes (in Image Module) + + + + + + + Redefinition by Client Side Image Map Module + + + + + + + Redefinition by Server Side Image Module + + + + + + + + + Client-side mage maps module + + Elements defined here: + * area, map + + + + + + + + Original Area Attributes (in CSI Module) + + + + + + + Redefinition by Events Attribute Module + + + + + + + Target Module - Area Attribute Additions + + + + + + + + + Server-side image maps module + + Attributes defined here: + * ismap on img + + + + + + + + Object module + + Elements defined here: + * object + + + + + + + + Original Object Attlist + + + + + + + Redefinition by Client Image Map Module + + + + + + + + + Param module + + Elements defined here: + * param + + + + + + + Tables module + + Elements defined here: + * table, caption, thead, tfoot, tbody, colgroup, col, tr, th, td + + + + + + + + Forms module + + Elements defined here: + * form, label, input, select, optgroup, option, + * textarea, fieldset, legend, button + + + + + + + Changes to XHTML Form Attlist + + + + + + Original Form Attributes (declared in Forms Module) + + + + + + + XHTML Events Module - Attribute additions + + + + + + + XHTML Target Module - Attribute additions + + + + + + + + Changes to XHTML Form Input Element + + + + + + Original Input Attributes (in Forms Module) + + + + + + + Redefinition by Client Side Image Map Module + + + + + + + Redefinition by Server Side Image Map Module + + + + + + + Redefinition by Event Attribute Module + + + + + + + + + Original Label Attributes (in Forms Module) + + + + + + + Redefinition by Event Attribute Module + + + + + + + + + Original Select Attributes (in Forms Module) + + + + + + + Redefinition by Event Attribute Module + + + + + + + + + Original TextArea Attributes (in Forms Module) + + + + + + + Redefinition by Event Attribute Module + + + + + + + + + Original Button Attributes (in Forms Module) + + + + + + + Redefinition by Event Attribute Module + + + + + + + + + Ruby module + + Elements defined here: + * ruby, rbc, rtc, rb, rt, rp + + Note that either Ruby or Basic Ruby should be used but not both + + + + + + + + XHTML Events Modules + + Attributes defined here: + XHTML Event Types + + + + + + + + XHTML Target Attribute Module + + Attributes defined here: + target + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11.xsd new file mode 100644 index 00000000000..341699935d2 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xhtml11.xsd @@ -0,0 +1,104 @@ + + + + + This is the XML Schema driver for XHTML 1.1. + Please use this namespace for XHTML elements: + + "http://www.w3.org/1999/xhtml" + + $Id: xhtml11.xsd,v 1.7 2009/02/03 15:14:49 ahby Exp $ + + + + + + This is XHTML, a reformulation of HTML as a modular XML application + The Extensible HyperText Markup Language (XHTML) + Copyright ©1998-2007 World Wide Web Consortium + (Massachusetts Institute of Technology, European Research Consortium + for Informatics and Mathematics, Keio University). + All Rights Reserved. + + Permission to use, copy, modify and distribute the XHTML Schema + modules and their accompanying xs:documentation for any purpose + and without fee is hereby granted in perpetuity, provided that the above + copyright notice and this paragraph appear in all copies. + The copyright holders make no representation about the suitability of + these XML Schema modules for any purpose. + + They are provided "as is" without expressed or implied warranty. + + + + + This is the Schema Driver file for XHTML1.1 + Document Type + + This schema + + imports external schemas (xml.xsd) + + refedines (and include)s schema modules for XHTML1.1 Document Type. + + includes Schema for Named content model for the + XHTML1.1 Document Type + + XHTML1.1 Document Type includes the following Modules + XHTML Core modules (Required for XHTML Family Conformance) + + text + + hypertext + + lists + + structure + Other XHTML modules + + Edit + + Bdo + + Presentational + + Link + + Meta + + Base + + Scripting + + Style + + Image + + Applet + + Object + + Param (Applet/Object modules require Param Module) + + Tables + + Forms + + Client side image maps + + Server side image maps + + Ruby + + + + + + This import brings in the XML namespace attributes + The XML attributes are used by various modules. + + + + + + + Document Model module for the XHTML1.1 Document Type. + This schema file defines all named models used by XHTML + Modularization Framework for XHTML1.1 Document Type + + + + + + + Schema that includes all modules (and redefinitions) + for XHTML1.1 Document Type. + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-1.xsd new file mode 100644 index 00000000000..0d53b8a52a2 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-1.xsd @@ -0,0 +1,73 @@ + + + + + + This is the XML Schema for XML Events + + URI: http://www.w3.org/MarkUp/SCHEMA/xml-events-1.xsd + $Id: xml-events-1.xsd,v 1.8 2004/11/22 17:09:15 ahby Exp $ + + + + + + + XML Events element listener + + This module defines the listener element for XML Events. + This element can be used to define event listeners. This + module relies upon the XmlEvents.attlist attribute group + defined in xml-events-attribs-1.xsd. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-copyright-1.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-copyright-1.xsd new file mode 100644 index 00000000000..ebe0e924003 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xhtml11/xml-events-copyright-1.xsd @@ -0,0 +1,34 @@ + + + + + + This is XML Events, a generalized event model for XML-based + markup languages. + + Copyright 2001-2003 World Wide Web Consortium + (Massachusetts Institute of Technology, European Research + Consortium for Informatics and Mathematics, Keio University). + All Rights Reserved. + + Permission to use, copy, modify and distribute the + XML Events Schema modules and their accompanying xs:documentation + for any purpose and without fee is hereby granted in perpetuity, + provided that the above copyright notice and this paragraph appear + in all copies. + + The copyright holders make no representation about the suitability of + these XML Schema modules for any purpose. + + They are provided "as is" without expressed or implied warranty. + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xmlNS2001/xml.xsd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xmlNS2001/xml.xsd new file mode 100644 index 00000000000..cd234ef748a --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/w3c/xsd/xmlNS2001/xml.xsd @@ -0,0 +1,117 @@ + + + + + + + See http://www.w3.org/XML/1998/namespace.html and + http://www.w3.org/TR/REC-xml for information about this namespace. + + This schema document describes the XML namespace, in a form + suitable for import by other schema documents. + + Note that local names in this namespace are intended to be defined + only by the World Wide Web Consortium or its subgroups. The + following names are currently defined in this namespace and should + not be used with conflicting semantics by any Working Group, + specification, or document instance: + + base (as an attribute name): denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification. + + lang (as an attribute name): denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification. + + space (as an attribute name): denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification. + + Father (in any context at all): denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: + + In appreciation for his vision, leadership and dedication + the W3C XML Plenary on this 10th day of February, 2000 + reserves for Jon Bosak in perpetuity the XML name + xml:Father + + + + + This schema defines attributes and an attribute group + suitable for use by + schemas wishing to allow xml:base, xml:lang or xml:space attributes + on elements they define. + + To enable this, such a schema must import this schema + for the XML namespace, e.g. as follows: + <schema . . .> + . . . + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/03/xml.xsd"/> + + Subsequently, qualified reference to any of the attributes + or the group defined below will have the desired effect, e.g. + + <type . . .> + . . . + <attributeGroup ref="xml:specialAttrs"/> + + will define a type which will schema-validate an instance + element with any of those attributes + + + + In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + http://www.w3.org/2001/03/xml.xsd. + At the date of issue it can also be found at + http://www.w3.org/2001/xml.xsd. + The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML Schema + itself. In other words, if the XML Schema namespace changes, the version + of this document at + http://www.w3.org/2001/xml.xsd will change + accordingly; the version at + http://www.w3.org/2001/03/xml.xsd will not change. + + + + + + In due course, we should install the relevant ISO 2- and 3-letter + codes as the enumerated possible values . . . + + + + + + + + + + + + + + + See http://www.w3.org/TR/xmlbase/ for + information about this attribute. + + + + + + + + + + \ No newline at end of file diff --git a/src/java.xml/share/classes/module-info.java b/src/java.xml/share/classes/module-info.java index e5c421261b0..a12fd3e8f45 100644 --- a/src/java.xml/share/classes/module-info.java +++ b/src/java.xml/share/classes/module-info.java @@ -417,11 +417,97 @@ * * *

JDK built-in Catalog

- * The JDK has a built-in catalog that hosts the following DTDs defined by the Java Platform: - *
    - *
  • DTD for {@link java.util.prefs.Preferences java.util.prefs.Preferences}, preferences.dtd
  • - *
  • DTD for {@link java.util.Properties java.util.Properties}, properties.dtd
  • - *
+ * The JDK has a built-in catalog that hosts DTDs and XSDs list in the following table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
DTDs and XSDs in JDK built-in Catalog
SourceFiles
+ * {@link java.util.prefs.Preferences java.util.prefs.Preferences} + * preferences.dtd + *
+ * {@link java.util.Properties java.util.Properties} + * properties.dtd + *
+ * XML Schema Part 1: Structures Second Edition
+ * XML Schema Part 2: Datatypes Second Edition + *
+ * XMLSchema.dtd
+ * datatypes.dtd
+ * XMLSchema.xsd
+ * datatypes.xsd + *
+ * XHTML™ 1.0 The Extensible HyperText Markup Language + * + * xhtml1-frameset.dtd
+ * xhtml1-strict.dtd
+ * xhtml1-transitional.dtd + *
+ * XHTML™ 1.0 in XML Schema + * + * xhtml1-frameset.xsd
+ * xhtml1-strict.xsd
+ * xhtml1-transitional.xsd + *
+ * XHTML™ 1.1 - Module-based XHTML - Second Edition + * + * xhtml11.dtd + *
+ * XHTML 1.1 XML Schema Definition + * + * xhtml11.xsd + *
+ * XML DTD for W3C specifications + * + * xmlspec.dtd + *
+ * The "xml:" Namespace + * + * xml.xsd + *
*

* The catalog is loaded once when the first JAXP processor factory is created. * diff --git a/src/java.xml/share/legal/schema10part1.md b/src/java.xml/share/legal/schema10part1.md new file mode 100644 index 00000000000..bd7498b9e47 --- /dev/null +++ b/src/java.xml/share/legal/schema10part1.md @@ -0,0 +1,51 @@ +## XML Schema Part 1: Structures Second Edition + +### W3C Software and Document license +

+Software and Document license - 2023 version
+
+Copied from:  https://www.w3.org/copyright/software-license-2023
+
+License
+
+By obtaining and/or copying this work, you (the licensee) agree that you have
+read, understood, and will comply with the following terms and conditions.
+
+Permission to copy, modify, and distribute this work, with or without modification,
+for any purpose and without fee or royalty is hereby granted, provided that you
+include the following on ALL copies of the work or portions thereof, including
+modifications:
+
+    The full text of this NOTICE in a location viewable to users of the
+    redistributed or derivative work.
+    Any pre-existing intellectual property disclaimers, notices, or terms and
+    conditions. If none exist, the W3C software and document short notice should
+    be included.
+    Notice of any changes or modifications, through a copyright statement on the
+    new code or document such as "This software or document includes material
+    copied from or derived from [title and URI of the W3C document]. Copyright ©
+    [$year-of-document] World Wide Web Consortium.
+    https://www.w3.org/copyright/software-license-2023/"
+
+Disclaimers
+
+THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF
+MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
+SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
+TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
+
+The name and trademarks of copyright holders may NOT be used in advertising or
+publicity pertaining to the work without specific, written prior permission.
+Title to copyright in this work will at all times remain with copyright holders.
+
+------
+
+Copyright © 2004 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability,
+trademark and document use rules apply.
+
+
+
diff --git a/src/java.xml/share/legal/schema10part2.md b/src/java.xml/share/legal/schema10part2.md new file mode 100644 index 00000000000..601b06db18b --- /dev/null +++ b/src/java.xml/share/legal/schema10part2.md @@ -0,0 +1,50 @@ +## XML Schema Part 2: Datatypes Second Edition + +### W3C Software and Document license +
+Software and Document license - 2023 version
+
+Copied from:  https://www.w3.org/copyright/software-license-2023
+
+License
+
+By obtaining and/or copying this work, you (the licensee) agree that you have
+read, understood, and will comply with the following terms and conditions.
+
+Permission to copy, modify, and distribute this work, with or without modification,
+for any purpose and without fee or royalty is hereby granted, provided that you
+include the following on ALL copies of the work or portions thereof, including
+modifications:
+
+    The full text of this NOTICE in a location viewable to users of the
+    redistributed or derivative work.
+    Any pre-existing intellectual property disclaimers, notices, or terms and
+    conditions. If none exist, the W3C software and document short notice should
+    be included.
+    Notice of any changes or modifications, through a copyright statement on the
+    new code or document such as "This software or document includes material
+    copied from or derived from [title and URI of the W3C document]. Copyright ©
+    [$year-of-document] World Wide Web Consortium.
+    https://www.w3.org/copyright/software-license-2023/"
+
+Disclaimers
+
+THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF
+MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
+SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
+TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
+
+The name and trademarks of copyright holders may NOT be used in advertising or
+publicity pertaining to the work without specific, written prior permission.
+Title to copyright in this work will at all times remain with copyright holders.
+
+------
+
+Copyright © 2004 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability,
+trademark and document use rules apply.
+
+
diff --git a/src/java.xml/share/legal/xhtml10.md b/src/java.xml/share/legal/xhtml10.md new file mode 100644 index 00000000000..765584b6c38 --- /dev/null +++ b/src/java.xml/share/legal/xhtml10.md @@ -0,0 +1,57 @@ +## XHTMLâ„¢ 1.0 The Extensible HyperText Markup Language + +### W3C Software Notice and License +
+W3C(R) SOFTWARE NOTICE AND LICENSE
+
+Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts
+Institute of Technology, Institut National de Recherche en
+Informatique et en Automatique, Keio University). All Rights
+Reserved. http://www.w3.org/Consortium/Legal/
+
+This W3C work (including software, documents, or other related items)
+is being provided by the copyright holders under the following
+license. By obtaining, using and/or copying this work, you (the
+licensee) agree that you have read, understood, and will comply with
+the following terms and conditions:
+
+Permission to use, copy, modify, and distribute this software and its
+documentation, with or without modification, for any purpose and
+without fee or royalty is hereby granted, provided that you include
+the following on ALL copies of the software and documentation or
+portions thereof, including modifications, that you make:
+
+    The full text of this NOTICE in a location viewable to users of
+    the redistributed or derivative work.
+
+    Any pre-existing intellectual property disclaimers, notices, or
+    terms and conditions. If none exist, a short notice of the following
+    form (hypertext is preferred, text is permitted) should be used within
+    the body of any redistributed or derivative code: "Copyright (C)
+    [$date-of-software] World Wide Web Consortium, (Massachusetts
+    Institute of Technology, Institut National de Recherche en
+    Informatique et en Automatique, Keio University). All Rights
+    Reserved. http://www.w3.org/Consortium/Legal/"
+
+    Notice of any changes or modifications to the W3C files, including
+    the date changes were made. (We recommend you provide URIs to the
+    location from which the code is derived.)
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT
+HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR
+DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
+TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR
+DOCUMENTATION.
+
+The name and trademarks of copyright holders may NOT be used in
+advertising or publicity pertaining to the software without specific,
+written prior permission. Title to copyright in this software and any
+associated documentation will at all times remain with copyright
+holders.
+
+
diff --git a/src/java.xml/share/legal/xhtml10schema.md b/src/java.xml/share/legal/xhtml10schema.md new file mode 100644 index 00000000000..b6385687551 --- /dev/null +++ b/src/java.xml/share/legal/xhtml10schema.md @@ -0,0 +1,150 @@ +## XHTML™ 1.0 in XML Schema + +### W3C® Intellectual Rights Notice and Legal Disclaimers +
+Copyright © 1994-2002 W3C ® (Massachusetts Institute of Technology,
+Institut National de Recherche en Informatique et en Automatique, Keio University),
+All Rights Reserved.
+
+World Wide Web Consortium (W3C®) web site pages may contain other proprietary
+notices and copyright information, the terms of which must be observed and
+followed. Specific notices do exist for W3C documents and software. Also, there
+are specific usage policies associated with some of the W3C Icons. Please see
+our Intellectual Rights FAQ for common questions about using materials from our site.
+
+Notice and Disclaimers
+
+1. Unless otherwise noted, all materials contained in this Site are copyrighted
+and may not be used except as provided in these terms and conditions or in the
+copyright notice (documents and software) or other proprietary notice provided
+with the relevant materials.
+
+2. The materials contained in the Site may be downloaded or copied provided that
+ALL copies retain the copyright and any other proprietary notices contained on
+the materials. No material may be modified, edited or taken out of context such
+that its use creates a false or misleading statement or impression as to the
+positions, statements or actions of W3C.
+
+3. The name and trademarks of copyright holders may NOT be used in advertising
+or publicity pertaining to the Web site, its content, specifications, or
+software without specific, written prior permission. Title to copyright in
+Web site documents will at all times remain with copyright holders. Use of W3C
+trademarks and service marks is covered by the W3C Trademark and Servicemark
+License.
+
+4. Caches of W3C materials should comply with the "maximum time to live"
+information provided with the materials. After such materials have expired
+they should not be served from caches without first validating the contents
+of the W3C Site. Organizations that want to mirror W3C content must abide by
+the W3C Mirroring Policy.
+
+W3C®Trademarks and Generic Terms
+Trademarks owned by W3C host institutions on behalf of W3C and generic terms
+used by the W3C
+
+5. The trademarks, logos, and service marks (collectively the "Trademarks")
+displayed on the Site are registered and unregistered Trademarks of the
+Massachusetts Institute of Technology (MIT), Institut National de Recherche
+en Informatique et en Automatique (INRIA), or Keio University (Keio). All use
+of the W3C Trademarks is governed by the W3C Trademark and Servicemark License.
+No additional rights are granted by implication, estoppel, or otherwise. Terms
+which claimed as generic are not governed by any W3C license and are used as
+common descriptors by the W3C.
+
+The following is a list of W3C terms claimed as a trademark or generic term
+by MIT, INRIA, and/or Keio on behalf of the W3C:
+
+    W3C®, World Wide Web Consortium  (registered in numerous countries)
+    Amayaâ„¢, a Web Browser
+    CSSâ„¢, Cascading Style Sheets Specification
+    DOMâ„¢, Document Object Model
+    HTML (generic), HyperText Markup Language
+    HTTP (generic), Hypertext Transfer Protocol
+    MathMLâ„¢, Mathematical Markup Language
+    Metadata (generic)
+    P3Pâ„¢, Platform for Privacy Preferences Project
+    PICSâ„¢, Platform for Internet Content Selection
+    RDF (generic), Resource Description Framework
+    SMILâ„¢, Synchronized Multimedia Integration Language
+    SVGâ„¢, Scalable Vector Graphics
+    WAIâ„¢, Web Accessibility Initiative
+    XENC (generic), XML Encryption
+    XHTMLâ„¢, The Extensible HyperText Markup Language
+    XML (generic), Extensible Markup Language
+    XSLâ„¢,  Extensible Stylesheet Language
+
+    ACSSâ„¢, Aural Cascading Style Sheets
+    DSigâ„¢, Digital Signature Initiative
+    JEPIâ„¢, Joint Electronic Payment Initiative
+    Jigsawâ„¢
+    PICSRulesâ„¢
+    WebFontsâ„¢
+
+The absence of a product or service name or logo from this list does not
+constitute a waiver of MIT's, INRIA's, or Keio's trademark or other intellectual
+rights concerning that name or logo.
+
+Any questions concerning the use, status, or standing of W3C trademarks should
+be directed to: site-policy@w3.org or to W3C (c/o Joseph Reagle), Laboratory
+for Computer Science NE43-358, Massachusetts Institute of Technology, 200
+Technology Square, Cambridge, MA 02139.
+
+Non-W3C Trademarks; Member Trademarks
+
+The trademarks, logos, and service marks not owned on behalf of the W3C and
+that are displayed on the Site are the registered and unregistered marks of
+their respective owners. No rights are granted by the W3C to use such marks,
+whether by implication, estoppel, or otherwise.
+
+"METADATA" is a trademark of the Metadata Company. W3C uses the term "metadata"
+in a descriptive sense, meaning "data about data". W3C is not in any way
+affiliated with the Metadata Company.
+Legal Disclaimers
+
+6. W3C has not reviewed any or all of the web sites linked to this Site and is
+not responsible for the content of any off-site pages or any other web sites
+linked to this Site. Please understand that any non-W3C web site is independent
+from W3C, and W3C has no control over the content on that web site.
+In addition, a link to a non-W3C web site does not mean that W3C endorses or
+accepts any responsibility for the content, or the use, of such site. It is
+the user's responsibility to take precautions to ensure that whatever is
+selected is free of such items as viruses, worms, Trojan horses and other
+items of a destructive nature.
+
+7. Information W3C publishes on its Site may contain references or cross
+references to W3C specifications, projects, programs and services that are
+not announced or available in your country. Such references do not imply that
+W3C intends to announce such specifications, projects, programs or services
+in your country.
+
+8. Information on this Site may contain technical inaccuracies or typographical
+errors. Information may be changed or updated without notice. W3C may make
+improvements and/or changes in the materials contained in or described on this
+site at any time without notice. W3C may also make changes in these Terms and
+Conditions without notice. User is bound by such revisions and should therefore
+periodically visit this page to review the then current Terms and Conditions.
+
+9. Limitation on Warranties.
+
+ALL MATERIALS ON THE W3C SITE ARE PROVIDED "AS IS." W3C, MIT, INRIA, AND KEIO
+MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+TITLE OR NON-INFRINGEMENT. AS TO DOCUMENTS AND GRAPHICS PUBLISHED ON THIS SITE,
+W3C, MIT, INRIA, AND KEIO MAKE NO REPRESENTATION OR WARRANTY THAT THE CONTENTS
+OF SUCH DOCUMENT OR GRAPHICS ARE FREE FROM ERROR OR SUITABLE FOR ANY PURPOSE;
+NOR THAT IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY
+PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+Please note that some jurisdictions may not allow the exclusion of implied
+ warranties, so some of the above exclusions may not apply to you.
+
+10. Limitation on Liability.
+
+IN NO EVENT WILL W3C, MIT, INRIA, AND KEIO BE LIABLE TO ANY PARTY FOR ANY
+DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES FOR ANY USE OF THIS SITE,
+OR ON ANY OTHER HYPERLINKED WEB SITE, INCLUDING, WITHOUT LIMITATION, ANY LOST
+PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON YOUR
+INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN IF W3C, MIT, INRIA, OR KEIO
+IS EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
diff --git a/src/java.xml/share/legal/xhtml11.md b/src/java.xml/share/legal/xhtml11.md new file mode 100644 index 00000000000..b447a5ac9e4 --- /dev/null +++ b/src/java.xml/share/legal/xhtml11.md @@ -0,0 +1,68 @@ +## XHTMLâ„¢ 1.1 - Module-based XHTML - Second Edition + +### W3C Document license +
+From: https://www.w3.org/copyright/document-license-2023
+
+Copied 10/15/2024
+
+License
+
+By using and/or copying this document, or the W3C document from which this
+statement is linked, you (the licensee) agree that you have read, understood,
+and will comply with the following terms and conditions:
+
+Permission to copy, and distribute the contents of this document, or the W3C
+document from which this statement is linked, in any medium for any purpose
+and without fee or royalty is hereby granted, provided that you include the
+following on ALL copies of the document, or portions thereof, that you use:
+
+    A link or URL to the original W3C document.
+    The pre-existing copyright notice of the original author, or if it doesn't
+    exist, a notice (hypertext is preferred, but a textual representation is
+    permitted) of the form: "Copyright © [$date-of-document] World Wide Web
+    Consortium. https://www.w3.org/copyright/document-license-2023/"
+    If it exists, the STATUS of the W3C document.
+
+When space permits, inclusion of the full text of this NOTICE should be provided.
+We request that authorship attribution be provided in any software, documents,
+or other items or products that you create pursuant to the implementation of the
+contents of this document, or any portion thereof.
+
+No right to create modifications or derivatives of W3C documents is granted
+pursuant to this license, except as follows: To facilitate implementation of
+the technical specifications set forth in this document, anyone may prepare
+and distribute derivative works and portions of this document in software,
+in supporting materials accompanying software, and in documentation of software,
+PROVIDED that all such works include the notice below. HOWEVER, the publication
+of derivative works of this document for use as a technical specification is
+expressly prohibited.
+
+In addition, "Code Components" —Web IDL in sections clearly marked as Web IDL;
+and W3C-defined markup (HTML, CSS, etc.) and computer programming language code
+clearly marked as code examples— are licensed under the W3C Software License.
+
+The notice is:
+
+"Copyright © 2023 W3C®. This software or document includes material copied from
+or derived from [title and URI of the W3C document]."
+
+Disclaimers §anchor
+
+THIS DOCUMENT IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS
+OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE;
+THAT THE CONTENTS OF THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE
+IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS,
+COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE
+OR IMPLEMENTATION OF THE CONTENTS THEREOF.
+
+The name and trademarks of copyright holders may NOT be used in advertising or
+publicity pertaining to this document or its contents without specific, written
+prior permission. Title to copyright in this document will at all times remain
+with copyright holders.
+
+
diff --git a/src/java.xml/share/legal/xhtml11schema.md b/src/java.xml/share/legal/xhtml11schema.md new file mode 100644 index 00000000000..c7a2f0efda4 --- /dev/null +++ b/src/java.xml/share/legal/xhtml11schema.md @@ -0,0 +1,60 @@ +## XHTML 1.1 XML Schema Definition + +### W3C® SOFTWARE NOTICE AND LICENSE +
+http://www.w3.org/Consortium/Legal/copyright-software-19980720
+
+W3C® SOFTWARE NOTICE AND LICENSE
+
+Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of
+Technology, Institut National de Recherche en Informatique et en Automatique,
+Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/
+
+This W3C work (including software, documents, or other related items) is
+being provided by the copyright holders under the following license. By
+obtaining, using and/or copying this work, you (the licensee) agree that you
+have read, understood, and will comply with the following terms and conditions:
+Permission to use, copy, modify, and distribute this software and its
+documentation, with or without modification,  for any purpose and without fee
+or royalty is hereby granted, provided that you include the following on ALL
+copies of the software and documentation or portions thereof, including
+modifications, that you make:
+
+1.      The full text of this NOTICE in a location viewable to users of the
+redistributed or derivative work.
+
+2.      Any pre-existing intellectual property disclaimers, notices, or terms
+and conditions. If none exist, a short notice of the following form (hypertext
+is preferred, text is permitted) should be used within the body of any
+redistributed or derivative code: "Copyright © [$date-of-software] World Wide
+Web Consortium, (Massachusetts Institute of Technology, Institut National de
+Recherche en Informatique et en Automatique, Keio University). All Rights
+Reserved. http://www.w3.org/Consortium/Legal/"
+
+3.      Notice of any changes or modifications to the W3C files, including the
+date changes were made. (We recommend you provide URIs to the location from
+which the code is derived.)
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
+NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
+THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
+PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
+
+The name and trademarks of copyright holders may NOT be used in advertising or
+publicity pertaining to the software without specific, written prior permission.
+Title to copyright in this software and any associated documentation will at all
+times remain with copyright holders.
+
+The formulation of W3C's notice and license became active on August 14 1998 so
+as to improve compatibility with GPL. This version ensures that W3C software
+licensing terms are no more restrictive than GPL and consequently W3C software
+may be distributed in GPL packages. See the older formulation for the policy
+prior to this date. Please see our Copyright FAQ for common questions about
+using materials from our site, including specific terms and conditions for
+packages like libwww, Amaya, and Jigsaw. Other questions about this notice can
+be directed to site-policy@w3.org.
+
+
diff --git a/src/java.xml/share/legal/xmlspec.md b/src/java.xml/share/legal/xmlspec.md new file mode 100644 index 00000000000..bfb7d4f6ed3 --- /dev/null +++ b/src/java.xml/share/legal/xmlspec.md @@ -0,0 +1,63 @@ +## XML DTD for W3C specifications + +### W3C Software Notice and License +
+W3C(R) SOFTWARE NOTICE AND LICENSE
+
+Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts
+Institute of Technology, Institut National de Recherche en
+Informatique et en Automatique, Keio University). All Rights
+Reserved. http://www.w3.org/Consortium/Legal/
+
+This W3C work (including software, documents, or other related items)
+is being provided by the copyright holders under the following
+license. By obtaining, using and/or copying this work, you (the
+licensee) agree that you have read, understood, and will comply with
+the following terms and conditions:
+
+Permission to use, copy, modify, and distribute this software and its
+documentation, with or without modification, for any purpose and
+without fee or royalty is hereby granted, provided that you include
+the following on ALL copies of the software and documentation or
+portions thereof, including modifications, that you make:
+
+    The full text of this NOTICE in a location viewable to users of
+    the redistributed or derivative work.
+
+    Any pre-existing intellectual property disclaimers, notices, or
+    terms and conditions. If none exist, a short notice of the following
+    form (hypertext is preferred, text is permitted) should be used within
+    the body of any redistributed or derivative code: "Copyright (C)
+    [$date-of-software] World Wide Web Consortium, (Massachusetts
+    Institute of Technology, Institut National de Recherche en
+    Informatique et en Automatique, Keio University). All Rights
+    Reserved. http://www.w3.org/Consortium/Legal/"
+
+    Notice of any changes or modifications to the W3C files, including
+    the date changes were made. (We recommend you provide URIs to the
+    location from which the code is derived.)
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT
+HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR
+DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
+TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR
+DOCUMENTATION.
+
+The name and trademarks of copyright holders may NOT be used in
+advertising or publicity pertaining to the software without specific,
+written prior permission. Title to copyright in this software and any
+associated documentation will at all times remain with copyright
+holders.
+
+----------
+
+COPYRIGHT:
+
+  Copyright (C) 2000, 2001, 2002, 2003 Sun Microsystems, Inc. All Rights Reserved.
+
+
diff --git a/src/java.xml/share/legal/xmlxsd.md b/src/java.xml/share/legal/xmlxsd.md new file mode 100644 index 00000000000..163e26e9f77 --- /dev/null +++ b/src/java.xml/share/legal/xmlxsd.md @@ -0,0 +1,43 @@ +## The "xml:" Namespace + +### W3C Software and Document license +
+From: https://www.w3.org/copyright/software-license-2023/
+Copied on 2024/10/15
+
+License
+
+By obtaining and/or copying this work, you (the licensee) agree that you have
+read, understood, and will comply with the following terms and conditions.
+
+Permission to copy, modify, and distribute this work, with or without modification,
+for any purpose and without fee or royalty is hereby granted, provided that you
+include the following on ALL copies of the work or portions thereof, including
+modifications:
+
+    The full text of this NOTICE in a location viewable to users of the
+    redistributed or derivative work.
+    Any pre-existing intellectual property disclaimers, notices, or terms and
+    conditions. If none exist, the W3C software and document short notice should
+    be included.
+    Notice of any changes or modifications, through a copyright statement on the
+    new code or document such as "This software or document includes material
+    copied from or derived from [title and URI of the W3C document]. Copyright ©
+    [$year-of-document] World Wide Web Consortium.
+    https://www.w3.org/copyright/software-license-2023/"
+
+Disclaimers §anchor
+
+THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF
+MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
+SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,
+TRADEMARKS OR OTHER RIGHTS.
+
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
+
+The name and trademarks of copyright holders may NOT be used in advertising or
+publicity pertaining to the work without specific, written prior permission.
+Title to copyright in this work will at all times remain with copyright holders.
+
diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport2.java b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport2.java index 5bd748c3d87..ecb88de1932 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport2.java +++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport2.java @@ -51,7 +51,7 @@ /* * @test - * @bug 8158084 8162438 8162442 8163535 8166220 + * @bug 8158084 8162438 8162442 8163535 8166220 8344800 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm catalog.CatalogSupport2 * @summary extends CatalogSupport tests, verifies that the use of the Catalog may @@ -234,7 +234,7 @@ public Object[][] getDataSchemaC() { return new Object[][]{ // for resolving DTD in xsd - {false, true, xml_catalog, xsd_xmlSchema, null}, + {false, true, xml_catalog, xsd_val_test_dtd, null}, // for resolving xsd import {false, true, xml_catalog, xsd_xmlSchema_import, null}, // for resolving xsd include diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport3.java b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport3.java index 0b2c0198699..4d44ce80fe6 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport3.java +++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupport3.java @@ -51,7 +51,7 @@ /* * @test - * @bug 8158084 8162438 8162442 8163535 8166220 + * @bug 8158084 8162438 8162442 8163535 8166220 8344800 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm catalog.CatalogSupport3 * @summary extends CatalogSupport tests, verifies that the use of the Catalog may @@ -236,7 +236,7 @@ public Object[][] getDataSchemaC() { return new Object[][]{ // for resolving DTD in xsd - {true, false, xml_catalog, xsd_xmlSchema, null}, + {true, false, xml_catalog, xsd_val_test_dtd, null}, // for resolving xsd import {true, false, xml_catalog, xsd_xmlSchema_import, null}, // for resolving xsd include diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java index e22c1edf6c2..faa260b275e 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -126,7 +126,7 @@ protected void setUp() { // For the xsd import and include String xsd_xmlSchema, dtd_xmlSchema, dtd_datatypes; String xsd_xmlSchema_import, xsd_xml; - String xml_val_test, xml_val_test_id, xsd_val_test; + String xml_val_test, xml_val_test_id, xsd_val_test, xsd_val_test_dtd; String xsd_include_company, xsd_include_person, xsd_include_product; String xsl_include, xsl_includeDTD, xsl_import_html, xsl_include_header, xsl_include_footer; @@ -254,6 +254,7 @@ void initFiles() { xml_val_test = filepath + "/val_test.xml"; xml_val_test_id = "file://" + slash + xml_val_test; xsd_val_test = filepath + "/val_test.xsd"; + xsd_val_test_dtd = Paths.get(filepath + "val_test_dtd.xsd").toUri().toASCIIString(); xml_xsl = "\n" + "\n" + @@ -375,7 +376,11 @@ public void testValidation(boolean setUseCatalog, boolean useCatalog, String cat factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); } - Schema schema = factory.newSchema(new StreamSource(new StringReader(xsd))); + if (xsd.endsWith(".xsd")) { + Schema schema = factory.newSchema(new StreamSource(xsd)); + } else { + Schema schema = factory.newSchema(new StreamSource(new StringReader(xsd))); + } success("XMLSchema.dtd and datatypes.dtd are resolved."); } diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/val_test_dtd.xsd b/test/jaxp/javax/xml/jaxp/unittest/catalog/val_test_dtd.xsd new file mode 100644 index 00000000000..1c320079e17 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/val_test_dtd.xsd @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + From 1997e89ddf9fba7c6eea6c96bd0b5426576d4460 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Mon, 2 Dec 2024 22:54:53 +0000 Subject: [PATCH 056/171] 8345346: Shenandoah: Description of ShenandoahGCMode still refers to incremental update mode Reviewed-by: ysr --- src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 4239fc37a26..a9c5e47c5c2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -134,7 +134,6 @@ "GC mode to use. Among other things, this defines which " \ "barriers are in in use. Possible values are:" \ " satb - snapshot-at-the-beginning concurrent GC (three pass mark-evac-update);" \ - " iu - incremental-update concurrent GC (three pass mark-evac-update);" \ " passive - stop the world GC only (either degenerated or full);" \ " generational - generational concurrent GC") \ \ @@ -183,7 +182,7 @@ range(0,100) \ \ product(uintx, ShenandoahInitFreeThreshold, 70, EXPERIMENTAL, \ - "When less than this amount of memory is free within the" \ + "When less than this amount of memory is free within the " \ "heap or generation, trigger a learning cycle if we are " \ "in learning mode. Learning mode happens during initialization " \ "and following a drastic state change, such as following a " \ From 68b1b94d1be686037e2aaef57c0d9adc594fac7a Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Mon, 2 Dec 2024 22:55:53 +0000 Subject: [PATCH 057/171] 8344904: Interned strings in old classes are not stored in CDS archive Reviewed-by: dholmes, ccheung --- src/hotspot/share/cds/metaspaceShared.cpp | 4 +- src/hotspot/share/oops/constantPool.cpp | 38 +++++++--- src/hotspot/share/oops/constantPool.hpp | 1 + .../OldClassWithStaticString.jasm | 49 +++++++++++++ .../sharedStrings/StaticStringInOldClass.java | 72 +++++++++++++++++++ 5 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/OldClassWithStaticString.jasm create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/StaticStringInOldClass.java diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 3ea2f54cfa9..3571f11bb6e 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -1014,9 +1014,7 @@ void VM_PopulateDumpSharedSpace::dump_java_heap_objects(GrowableArray* k Klass* k = klasses->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - if (ik->is_linked()) { - ik->constants()->add_dumped_interned_strings(); - } + ik->constants()->add_dumped_interned_strings(); } } if (_extra_interned_strings != nullptr) { diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 31644f33797..22896d9e00f 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -53,6 +53,7 @@ #include "oops/array.hpp" #include "oops/constantPool.inline.hpp" #include "oops/cpCache.inline.hpp" +#include "oops/fieldStreams.inline.hpp" #include "oops/instanceKlass.hpp" #include "oops/klass.inline.hpp" #include "oops/objArrayKlass.hpp" @@ -61,6 +62,7 @@ #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" +#include "runtime/fieldDescriptor.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" #include "runtime/javaCalls.hpp" @@ -403,18 +405,38 @@ void ConstantPool::find_required_hidden_classes() { } void ConstantPool::add_dumped_interned_strings() { - objArrayOop rr = resolved_references(); - if (rr != nullptr) { - int rr_len = rr->length(); - for (int i = 0; i < rr_len; i++) { - oop p = rr->obj_at(i); - if (java_lang_String::is_instance(p) && - !ArchiveHeapWriter::is_string_too_large_to_archive(p)) { - HeapShared::add_to_dumped_interned_strings(p); + InstanceKlass* ik = pool_holder(); + if (!ik->is_linked()) { + // resolved_references() doesn't exist yet, so we have no resolved CONSTANT_String entries. However, + // some static final fields may have default values that were initialized when the class was parsed. + // We need to enter those into the CDS archive strings table. + for (JavaFieldStream fs(ik); !fs.done(); fs.next()) { + if (fs.access_flags().is_static()) { + fieldDescriptor& fd = fs.field_descriptor(); + if (fd.field_type() == T_OBJECT) { + int offset = fd.offset(); + check_and_add_dumped_interned_string(ik->java_mirror()->obj_field(offset)); + } + } + } + } else { + objArrayOop rr = resolved_references(); + if (rr != nullptr) { + int rr_len = rr->length(); + for (int i = 0; i < rr_len; i++) { + check_and_add_dumped_interned_string(rr->obj_at(i)); } } } } + +void ConstantPool::check_and_add_dumped_interned_string(oop obj) { + if (obj != nullptr && java_lang_String::is_instance(obj) && + !ArchiveHeapWriter::is_string_too_large_to_archive(obj)) { + HeapShared::add_to_dumped_interned_strings(obj); + } +} + #endif #if INCLUDE_CDS diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 9ada3e29d49..935151c7cda 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -162,6 +162,7 @@ class ConstantPool : public Metadata { assert(is_within_bounds(cp_index), "index out of bounds"); return (jdouble*) &base()[cp_index]; } + static void check_and_add_dumped_interned_string(oop obj); ConstantPool(Array* tags); ConstantPool(); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/OldClassWithStaticString.jasm b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/OldClassWithStaticString.jasm new file mode 100644 index 00000000000..d36f0e1d212 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/OldClassWithStaticString.jasm @@ -0,0 +1,49 @@ +/* + * 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. + * + */ + +/* + +// Compiled from this source, but we want the version to be 49.0 to use the old verifier. + +class OldClassWithStaticString { + static final String s = "xxxx123yyyy456"; + static final String t = "OldClassWithStaticString"; +} + +*/ + +public super class OldClassWithStaticString + version 49:0 +{ + public static final Field s:"Ljava/lang/String;" = String "xxxx123yyyy456"; + public static final Field t:"Ljava/lang/String;" = String "OldClassWithStaticString"; + + Method "":"()V" + stack 1 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/StaticStringInOldClass.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/StaticStringInOldClass.java new file mode 100644 index 00000000000..f282b0c664d --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/StaticStringInOldClass.java @@ -0,0 +1,72 @@ +/* + * 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 8344904 + * @summary make sure all interned strings in old classes are archived. + * @requires vm.cds.write.archived.java.heap + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @build OldClassWithStaticString + * @build StaticStringInOldClass + * @run driver jdk.test.lib.helpers.ClassFileInstaller + * -jar StaticStringInOldClass.jar StaticStringInOldClass StaticStringInOldClassApp OldClassWithStaticString + * @run driver StaticStringInOldClass + */ + +import java.lang.reflect.Field; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.helpers.ClassFileInstaller; + +public class StaticStringInOldClass { + static final String appClass = StaticStringInOldClassApp.class.getName(); + static String[] classes = { + appClass, + OldClassWithStaticString.class.getName(), + }; + + public static void main(String[] args) throws Exception { + String appJar = ClassFileInstaller.getJarPath("StaticStringInOldClass.jar"); + OutputAnalyzer output; + output = TestCommon.testDump(appJar, TestCommon.list(classes)); + output = TestCommon.exec(appJar, appClass); + TestCommon.checkExec(output, "Hello"); + } +} + +class StaticStringInOldClassApp { + static String a = "xxxx123"; + public static void main(String args[]) throws Exception { + System.out.println("Hello"); + String x = (a + "yyyy456").intern(); + Class c = OldClassWithStaticString.class; + Field f = c.getField("s"); + String y = (String)(f.get(null)); + if (x != y) { + throw new RuntimeException("Interned strings not equal: " + + "\"" + x + "\" @ " + System.identityHashCode(x) + " vs " + + "\"" + y + "\" @ " + System.identityHashCode(y)); + } + } +} From 5958463cadb04560ec85d9af972255bfe6dcc2f2 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Mon, 2 Dec 2024 23:49:57 +0000 Subject: [PATCH 058/171] 8343377: Performance regression in reflective invocation of native methods Reviewed-by: mchung --- .../reflect/DirectMethodHandleAccessor.java | 2 +- .../reflect/MethodHandleAccessorFactory.java | 62 +++++++++++---- .../java/lang/reflect/NativeMethodInvoke.java | 79 +++++++++++++++++++ 3 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/lang/reflect/NativeMethodInvoke.java diff --git a/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java b/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java index 54f0c7c563d..e6c5aa5db2e 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java +++ b/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java @@ -47,7 +47,7 @@ class DirectMethodHandleAccessor extends MethodAccessorImpl { * Creates a MethodAccessorImpl for a non-native method. */ static MethodAccessorImpl methodAccessor(Method method, MethodHandle target) { - assert !Modifier.isNative(method.getModifiers()); + assert !MethodHandleAccessorFactory.isSignaturePolymorphicMethod(method); return new DirectMethodHandleAccessor(method, target, false); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java index 3a367272315..93a208662d5 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java +++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -28,6 +28,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; @@ -209,7 +210,7 @@ static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) { } private static MethodHandle getDirectMethod(Method method, boolean callerSensitive) throws IllegalAccessException { - var mtype = methodType(method.getReturnType(), method.getParameterTypes()); + var mtype = methodType(method.getReturnType(), reflectionFactory.getExecutableSharedParameterTypes(method)); var isStatic = Modifier.isStatic(method.getModifiers()); var dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), method.getName(), mtype) : JLIA.findVirtual(method.getDeclaringClass(), method.getName(), mtype); @@ -231,7 +232,7 @@ private static MethodHandle getDirectMethod(Method method, boolean callerSensiti private static MethodHandle findCallerSensitiveAdapter(Method method) throws IllegalAccessException { String name = method.getName(); // append a Class parameter - MethodType mtype = methodType(method.getReturnType(), method.getParameterTypes()) + MethodType mtype = methodType(method.getReturnType(), reflectionFactory.getExecutableSharedParameterTypes(method)) .appendParameterTypes(Class.class); boolean isStatic = Modifier.isStatic(method.getModifiers()); @@ -347,36 +348,39 @@ static void ensureClassInitialized(Class defc) { * Native accessor, i.e. VM reflection implementation, is used if one of * the following conditions is met: * 1. during VM early startup before method handle support is fully initialized - * 2. a Java native method - * 3. -Djdk.reflect.useNativeAccessorOnly=true is set + * 2. -Djdk.reflect.useNativeAccessorOnly=true is set + * 3. a signature polymorphic method * 4. the member takes a variable number of arguments and the last parameter * is not an array (see details below) * 5. the member's method type has an arity >= 255 * + * Conditions 3-5 are due to the restrictions of method handles. * Otherwise, direct invocation of method handles is used. */ private static boolean useNativeAccessor(Executable member) { if (!VM.isJavaLangInvokeInited()) return true; - if (Modifier.isNative(member.getModifiers())) + if (ReflectionFactory.useNativeAccessorOnly()) // for testing only return true; - if (ReflectionFactory.useNativeAccessorOnly()) // for testing only + // java.lang.invoke cannot find the underlying native stubs of signature + // polymorphic methods that core reflection must invoke. + // Fall back to use the native implementation instead. + if (member instanceof Method method && isSignaturePolymorphicMethod(method)) return true; - // MethodHandle::withVarargs on a member with varargs modifier bit set - // verifies that the last parameter of the member must be an array type. - // The JVMS does not require the last parameter descriptor of the method descriptor - // is an array type if the ACC_VARARGS flag is set in the access_flags item. - // Hence the reflection implementation does not check the last parameter type - // if ACC_VARARGS flag is set. Workaround this by invoking through - // the native accessor. + // For members with ACC_VARARGS bit set, MethodHandles produced by lookup + // always have variable arity set and hence the last parameter of the member + // must be an array type. Such restriction does not exist in core reflection + // and the JVM, which always use fixed-arity invocations. Fall back to use + // the native implementation instead. int paramCount = member.getParameterCount(); if (member.isVarArgs() && - (paramCount == 0 || !(member.getParameterTypes()[paramCount-1].isArray()))) { + (paramCount == 0 || !(reflectionFactory.getExecutableSharedParameterTypes(member)[paramCount-1].isArray()))) { return true; } + // A method handle cannot be created if its type has an arity >= 255 // as the method handle's invoke method consumes an extra argument // of the method handle itself. Fall back to use the native implementation. @@ -396,7 +400,7 @@ private static boolean useNativeAccessor(Executable member) { */ private static int slotCount(Executable member) { int slots = 0; - Class[] ptypes = member.getParameterTypes(); + Class[] ptypes = reflectionFactory.getExecutableSharedParameterTypes(member); for (Class ptype : ptypes) { if (ptype == double.class || ptype == long.class) { slots++; @@ -406,6 +410,31 @@ private static int slotCount(Executable member) { (Modifier.isStatic(member.getModifiers()) ? 0 : 1); } + /** + * Signature-polymorphic methods. Lookup has special rules for these methods, + * but core reflection must observe them as they are declared, and reflective + * invocation must invoke the native method stubs that throw UOE. + * + * @param method the method to check + * @return {@code true} if this method is signature polymorphic + * @jls 15.12.3 Compile-Time Step 3: Is the Chosen Method Appropriate? + * @jvms 2.9.3 Signature Polymorphic Methods + */ + public static boolean isSignaturePolymorphicMethod(Method method) { + // ACC_NATIVE and ACC_VARARGS + if (!method.isVarArgs() || !Modifier.isNative(method.getModifiers())) { + return false; + } + // Declared in MethodHandle or VarHandle + var declaringClass = method.getDeclaringClass(); + if (declaringClass != MethodHandle.class && declaringClass != VarHandle.class) { + return false; + } + // Single parameter of declared type Object[] + Class[] parameters = reflectionFactory.getExecutableSharedParameterTypes(method); + return parameters.length == 1 && parameters[0] == Object[].class; + } + /* * Delay initializing these static fields until java.lang.invoke is fully initialized. */ @@ -414,4 +443,5 @@ static class LazyStaticHolder { } private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); } diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/NativeMethodInvoke.java b/test/micro/org/openjdk/bench/java/lang/reflect/NativeMethodInvoke.java new file mode 100644 index 00000000000..b48cef58037 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/reflect/NativeMethodInvoke.java @@ -0,0 +1,79 @@ +/* + * 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. + */ +package org.openjdk.bench.java.lang.reflect; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +/** + * Benchmark for regression in native method invocation. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class NativeMethodInvoke { + + private Method objectHashCode; + private Method threadCurrentThread; + + private Object[] objects; + + @Setup + public void setup() throws ReflectiveOperationException { + objects = new Object[]{ + 1, 5L, + 5.6d, 23.11f, + Boolean.TRUE, 'd' + }; + + objectHashCode = Object.class.getDeclaredMethod("hashCode"); + threadCurrentThread = Thread.class.getDeclaredMethod("currentThread"); + } + + @Benchmark + public void objectHashCode(Blackhole bh) throws ReflectiveOperationException { + for (var obj : objects) { + bh.consume(objectHashCode.invoke(obj)); + } + } + + @Benchmark + public Object threadCurrentThread() throws ReflectiveOperationException { + return threadCurrentThread.invoke(null); + } +} From 3f6c04247ff6ad69330bc219ed26944852954e85 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 3 Dec 2024 00:12:50 +0000 Subject: [PATCH 059/171] 8345143: Remove uses of SecurityManager in the java.desktop module Reviewed-by: azvegint, honkar --- .../classes/com/apple/eio/FileManager.java | 33 ------------------- .../com/sun/beans/finder/ClassFinder.java | 4 +-- .../com/sun/beans/introspect/ClassInfo.java | 6 +--- .../com/sun/java/swing/plaf/gtk/GTKStyle.java | 1 - .../com/sun/media/sound/AudioSynthesizer.java | 4 --- .../com/sun/media/sound/SoftSynthesizer.java | 7 ++-- .../share/classes/java/awt/Font.java | 23 ++----------- .../share/classes/java/awt/Window.java | 17 +++------- .../share/classes/java/beans/Beans.java | 17 +--------- .../classes/java/beans/Introspector.java | 5 --- .../java/beans/PropertyEditorManager.java | 10 ------ .../javax/imageio/spi/IIORegistry.java | 19 ++--------- .../classes/javax/sound/midi/Synthesizer.java | 4 +-- .../javax/swing/DefaultListCellRenderer.java | 17 +++------- .../classes/javax/swing/FocusManager.java | 4 --- .../share/classes/javax/swing/JFrame.java | 7 ---- .../classes/javax/swing/JInternalFrame.java | 8 ++--- .../share/classes/javax/swing/JTable.java | 5 --- .../share/classes/javax/swing/Popup.java | 10 +----- .../share/classes/javax/swing/TimerQueue.java | 1 - .../share/classes/javax/swing/UIDefaults.java | 2 -- .../plaf/basic/BasicComboBoxRenderer.java | 8 +---- .../javax/swing/plaf/basic/BasicLabelUI.java | 12 ------- .../javax/swing/plaf/metal/MetalLabelUI.java | 13 -------- .../javax/swing/plaf/metal/MetalSliderUI.java | 23 +++---------- .../swing/table/DefaultTableCellRenderer.java | 7 +--- .../javax/swing/text/DefaultCaret.java | 2 -- .../javax/swing/text/DefaultFormatter.java | 2 -- .../javax/swing/text/NumberFormatter.java | 2 -- .../share/classes/sun/awt/OSInfo.java | 4 +-- .../share/classes/sun/awt/SunToolkit.java | 11 ------- .../sun/awt/util/PerformanceLogger.java | 4 +-- .../classes/sun/swing/SwingUtilities2.java | 14 -------- test/jdk/lib/client/ExtendedRobot.java | 5 --- 34 files changed, 33 insertions(+), 278 deletions(-) diff --git a/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java b/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java index 2f847416d21..7ace8d90c7c 100644 --- a/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java +++ b/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java @@ -130,11 +130,6 @@ public static int OSTypeToInt(String type) { * @since 1.4 */ public static void setFileTypeAndCreator(String filename, int type, int creator) throws IOException { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkWrite(filename); - } _setFileTypeAndCreator(filename, type, creator); } private static native void _setFileTypeAndCreator(String filename, int type, int creator) throws IOException; @@ -145,11 +140,6 @@ public static void setFileTypeAndCreator(String filename, int type, int creator) * @since 1.4 */ public static void setFileType(String filename, int type) throws IOException { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkWrite(filename); - } _setFileType(filename, type); } private static native void _setFileType(String filename, int type) throws IOException; @@ -160,11 +150,6 @@ public static void setFileType(String filename, int type) throws IOException { * @since 1.4 */ public static void setFileCreator(String filename, int creator) throws IOException { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkWrite(filename); - } _setFileCreator(filename, creator); } private static native void _setFileCreator(String filename, int creator) throws IOException; @@ -175,11 +160,6 @@ public static void setFileCreator(String filename, int creator) throws IOExcepti * @since 1.4 */ public static int getFileType(String filename) throws IOException { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(filename); - } return _getFileType(filename); } private static native int _getFileType(String filename) throws IOException; @@ -190,11 +170,6 @@ public static int getFileType(String filename) throws IOException { * @since 1.4 */ public static int getFileCreator(String filename) throws IOException { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(filename); - } return _getFileCreator(filename); } private static native int _getFileCreator(String filename) throws IOException; @@ -358,10 +333,6 @@ public static boolean moveToTrash(final File file) throws FileNotFoundException if (file == null) throw new FileNotFoundException(); final String fileName = file.getAbsolutePath(); - @SuppressWarnings("removal") - final SecurityManager security = System.getSecurityManager(); - if (security != null) security.checkDelete(fileName); - return _moveToTrash(fileName); } @@ -382,10 +353,6 @@ public static boolean revealInFinder(final File file) throws FileNotFoundExcepti if (file == null || !file.exists()) throw new FileNotFoundException(); final String fileName = file.getAbsolutePath(); - @SuppressWarnings("removal") - final SecurityManager security = System.getSecurityManager(); - if (security != null) security.checkRead(fileName); - return _revealInFinder(fileName); } diff --git a/src/java.desktop/share/classes/com/sun/beans/finder/ClassFinder.java b/src/java.desktop/share/classes/com/sun/beans/finder/ClassFinder.java index cdd022fcaf7..a8299458757 100644 --- a/src/java.desktop/share/classes/com/sun/beans/finder/ClassFinder.java +++ b/src/java.desktop/share/classes/com/sun/beans/finder/ClassFinder.java @@ -64,7 +64,7 @@ public static Class findClass(String name) throws ClassNotFoundException { return Class.forName(name, false, loader); } - } catch (ClassNotFoundException | SecurityException exception) { + } catch (ClassNotFoundException exception) { // use current class loader instead } return Class.forName(name); @@ -95,7 +95,7 @@ public static Class findClass(String name, ClassLoader loader) throws ClassNo if (loader != null) { try { return Class.forName(name, false, loader); - } catch (ClassNotFoundException | SecurityException exception) { + } catch (ClassNotFoundException exception) { // use default class loader instead } } diff --git a/src/java.desktop/share/classes/com/sun/beans/introspect/ClassInfo.java b/src/java.desktop/share/classes/com/sun/beans/introspect/ClassInfo.java index dce0469c7c9..6578011ccca 100644 --- a/src/java.desktop/share/classes/com/sun/beans/introspect/ClassInfo.java +++ b/src/java.desktop/share/classes/com/sun/beans/introspect/ClassInfo.java @@ -45,11 +45,7 @@ public static ClassInfo get(Class type) { if (type == null) { return DEFAULT; } - try { - return CACHE.get(type); - } catch (SecurityException exception) { - return DEFAULT; - } + return CACHE.get(type); } public static void clear() { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java index b9c2ce914d4..cc7a1333b64 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java @@ -27,7 +27,6 @@ import java.awt.*; import java.lang.reflect.*; -import java.security.*; import java.util.*; import javax.swing.*; import javax.swing.plaf.*; diff --git a/src/java.desktop/share/classes/com/sun/media/sound/AudioSynthesizer.java b/src/java.desktop/share/classes/com/sun/media/sound/AudioSynthesizer.java index 4f01291c22d..b0afee9871a 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/AudioSynthesizer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/AudioSynthesizer.java @@ -89,8 +89,6 @@ public interface AudioSynthesizer extends Synthesizer { * * @throws MidiUnavailableException thrown if the synthesizer cannot be * opened due to resource restrictions. - * @throws SecurityException thrown if the synthesizer cannot be - * opened due to security restrictions. * * @see #close * @see #isOpen @@ -119,8 +117,6 @@ void open(SourceDataLine line, Map info) * * @throws MidiUnavailableException thrown if the synthesizer cannot be * opened due to resource restrictions. - * @throws SecurityException thrown if the synthesizer cannot be - * opened due to security restrictions. * * @see #close * @see #isOpen diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java index 4c33416e470..6aaddb3e484 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -1111,7 +1111,7 @@ public void open(SourceDataLine line, Map info) throws MidiUnava line = testline; } else { // can throw LineUnavailableException, - // IllegalArgumentException, SecurityException + // IllegalArgumentException line = AudioSystem.getSourceDataLine(getFormat()); } } @@ -1122,7 +1122,7 @@ public void open(SourceDataLine line, Map info) throws MidiUnava int bufferSize = getFormat().getFrameSize() * (int)(getFormat().getFrameRate() * (latency/1000000f)); // can throw LineUnavailableException, - // IllegalArgumentException, SecurityException + // IllegalArgumentException line.open(getFormat(), bufferSize); // Remember that we opened that line @@ -1166,8 +1166,7 @@ public void open(SourceDataLine line, Map info) throws MidiUnava weakstream.sourceDataLine = sourceDataLine; } - } catch (final LineUnavailableException | SecurityException - | IllegalArgumentException e) { + } catch (final LineUnavailableException | IllegalArgumentException e) { if (isOpen()) { close(); } diff --git a/src/java.desktop/share/classes/java/awt/Font.java b/src/java.desktop/share/classes/java/awt/Font.java index 4de61473707..bbd6ee83e51 100644 --- a/src/java.desktop/share/classes/java/awt/Font.java +++ b/src/java.desktop/share/classes/java/awt/Font.java @@ -893,23 +893,8 @@ public static Font getFont(Map attributes) { * If a thread can create temp files anyway, no point in counting * font bytes. */ - @SuppressWarnings("removal") private static boolean hasTempPermission() { - - if (System.getSecurityManager() == null) { - return true; - } - File f = null; - boolean hasPerm = false; - try { - f = Files.createTempFile("+~JT", ".tmp").toFile(); - f.delete(); - f = null; - hasPerm = true; - } catch (Throwable t) { - /* inc. any kind of SecurityException */ - } - return hasPerm; + return true; } @@ -1757,11 +1742,7 @@ public static Font decode(String str) { * @see #decode(String) */ public static Font getFont(String nm, Font font) { - String str = null; - try { - str =System.getProperty(nm); - } catch(SecurityException e) { - } + String str = System.getProperty(nm); if (str == null) { return font; } diff --git a/src/java.desktop/share/classes/java/awt/Window.java b/src/java.desktop/share/classes/java/awt/Window.java index 9d12b0cd3c0..4e576f3dc9e 100644 --- a/src/java.desktop/share/classes/java/awt/Window.java +++ b/src/java.desktop/share/classes/java/awt/Window.java @@ -598,10 +598,7 @@ private void ownedInit(Window owner) { if (owner != null) { owner.addOwnedWindow(weakThis); if (owner.isAlwaysOnTop()) { - try { - setAlwaysOnTop(true); - } catch (SecurityException ignore) { - } + setAlwaysOnTop(true); } } @@ -1305,10 +1302,7 @@ public void toBack() { // to insure that it cannot be overridden by client subclasses. final void toBack_NoClientCode() { if(isAlwaysOnTop()) { - try { - setAlwaysOnTop(false); - }catch(SecurityException e) { - } + setAlwaysOnTop(false); } if (visible) { WindowPeer peer = (WindowPeer)this.peer; @@ -2191,10 +2185,7 @@ private void setOwnedWindowsAlwaysOnTop(boolean alwaysOnTop) { for (WeakReference ref : ownedWindowArray) { Window window = ref.get(); if (window != null) { - try { - window.setAlwaysOnTop(alwaysOnTop); - } catch (SecurityException ignore) { - } + window.setAlwaysOnTop(alwaysOnTop); } } } @@ -3032,7 +3023,7 @@ private void readObject(ObjectInputStream s) setModalExclusionType(et); // since 6.0 boolean aot = f.get("alwaysOnTop", false); if(aot) { - setAlwaysOnTop(aot); // since 1.5; subject to permission check + setAlwaysOnTop(aot); } shape = (Shape)f.get("shape", null); opacity = (Float)f.get("opacity", 1.0f); diff --git a/src/java.desktop/share/classes/java/beans/Beans.java b/src/java.desktop/share/classes/java/beans/Beans.java index e35a751a42e..5320d12ecd2 100644 --- a/src/java.desktop/share/classes/java/beans/Beans.java +++ b/src/java.desktop/share/classes/java/beans/Beans.java @@ -189,12 +189,7 @@ public static Object instantiate(ClassLoader cls, String beanName, // Note that calls on the system class loader will // look in the bootstrap class loader first. if (cls == null) { - try { - cls = ClassLoader.getSystemClassLoader(); - } catch (SecurityException ex) { - // We're not allowed to access the system class loader. - // Drop through. - } + cls = ClassLoader.getSystemClassLoader(); } // Try to find a serialized object with this name @@ -438,11 +433,6 @@ public static boolean isGuiAvailable() { */ public static void setDesignTime(boolean isDesignTime) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPropertiesAccess(); - } ThreadGroupContext.getContext().setDesignTime(isDesignTime); } @@ -454,11 +444,6 @@ public static void setDesignTime(boolean isDesignTime) { */ public static void setGuiAvailable(boolean isGuiAvailable) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPropertiesAccess(); - } ThreadGroupContext.getContext().setGuiAvailable(isGuiAvailable); } } diff --git a/src/java.desktop/share/classes/java/beans/Introspector.java b/src/java.desktop/share/classes/java/beans/Introspector.java index 7dfd2e60a8e..c526190808e 100644 --- a/src/java.desktop/share/classes/java/beans/Introspector.java +++ b/src/java.desktop/share/classes/java/beans/Introspector.java @@ -335,11 +335,6 @@ public static String[] getBeanInfoSearchPath() { */ public static void setBeanInfoSearchPath(String[] path) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPropertiesAccess(); - } ThreadGroupContext.getContext().getBeanInfoFinder().setPackages(path); } diff --git a/src/java.desktop/share/classes/java/beans/PropertyEditorManager.java b/src/java.desktop/share/classes/java/beans/PropertyEditorManager.java index d6cb76e058c..b776e3f3615 100644 --- a/src/java.desktop/share/classes/java/beans/PropertyEditorManager.java +++ b/src/java.desktop/share/classes/java/beans/PropertyEditorManager.java @@ -71,11 +71,6 @@ public PropertyEditorManager() {} * @param editorClass the class object of the editor class */ public static void registerEditor(Class targetType, Class editorClass) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPropertiesAccess(); - } ThreadGroupContext.getContext().getPropertyEditorFinder().register(targetType, editorClass); } @@ -109,11 +104,6 @@ public static String[] getEditorSearchPath() { * @param path Array of package names. */ public static void setEditorSearchPath(String[] path) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPropertiesAccess(); - } ThreadGroupContext.getContext().getPropertyEditorFinder().setPackages(path); } } diff --git a/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java b/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java index 1ea7146ae4a..09fa1eb202b 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java @@ -162,7 +162,6 @@ private void registerStandardSpis() { * @see javax.imageio.ImageIO#scanForPlugins * @see ClassLoader#getResources */ - @SuppressWarnings("removal") public void registerApplicationClasspathSpis() { // FIX: load only from application classpath @@ -175,22 +174,8 @@ public void registerApplicationClasspathSpis() { Iterator riter = ServiceLoader.load(c, loader).iterator(); while (riter.hasNext()) { - try { - // Note that the next() call is required to be inside - // the try/catch block; see 6342404. - IIOServiceProvider r = riter.next(); - registerServiceProvider(r); - } catch (ServiceConfigurationError err) { - if (System.getSecurityManager() != null) { - // In the applet case, we will catch the error so - // registration of other plugins can proceed - err.printStackTrace(); - } else { - // In the application case, we will throw the - // error to indicate app/system misconfiguration - throw err; - } - } + IIOServiceProvider r = riter.next(); + registerServiceProvider(r); } } } diff --git a/src/java.desktop/share/classes/javax/sound/midi/Synthesizer.java b/src/java.desktop/share/classes/javax/sound/midi/Synthesizer.java index c353fb7f390..055f3f1066b 100644 --- a/src/java.desktop/share/classes/javax/sound/midi/Synthesizer.java +++ b/src/java.desktop/share/classes/javax/sound/midi/Synthesizer.java @@ -345,10 +345,8 @@ public interface Synthesizer extends MidiDevice { * * @throws MidiUnavailableException if the receiver is cannot be opened, * usually because the MIDI device is in use by another application - * @throws SecurityException if the receiver cannot be opened due to - * security restrictions */ - // abstract void open() throws MidiUnavailableException, SecurityException; + // abstract void open() throws MidiUnavailableException; /** * Closes the receiver. diff --git a/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java b/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java index a0750a13bfa..628c8f6e68c 100644 --- a/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java +++ b/src/java.desktop/share/classes/javax/swing/DefaultListCellRenderer.java @@ -82,7 +82,6 @@ public class DefaultListCellRenderer extends JLabel * getListCellRendererComponent method and set the border * of the returned component directly. */ - private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); /** * No focus border @@ -100,20 +99,14 @@ public DefaultListCellRenderer() { setName("List.cellRenderer"); } - @SuppressWarnings("removal") private Border getNoFocusBorder() { Border border = DefaultLookup.getBorder(this, ui, "List.cellNoFocusBorder"); - if (System.getSecurityManager() != null) { - if (border != null) return border; - return SAFE_NO_FOCUS_BORDER; - } else { - if (border != null && - (noFocusBorder == null || - noFocusBorder == DEFAULT_NO_FOCUS_BORDER)) { - return border; - } - return noFocusBorder; + if (border != null && + (noFocusBorder == null || + noFocusBorder == DEFAULT_NO_FOCUS_BORDER)) { + return border; } + return noFocusBorder; } public Component getListCellRendererComponent( diff --git a/src/java.desktop/share/classes/javax/swing/FocusManager.java b/src/java.desktop/share/classes/javax/swing/FocusManager.java index 9aa923efafb..5e9ca85afce 100644 --- a/src/java.desktop/share/classes/javax/swing/FocusManager.java +++ b/src/java.desktop/share/classes/javax/swing/FocusManager.java @@ -99,10 +99,6 @@ public static FocusManager getCurrentManager() { * @see java.awt.DefaultKeyboardFocusManager */ public static void setCurrentManager(FocusManager aFocusManager) { - // Note: This method is not backward-compatible with 1.3 and earlier - // releases. It now throws a SecurityException in an applet, whereas - // in previous releases, it did not. This issue was discussed at - // length, and ultimately approved by Hans. KeyboardFocusManager toSet = (aFocusManager instanceof DelegatingDefaultFocusManager) ? ((DelegatingDefaultFocusManager)aFocusManager).getDelegate() diff --git a/src/java.desktop/share/classes/javax/swing/JFrame.java b/src/java.desktop/share/classes/javax/swing/JFrame.java index 1e4c9a1bf76..8bde7e18f03 100644 --- a/src/java.desktop/share/classes/javax/swing/JFrame.java +++ b/src/java.desktop/share/classes/javax/swing/JFrame.java @@ -380,13 +380,6 @@ public void setDefaultCloseOperation(int operation) { + " DISPOSE_ON_CLOSE, or EXIT_ON_CLOSE"); } - if (operation == EXIT_ON_CLOSE) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkExit(0); - } - } if (this.defaultCloseOperation != operation) { int oldValue = this.defaultCloseOperation; this.defaultCloseOperation = operation; diff --git a/src/java.desktop/share/classes/javax/swing/JInternalFrame.java b/src/java.desktop/share/classes/javax/swing/JInternalFrame.java index e7d0eec76fb..2f4ab1dcc1d 100644 --- a/src/java.desktop/share/classes/javax/swing/JInternalFrame.java +++ b/src/java.desktop/share/classes/javax/swing/JInternalFrame.java @@ -1781,12 +1781,8 @@ public void dispose() { isClosed = true; } fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); - try { - java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( - new sun.awt.UngrabEvent(this)); - } catch (SecurityException e) { - this.dispatchEvent(new sun.awt.UngrabEvent(this)); - } + java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( + new sun.awt.UngrabEvent(this)); } /** diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java index 0ff490d45ea..77d787172cf 100644 --- a/src/java.desktop/share/classes/javax/swing/JTable.java +++ b/src/java.desktop/share/classes/javax/swing/JTable.java @@ -5560,7 +5560,6 @@ public boolean stopCellEditing() { return super.stopCellEditing(); } - SwingUtilities2.checkAccess(constructor.getModifiers()); value = constructor.newInstance(new Object[]{s}); } catch (Exception e) { @@ -5584,7 +5583,6 @@ public Component getTableCellEditorComponent(JTable table, Object value, if (type == Object.class) { type = String.class; } - SwingUtilities2.checkAccess(type.getModifiers()); constructor = type.getConstructor(argTypes); } catch (Exception e) { @@ -6366,9 +6364,6 @@ public boolean print(PrintMode printMode, } } - // Get a PrinterJob. - // Do this before anything with side-effects since it may throw a - // security exception - in which case we don't want to do anything else. final PrinterJob job = PrinterJob.getPrinterJob(); if (isEditing()) { diff --git a/src/java.desktop/share/classes/javax/swing/Popup.java b/src/java.desktop/share/classes/javax/swing/Popup.java index acc69f82fe3..3d4329915bb 100644 --- a/src/java.desktop/share/classes/javax/swing/Popup.java +++ b/src/java.desktop/share/classes/javax/swing/Popup.java @@ -242,15 +242,7 @@ static class HeavyWeightWindow extends JWindow implements ModalExclude { // Popups are typically transient and most likely won't benefit // from true double buffering. Turn it off here. getRootPane().setUseTrueDoubleBuffering(false); - // Try to set "always-on-top" for the popup window. - // Applets usually don't have sufficient permissions to do it. - // In this case simply ignore the exception. - try { - setAlwaysOnTop(true); - } catch (SecurityException se) { - // setAlwaysOnTop is restricted, - // the exception is ignored - } + setAlwaysOnTop(true); } public void update(Graphics g) { diff --git a/src/java.desktop/share/classes/javax/swing/TimerQueue.java b/src/java.desktop/share/classes/javax/swing/TimerQueue.java index 249593caa7e..44dc0c6e6af 100644 --- a/src/java.desktop/share/classes/javax/swing/TimerQueue.java +++ b/src/java.desktop/share/classes/javax/swing/TimerQueue.java @@ -179,7 +179,6 @@ public void run() { // Allow run other threads on systems without kernel threads timer.getLock().newCondition().awaitNanos(1); - } catch (SecurityException ignore) { } finally { timer.getLock().unlock(); } diff --git a/src/java.desktop/share/classes/javax/swing/UIDefaults.java b/src/java.desktop/share/classes/javax/swing/UIDefaults.java index 53eb870d3e6..13bd6e723b4 100644 --- a/src/java.desktop/share/classes/javax/swing/UIDefaults.java +++ b/src/java.desktop/share/classes/javax/swing/UIDefaults.java @@ -1141,7 +1141,6 @@ public Object createValue(final UIDefaults table) { } } c = Class.forName(className, true, (ClassLoader)cl); - SwingUtilities2.checkAccess(c.getModifiers()); if (methodName != null) { Class[] types = getClassArray(args); Method m = c.getMethod(methodName, types); @@ -1149,7 +1148,6 @@ public Object createValue(final UIDefaults table) { } else { Class[] types = getClassArray(args); Constructor constructor = c.getConstructor(types); - SwingUtilities2.checkAccess(constructor.getModifiers()); return constructor.newInstance(args); } } catch(Exception e) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxRenderer.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxRenderer.java index 06539e15ff1..a4c95f02c86 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxRenderer.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxRenderer.java @@ -57,7 +57,6 @@ public class BasicComboBoxRenderer extends JLabel * the setBorder method. */ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); - private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); /** * Constructs a new instance of {@code BasicComboBoxRenderer}. @@ -68,13 +67,8 @@ public BasicComboBoxRenderer() { setBorder(getNoFocusBorder()); } - @SuppressWarnings("removal") private static Border getNoFocusBorder() { - if (System.getSecurityManager() != null) { - return SAFE_NO_FOCUS_BORDER; - } else { - return noFocusBorder; - } + return noFocusBorder; } public Dimension getPreferredSize() { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java index 68847cb1ebb..fab31c78d74 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java @@ -66,7 +66,6 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener * name in defaults table under the key "LabelUI". */ protected static BasicLabelUI labelUI = new BasicLabelUI(); - private static final Object BASIC_LABEL_UI_KEY = new Object(); private Rectangle paintIconR = new Rectangle(); private Rectangle paintTextR = new Rectangle(); @@ -466,18 +465,7 @@ protected void uninstallKeyboardActions(JLabel c) { * @param c a component * @return an instance of {@code BasicLabelUI} */ - @SuppressWarnings("removal") public static ComponentUI createUI(JComponent c) { - if (System.getSecurityManager() != null) { - AppContext appContext = AppContext.getAppContext(); - BasicLabelUI safeBasicLabelUI = - (BasicLabelUI) appContext.get(BASIC_LABEL_UI_KEY); - if (safeBasicLabelUI == null) { - safeBasicLabelUI = new BasicLabelUI(); - appContext.put(BASIC_LABEL_UI_KEY, safeBasicLabelUI); - } - return safeBasicLabelUI; - } return labelUI; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLabelUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLabelUI.java index 3578f2fc210..d3b7b4a836c 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLabelUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLabelUI.java @@ -54,8 +54,6 @@ public class MetalLabelUI extends BasicLabelUI */ protected static MetalLabelUI metalLabelUI = new MetalLabelUI(); - private static final Object METAL_LABEL_UI_KEY = new Object(); - /** * Constructs a {@code MetalLabelUI}. */ @@ -67,18 +65,7 @@ public MetalLabelUI() {} * @param c a component * @return an instance of {@code MetalLabelUI} */ - @SuppressWarnings("removal") public static ComponentUI createUI(JComponent c) { - if (System.getSecurityManager() != null) { - AppContext appContext = AppContext.getAppContext(); - MetalLabelUI safeMetalLabelUI = - (MetalLabelUI) appContext.get(METAL_LABEL_UI_KEY); - if (safeMetalLabelUI == null) { - safeMetalLabelUI = new MetalLabelUI(); - appContext.put(METAL_LABEL_UI_KEY, safeMetalLabelUI); - } - return safeMetalLabelUI; - } return metalLabelUI; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalSliderUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalSliderUI.java index c31ee81c729..bdded64b788 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalSliderUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalSliderUI.java @@ -105,9 +105,6 @@ public class MetalSliderUI extends BasicSliderUI { */ protected static Icon vertThumbIcon; - private static Icon SAFE_HORIZ_THUMB_ICON; - private static Icon SAFE_VERT_THUMB_ICON; - /** * Property for {@code JSlider.isFilled}. */ @@ -130,31 +127,19 @@ public MetalSliderUI() { super( null ); } - @SuppressWarnings("removal") private static Icon getHorizThumbIcon() { - if (System.getSecurityManager() != null) { - return SAFE_HORIZ_THUMB_ICON; - } else { - return horizThumbIcon; - } + return horizThumbIcon; } - @SuppressWarnings("removal") private static Icon getVertThumbIcon() { - if (System.getSecurityManager() != null) { - return SAFE_VERT_THUMB_ICON; - } else { - return vertThumbIcon; - } + return vertThumbIcon; } public void installUI( JComponent c ) { trackWidth = ((Integer)UIManager.get( "Slider.trackWidth" )).intValue(); tickLength = safeLength = ((Integer)UIManager.get( "Slider.majorTickLength" )).intValue(); - horizThumbIcon = SAFE_HORIZ_THUMB_ICON = - UIManager.getIcon( "Slider.horizontalThumbIcon" ); - vertThumbIcon = SAFE_VERT_THUMB_ICON = - UIManager.getIcon( "Slider.verticalThumbIcon" ); + horizThumbIcon = UIManager.getIcon( "Slider.horizontalThumbIcon" ); + vertThumbIcon = UIManager.getIcon( "Slider.verticalThumbIcon" ); super.installUI( c ); diff --git a/src/java.desktop/share/classes/javax/swing/table/DefaultTableCellRenderer.java b/src/java.desktop/share/classes/javax/swing/table/DefaultTableCellRenderer.java index 28e324166b5..8b4d7bec2c0 100644 --- a/src/java.desktop/share/classes/javax/swing/table/DefaultTableCellRenderer.java +++ b/src/java.desktop/share/classes/javax/swing/table/DefaultTableCellRenderer.java @@ -93,7 +93,6 @@ public class DefaultTableCellRenderer extends JLabel * getTableCellRendererComponent method and set the border * of the returned component directly. */ - private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); /** * A border without focus. @@ -117,13 +116,9 @@ public DefaultTableCellRenderer() { setName("Table.cellRenderer"); } - @SuppressWarnings("removal") private Border getNoFocusBorder() { Border border = DefaultLookup.getBorder(this, ui, "Table.cellNoFocusBorder"); - if (System.getSecurityManager() != null) { - if (border != null) return border; - return SAFE_NO_FOCUS_BORDER; - } else if (border != null) { + if (border != null) { if (noFocusBorder == null || noFocusBorder == DEFAULT_NO_FOCUS_BORDER) { return border; } diff --git a/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java b/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java index bed18fc3fc3..81edfd1f504 100644 --- a/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java +++ b/src/java.desktop/share/classes/javax/swing/text/DefaultCaret.java @@ -1431,8 +1431,6 @@ private Clipboard getSystemSelection() { return component.getToolkit().getSystemSelection(); } catch (HeadlessException he) { // do nothing... there is no system clipboard - } catch (SecurityException se) { - // do nothing... there is no allowed system clipboard } return null; } diff --git a/src/java.desktop/share/classes/javax/swing/text/DefaultFormatter.java b/src/java.desktop/share/classes/javax/swing/text/DefaultFormatter.java index 38ea1cda125..a0f6a963926 100644 --- a/src/java.desktop/share/classes/javax/swing/text/DefaultFormatter.java +++ b/src/java.desktop/share/classes/javax/swing/text/DefaultFormatter.java @@ -247,7 +247,6 @@ public Object stringToValue(String string) throws ParseException { Constructor cons; try { - SwingUtilities2.checkAccess(vc.getModifiers()); cons = vc.getConstructor(new Class[]{String.class}); } catch (NoSuchMethodException nsme) { @@ -256,7 +255,6 @@ public Object stringToValue(String string) throws ParseException { if (cons != null) { try { - SwingUtilities2.checkAccess(cons.getModifiers()); return cons.newInstance(new Object[] { string }); } catch (Throwable ex) { throw new ParseException("Error creating instance", 0); diff --git a/src/java.desktop/share/classes/javax/swing/text/NumberFormatter.java b/src/java.desktop/share/classes/javax/swing/text/NumberFormatter.java index f3e9a8d2a58..8abc78e5ade 100644 --- a/src/java.desktop/share/classes/javax/swing/text/NumberFormatter.java +++ b/src/java.desktop/share/classes/javax/swing/text/NumberFormatter.java @@ -436,11 +436,9 @@ private Object toggleSign(boolean positive) throws ParseException { valueClass = value.getClass(); } try { - SwingUtilities2.checkAccess(valueClass.getModifiers()); Constructor cons = valueClass.getConstructor( new Class[] { String.class }); if (cons != null) { - SwingUtilities2.checkAccess(cons.getModifiers()); return cons.newInstance(new Object[]{string}); } } catch (Throwable ex) { } diff --git a/src/java.desktop/share/classes/sun/awt/OSInfo.java b/src/java.desktop/share/classes/sun/awt/OSInfo.java index 509bac362f2..b278032c45f 100644 --- a/src/java.desktop/share/classes/sun/awt/OSInfo.java +++ b/src/java.desktop/share/classes/sun/awt/OSInfo.java @@ -64,7 +64,7 @@ and so the method getWindowsVersion() will return the constant for known OS. private static final Map windowsVersionMap = new HashMap(); // Cache the OSType for getOSType() - private static final OSType CURRENT_OSTYPE = getOSTypeImpl(); // No DoPriv needed + private static final OSType CURRENT_OSTYPE = getOSTypeImpl(); static { @@ -101,7 +101,7 @@ private static OSType getOSTypeImpl() { }; } - public static WindowsVersion getWindowsVersion() throws SecurityException { + public static WindowsVersion getWindowsVersion() { String osVersion = System.getProperty(OS_VERSION); if (osVersion == null) { diff --git a/src/java.desktop/share/classes/sun/awt/SunToolkit.java b/src/java.desktop/share/classes/sun/awt/SunToolkit.java index b630148f809..9bf805a5808 100644 --- a/src/java.desktop/share/classes/sun/awt/SunToolkit.java +++ b/src/java.desktop/share/classes/sun/awt/SunToolkit.java @@ -701,7 +701,6 @@ static Image getImageFromHash(Toolkit tk, URL url) { static Image getImageFromHash(Toolkit tk, String filename) { - checkPermissions(filename); synchronized (fileImgCache) { Image img = (Image)fileImgCache.get(filename); if (img == null) { @@ -757,7 +756,6 @@ protected Image getImageWithResolutionVariant(URL url, @Override public Image createImage(String filename) { - checkPermissions(filename); return createImage(new FileImageSource(filename)); } @@ -870,7 +868,6 @@ protected static boolean imageCached(URL url) { protected static boolean imageExists(String filename) { if (filename != null) { - checkPermissions(filename); return new File(filename).exists(); } return false; @@ -888,14 +885,6 @@ protected static boolean imageExists(URL url) { return false; } - private static void checkPermissions(String filename) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(filename); - } - } - /** * Scans {@code imageList} for best-looking image of specified dimensions. * Image can be scaled and/or padded with transparency. diff --git a/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java b/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java index 303b57626d6..c942ec4e8f8 100644 --- a/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java +++ b/src/java.desktop/share/classes/sun/awt/util/PerformanceLogger.java @@ -126,9 +126,7 @@ public class PerformanceLogger { /** * Returns status of whether logging is enabled or not. This is - * provided as a convenience method so that users do not have to - * perform the same GetPropertyAction check as above to determine whether - * to enable performance logging. + * provided as a convenience method. */ public static boolean loggingEnabled() { return perfLoggingOn; diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index 181d2d5d33b..20a07c608a0 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -1462,20 +1462,6 @@ public static boolean canAccessSystemClipboard() { return !GraphicsEnvironment.isHeadless(); } - /** - * Utility method that throws SecurityException if SecurityManager is set - * and modifiers are not public - * - * @param modifiers a set of modifiers - */ - @SuppressWarnings("removal") - public static void checkAccess(int modifiers) { - if (System.getSecurityManager() != null - && !Modifier.isPublic(modifiers)) { - throw new SecurityException("Resource is not accessible"); - } - } - public static String displayPropertiesToCSS(Font font, Color fg) { StringBuilder rule = new StringBuilder("body {"); if (font != null) { diff --git a/test/jdk/lib/client/ExtendedRobot.java b/test/jdk/lib/client/ExtendedRobot.java index 3f77da88750..1bcf8e18dfa 100644 --- a/test/jdk/lib/client/ExtendedRobot.java +++ b/test/jdk/lib/client/ExtendedRobot.java @@ -57,11 +57,6 @@ public class ExtendedRobot extends Robot { private final int syncDelay = DEFAULT_SYNC_DELAY; - //TODO: uncomment three lines below after moving functionality to java.awt.Robot - //{ - // syncDelay = AccessController.doPrivileged(new GetIntegerAction("java.awt.robotdelay", DEFAULT_SYNC_DELAY)); - //} - /** * Constructs an ExtendedRobot object in the coordinate system of the primary screen. * From d88c7b365afec04c4d1fa089e088c9bbd76c596d Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 3 Dec 2024 02:31:26 +0000 Subject: [PATCH 060/171] 8345279: Mistake in javadoc of javax.sql.rowset.BaseRowSet#setBigDecimal Reviewed-by: darcy, lancea, iris --- .../share/classes/javax/sql/rowset/BaseRowSet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.sql.rowset/share/classes/javax/sql/rowset/BaseRowSet.java b/src/java.sql.rowset/share/classes/javax/sql/rowset/BaseRowSet.java index 20a76e38b2e..9ec28f926ec 100644 --- a/src/java.sql.rowset/share/classes/javax/sql/rowset/BaseRowSet.java +++ b/src/java.sql.rowset/share/classes/javax/sql/rowset/BaseRowSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -1892,7 +1892,7 @@ public void setDouble(int parameterIndex, double x) throws SQLException { /** * Sets the designated parameter to the given - * java.lang.BigDecimal value. The driver converts this to + * java.math.BigDecimal value. The driver converts this to * an SQL NUMERIC value when it sends it to the database. *

* The parameter value set by this method is stored internally and From 325366ee1d72377c04344aa77f51f7c6d78b65d7 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 3 Dec 2024 02:32:17 +0000 Subject: [PATCH 061/171] 8345141: Remove uses of SecurityManager in ShellFolder related classes Reviewed-by: azvegint, honkar --- .../classes/sun/awt/shell/ShellFolder.java | 2 +- .../sun/awt/shell/ShellFolderManager.java | 42 +--------- .../share/classes/sun/swing/FilePane.java | 28 +++---- .../sun/awt/shell/Win32ShellFolder2.java | 14 +--- .../awt/shell/Win32ShellFolderManager2.java | 77 ++----------------- 5 files changed, 25 insertions(+), 138 deletions(-) diff --git a/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java b/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java index ee0f32d91c0..af1b7c74b59 100644 --- a/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java +++ b/src/java.desktop/share/classes/sun/awt/shell/ShellFolder.java @@ -232,7 +232,7 @@ public Image getIcon(int width, int height) { managerClass = null; } // swallow the exceptions below and use default shell folder - } catch (ClassNotFoundException | SecurityException | NullPointerException e) { + } catch (ClassNotFoundException | NullPointerException e) { } if (managerClass == null) { diff --git a/src/java.desktop/share/classes/sun/awt/shell/ShellFolderManager.java b/src/java.desktop/share/classes/sun/awt/shell/ShellFolderManager.java index 868a5bb3910..10d0b539285 100644 --- a/src/java.desktop/share/classes/sun/awt/shell/ShellFolderManager.java +++ b/src/java.desktop/share/classes/sun/awt/shell/ShellFolderManager.java @@ -28,8 +28,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.util.concurrent.Callable; -import java.util.stream.Stream; - /** * @author Michael Martak @@ -70,13 +68,13 @@ public Object get(String key) { // Return the default shellfolder for a new filechooser File homeDir = new File(System.getProperty("user.home")); try { - return checkFile(createShellFolder(homeDir)); + return createShellFolder(homeDir); } catch (FileNotFoundException e) { - return checkFile(homeDir); + return homeDir; } } else if (key.equals("roots")) { // The root(s) of the displayable hierarchy - return checkFiles(File.listRoots()); + return File.listRoots(); } else if (key.equals("fileChooserComboBoxFolders")) { // Return an array of ShellFolders representing the list to // show by default in the file chooser's combobox @@ -86,44 +84,12 @@ public Object get(String key) { // folders, such as Desktop, Documents, History, Network, Home, etc. // This is used in the shortcut panel of the filechooser on Windows 2000 // and Windows Me - return checkFiles(new File[] { (File)get("fileChooserDefaultFolder") }); + return new File[] { (File)get("fileChooserDefaultFolder") }; } return null; } - private static File checkFile(File f) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - return (sm == null || f == null) ? f : checkFile(f, sm); - } - - private static File checkFile(File f, @SuppressWarnings("removal") SecurityManager sm) { - try { - sm.checkRead(f.getPath()); - if (f instanceof ShellFolder) { - ShellFolder sf = (ShellFolder)f; - if (sf.isLink()) { - sm.checkRead(sf.getLinkLocation().getPath()); - } - } - return f; - } catch (SecurityException | FileNotFoundException e) { - return null; - } - } - - private static File[] checkFiles(File[] fs) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - return (sm == null || fs == null) ? fs : checkFiles(Stream.of(fs), sm); - } - - private static File[] checkFiles(Stream fs, @SuppressWarnings("removal") SecurityManager sm) { - return fs.filter(f -> f != null && checkFile(f, sm) != null) - .toArray(File[]::new); - } - /** * Does {@code dir} represent a "computer" such as a node on the network, or * "My Computer" on the desktop. diff --git a/src/java.desktop/share/classes/sun/swing/FilePane.java b/src/java.desktop/share/classes/sun/swing/FilePane.java index 395cf058b7d..052e7a96ac4 100644 --- a/src/java.desktop/share/classes/sun/swing/FilePane.java +++ b/src/java.desktop/share/classes/sun/swing/FilePane.java @@ -2119,24 +2119,20 @@ public boolean canWrite(File f) { return false; } - try { - if (f instanceof ShellFolder) { - return f.canWrite(); - } else { - if (usesShellFolder(getFileChooser())) { - try { - return ShellFolder.getShellFolder(f).canWrite(); - } catch (FileNotFoundException ex) { - // File doesn't exist - return false; - } - } else { - // Ordinary file - return f.canWrite(); + if (f instanceof ShellFolder) { + return f.canWrite(); + } else { + if (usesShellFolder(getFileChooser())) { + try { + return ShellFolder.getShellFolder(f).canWrite(); + } catch (FileNotFoundException ex) { + // File doesn't exist + return false; } + } else { + // Ordinary file + return f.canWrite(); } - } catch (SecurityException e) { - return false; } } diff --git a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java index 24e2bb45df8..0a4fb12cda6 100644 --- a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java +++ b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java @@ -666,13 +666,6 @@ public String call() throws IOException { return getFileSystemPath0(csidl); } }, IOException.class); - if (path != null) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(path); - } - } return path; } @@ -750,11 +743,6 @@ private native long getEnumObjects(long pIShellFolder, boolean isDesktop, * {@code null} if this shellfolder does not denote a directory. */ public File[] listFiles(final boolean includeHiddenFiles) { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(getPath()); - } try { File[] files = invoke(new Callable() { @@ -813,7 +801,7 @@ && pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) { } }, InterruptedException.class); - return Win32ShellFolderManager2.checkFiles(files); + return files; } catch (InterruptedException e) { return new File[0]; } diff --git a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java index 810b25b55fd..53653f07f34 100644 --- a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java +++ b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java @@ -42,7 +42,6 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; import sun.awt.OSInfo; import sun.awt.util.ThreadGroupUtils; @@ -167,9 +166,6 @@ static Win32ShellFolder2 getDesktop() { if (desktop == null) { try { desktop = new Win32ShellFolder2(DESKTOP); - } catch (final SecurityException ignored) { - // Ignore, the message may have sensitive information, not - // accessible other ways } catch (IOException | InterruptedException e) { if (log.isLoggable(PlatformLogger.Level.WARNING)) { log.warning("Cannot access 'Desktop'", e); @@ -183,9 +179,6 @@ static Win32ShellFolder2 getDrives() { if (drives == null) { try { drives = new Win32ShellFolder2(DRIVES); - } catch (final SecurityException ignored) { - // Ignore, the message may have sensitive information, not - // accessible other ways } catch (IOException | InterruptedException e) { if (log.isLoggable(PlatformLogger.Level.WARNING)) { log.warning("Cannot access 'Drives'", e); @@ -202,9 +195,6 @@ static Win32ShellFolder2 getRecent() { if (path != null) { recent = createShellFolder(getDesktop(), new File(path)); } - } catch (final SecurityException ignored) { - // Ignore, the message may have sensitive information, not - // accessible other ways } catch (InterruptedException | IOException e) { if (log.isLoggable(PlatformLogger.Level.WARNING)) { log.warning("Cannot access 'Recent'", e); @@ -218,9 +208,6 @@ static Win32ShellFolder2 getNetwork() { if (network == null) { try { network = new Win32ShellFolder2(NETWORK); - } catch (final SecurityException ignored) { - // Ignore, the message may have sensitive information, not - // accessible other ways } catch (IOException | InterruptedException e) { if (log.isLoggable(PlatformLogger.Level.WARNING)) { log.warning("Cannot access 'Network'", e); @@ -244,9 +231,6 @@ static Win32ShellFolder2 getPersonal() { personal.setIsPersonal(); } } - } catch (final SecurityException ignored) { - // Ignore, the message may have sensitive information, not - // accessible other ways } catch (InterruptedException | IOException e) { if (log.isLoggable(PlatformLogger.Level.WARNING)) { log.warning("Cannot access 'Personal'", e); @@ -287,7 +271,7 @@ public Object get(String key) { if (file == null) { file = getDesktop(); } - return checkFile(file); + return file; } else if (key.equals("roots")) { // Should be "History" and "Desktop" ? if (roots == null) { @@ -298,11 +282,11 @@ public Object get(String key) { roots = (File[])super.get(key); } } - return checkFiles(roots); + return roots; } else if (key.equals("fileChooserComboBoxFolders")) { Win32ShellFolder2 desktop = getDesktop(); - if (desktop != null && checkFile(desktop) != null) { + if (desktop != null) { ArrayList folders = new ArrayList(); Win32ShellFolder2 drives = getDrives(); @@ -313,7 +297,7 @@ public Object get(String key) { folders.add(desktop); // Add all second level folders - File[] secondLevelFolders = checkFiles(desktop.listFiles()); + File[] secondLevelFolders = desktop.listFiles(); Arrays.sort(secondLevelFolders); for (File secondLevelFolder : secondLevelFolders) { Win32ShellFolder2 folder = (Win32ShellFolder2) secondLevelFolder; @@ -321,7 +305,7 @@ public Object get(String key) { folders.add(folder); // Add third level for "My Computer" if (folder.equals(drives)) { - File[] thirdLevelFolders = checkFiles(folder.listFiles()); + File[] thirdLevelFolders = folder.listFiles(); if (thirdLevelFolders != null && thirdLevelFolders.length > 0) { List thirdLevelFoldersList = Arrays.asList(thirdLevelFolders); @@ -331,7 +315,7 @@ public Object get(String key) { } } } - return checkFiles(folders); + return folders.toArray(new File[folders.size()]); } else { return super.get(key); } @@ -374,7 +358,7 @@ public Object get(String key) { } } } - return checkFiles(folders); + return folders.toArray(new File[folders.size()]); } else if (key.startsWith("fileChooserIcon ")) { String name = key.substring(key.indexOf(" ") + 1); @@ -421,53 +405,6 @@ public Object get(String key) { return null; } - private static File checkFile(File file) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - return (sm == null || file == null) ? file : checkFile(file, sm); - } - - private static File checkFile(File file, @SuppressWarnings("removal") SecurityManager sm) { - try { - sm.checkRead(file.getPath()); - - if (file instanceof Win32ShellFolder2) { - Win32ShellFolder2 f = (Win32ShellFolder2)file; - if (f.isLink()) { - Win32ShellFolder2 link = (Win32ShellFolder2)f.getLinkLocation(); - if (link != null) - sm.checkRead(link.getPath()); - } - } - return file; - } catch (SecurityException se) { - return null; - } - } - - static File[] checkFiles(File[] files) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm == null || files == null || files.length == 0) { - return files; - } - return checkFiles(Arrays.stream(files), sm); - } - - private static File[] checkFiles(List files) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm == null || files.isEmpty()) { - return files.toArray(new File[files.size()]); - } - return checkFiles(files.stream(), sm); - } - - private static File[] checkFiles(Stream filesStream, @SuppressWarnings("removal") SecurityManager sm) { - return filesStream.filter((file) -> checkFile(file, sm) != null) - .toArray(File[]::new); - } - /** * Does {@code dir} represent a "computer" such as a node on the network, or * "My Computer" on the desktop. From 24983dd4c107f11032969e3c079fd0ee07098583 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 3 Dec 2024 02:34:20 +0000 Subject: [PATCH 062/171] 7038838: Unspecified NPE in java.net.IDN methods Reviewed-by: liach, dfuchs --- src/java.base/share/classes/java/net/IDN.java | 3 ++ test/jdk/java/net/IDNTest.java | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/jdk/java/net/IDNTest.java diff --git a/src/java.base/share/classes/java/net/IDN.java b/src/java.base/share/classes/java/net/IDN.java index 6f11643264f..0aaac840766 100644 --- a/src/java.base/share/classes/java/net/IDN.java +++ b/src/java.base/share/classes/java/net/IDN.java @@ -66,6 +66,9 @@ * Applications are responsible for taking adequate security measures when using * international domain names. * + *

Unless otherwise specified, passing a {@code null} argument to any method + * in this class will cause a {@link NullPointerException} to be thrown. + * * @spec https://www.rfc-editor.org/info/rfc1122 * RFC 1122: Requirements for Internet Hosts - Communication Layers * @spec https://www.rfc-editor.org/info/rfc1123 diff --git a/test/jdk/java/net/IDNTest.java b/test/jdk/java/net/IDNTest.java new file mode 100644 index 00000000000..aa58e472bf3 --- /dev/null +++ b/test/jdk/java/net/IDNTest.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import java.net.IDN; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 7038838 + * @summary verify the behaviour of the methods on java.net.IDN class + * @run junit IDNTest + */ +public class IDNTest { + + /* + * Verify that various methods on the IDN class throw a NullPointerException + * for any null parameter. + */ + @Test + public void testNullPointerException() throws Exception { + assertThrows(NullPointerException.class, () -> IDN.toASCII(null)); + assertThrows(NullPointerException.class, () -> IDN.toASCII(null, 0)); + assertThrows(NullPointerException.class, () -> IDN.toUnicode(null)); + assertThrows(NullPointerException.class, () -> IDN.toUnicode(null, 0)); + } +} \ No newline at end of file From 40ae4699622cca72830acd146b7b5c4efd5a43ec Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 3 Dec 2024 02:36:14 +0000 Subject: [PATCH 063/171] 8235786: Javadoc for com/sun/net/httpserver/HttpExchange.java#setAttribute is unclear Reviewed-by: dfuchs, michaelm --- .../com/sun/net/httpserver/HttpExchange.java | 19 ++- .../net/httpserver/ExchangeAttributeTest.java | 130 +++++++++++++++--- 2 files changed, 126 insertions(+), 23 deletions(-) diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java index ea2845f56f5..ebcbaa3a773 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -233,22 +233,29 @@ protected HttpExchange() { public abstract String getProtocol(); /** - * {@link Filter} modules may store arbitrary objects with {@code HttpExchange} - * instances as an out-of-band communication mechanism. Other filters + * Returns the attribute's value from this exchange's + * {@linkplain HttpContext#getAttributes() context attributes}. + * + * @apiNote {@link Filter} modules may store arbitrary objects as attributes through + * {@code HttpExchange} instances as an out-of-band communication mechanism. Other filters * or the exchange handler may then access these objects. * *

Each {@code Filter} class will document the attributes which they make * available. * * @param name the name of the attribute to retrieve - * @return the attribute object, or {@code null} if it does not exist + * @return the attribute's value or {@code null} if either the attribute isn't set + * or the attribute value is {@code null} * @throws NullPointerException if name is {@code null} */ public abstract Object getAttribute(String name); /** - * {@link Filter} modules may store arbitrary objects with {@code HttpExchange} - * instances as an out-of-band communication mechanism. Other filters + * Sets an attribute with the given {@code name} and {@code value} in this exchange's + * {@linkplain HttpContext#getAttributes() context attributes}. + * + * @apiNote {@link Filter} modules may store arbitrary objects as attributes through + * {@code HttpExchange} instances as an out-of-band communication mechanism. Other filters * or the exchange handler may then access these objects. * *

Each {@code Filter} class will document the attributes which they make diff --git a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java index 2ce3dfd016d..84c7837e0b3 100644 --- a/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java +++ b/test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -23,16 +23,19 @@ /* * @test - * @bug 8288109 + * @bug 8288109 8235786 * @summary Tests for HttpExchange set/getAttribute * @library /test/lib * @run junit/othervm ExchangeAttributeTest */ +import com.sun.net.httpserver.Filter; +import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -53,44 +56,87 @@ public class ExchangeAttributeTest { - static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress(); - static final boolean ENABLE_LOGGING = true; - static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + private static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress(); + private static final boolean ENABLE_LOGGING = true; + private static final Logger logger = Logger.getLogger("com.sun.net.httpserver"); + + private static HttpServer server; @BeforeAll - public static void setup() { + public static void setup() throws Exception { if (ENABLE_LOGGING) { ConsoleHandler ch = new ConsoleHandler(); logger.setLevel(Level.ALL); ch.setLevel(Level.ALL); logger.addHandler(ch); } + server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10); + server.createContext("/normal", new AttribHandler()); + final HttpContext filteredCtx = server.createContext("/filtered", new AttribHandler()); + filteredCtx.getFilters().add(new AttributeAddingFilter()); + server.start(); + System.out.println("Server started at " + server.getAddress()); + } + + @AfterAll + public static void afterAll() { + if (server != null) { + System.out.println("Stopping server " + server.getAddress()); + server.stop(0); + } } + /* + * Verifies that HttpExchange.setAttribute() allows for null value. + */ @Test - public void testExchangeAttributes() throws Exception { - var handler = new AttribHandler(); - var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10); - server.createContext("/", handler); - server.start(); - try { - var client = HttpClient.newBuilder().proxy(NO_PROXY).build(); - var request = HttpRequest.newBuilder(uri(server, "")).build(); + public void testNullAttributeValue() throws Exception { + try (var client = HttpClient.newBuilder().proxy(NO_PROXY).build()) { + var request = HttpRequest.newBuilder(uri(server, "/normal", null)).build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); - } finally { - server.stop(0); + } + } + + /* + * Verifies that an attribute set on one exchange is accessible to another exchange that + * belongs to the same HttpContext. + */ + @Test + public void testSharedAttribute() throws Exception { + try (var client = HttpClient.newBuilder().proxy(NO_PROXY).build()) { + final var firstReq = HttpRequest.newBuilder(uri(server, "/filtered", "firstreq")) + .build(); + System.out.println("issuing request " + firstReq); + final var firstResp = client.send(firstReq, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, firstResp.statusCode()); + + // issue the second request + final var secondReq = HttpRequest.newBuilder(uri(server, "/filtered", "secondreq")) + .build(); + System.out.println("issuing request " + secondReq); + final var secondResp = client.send(secondReq, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, secondResp.statusCode()); + + // verify that the filter was invoked for both the requests. the filter internally + // does the setAttribute() and getAttribute() and asserts that the attribute value + // set by the first exchange was available through the second exchange. + assertTrue(AttributeAddingFilter.filteredFirstReq, "Filter wasn't invoked for " + + firstReq.uri()); + assertTrue(AttributeAddingFilter.filteredSecondReq, "Filter wasn't invoked for " + + secondReq.uri()); } } // --- infra --- - static URI uri(HttpServer server, String path) throws URISyntaxException { + static URI uri(HttpServer server, String path, String query) throws URISyntaxException { return URIBuilder.newBuilder() .scheme("http") .loopback() .port(server.getAddress().getPort()) .path(path) + .query(query) .build(); } @@ -112,4 +158,54 @@ public void handle(HttpExchange exchange) throws IOException { } } } + + private static final class AttributeAddingFilter extends Filter { + + private static final String ATTR_NAME ="foo-bar"; + private static final String ATTR_VAL ="hello-world"; + private static volatile boolean filteredFirstReq; + private static volatile boolean filteredSecondReq; + + @Override + public void doFilter(final HttpExchange exchange, final Chain chain) throws IOException { + if (exchange.getRequestURI().getQuery().contains("firstreq")) { + filteredFirstReq = true; + // add a request attribute through the exchange, for this first request + // and at the same time verify that the attribute doesn't already exist + final Object attrVal = exchange.getAttribute(ATTR_NAME); + if (attrVal != null) { + throw new IOException("attribute " + ATTR_NAME + " with value: " + attrVal + + " unexpectedly present in exchange: " + exchange.getRequestURI()); + } + // set the value + exchange.setAttribute(ATTR_NAME, ATTR_VAL); + System.out.println(exchange.getRequestURI() + " set attribute " + + ATTR_NAME + "=" + ATTR_VAL); + } else if (exchange.getRequestURI().getQuery().contains("secondreq")) { + filteredSecondReq = true; + // verify the attribute is already set and the value is the expected one. + final Object attrVal = exchange.getAttribute(ATTR_NAME); + if (attrVal == null) { + throw new IOException("attribute " + ATTR_NAME + " is missing in exchange: " + + exchange.getRequestURI()); + } + if (!ATTR_VAL.equals(attrVal)) { + throw new IOException("unexpected value: " + attrVal + " for attribute " + + ATTR_NAME + " in exchange: " + exchange.getRequestURI()); + } + System.out.println(exchange.getRequestURI() + " found attribute " + + ATTR_NAME + "=" + attrVal); + } else { + // unexpected request + throw new IOException("unexpected request " + exchange.getRequestURI()); + } + // let the request proceed + chain.doFilter(exchange); + } + + @Override + public String description() { + return "AttributeAddingFilter"; + } + } } From e023addf701ce4321040c96bd501355ece75a05c Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 3 Dec 2024 03:19:10 +0000 Subject: [PATCH 064/171] 8345297: test/jdk/javax/swing/Action/8133039/bug8133039.java fails in ubuntu22.04 Reviewed-by: azvegint --- test/jdk/javax/swing/Action/8133039/bug8133039.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/javax/swing/Action/8133039/bug8133039.java b/test/jdk/javax/swing/Action/8133039/bug8133039.java index f36b1b1ab10..48de406aaaf 100644 --- a/test/jdk/javax/swing/Action/8133039/bug8133039.java +++ b/test/jdk/javax/swing/Action/8133039/bug8133039.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -39,8 +39,8 @@ * @bug 8133039 * @summary Provide public API to sun.swing.UIAction#isEnabled(Object) * @modules java.desktop/sun.swing - * @author Alexander Scherbatiy */ + public class bug8133039 { private static volatile int ACTION_PERFORMED_CALLS = 0; @@ -91,6 +91,7 @@ private static void testPopupAction() throws Exception { Robot robot = new Robot(); robot.setAutoDelay(100); robot.waitForIdle(); + robot.delay(1000); robot.keyPress(KeyEvent.VK_A); robot.keyRelease(KeyEvent.VK_A); From a3b58ee5cd1ec0ea78649d4128d272458b05eb13 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Tue, 3 Dec 2024 04:06:39 +0000 Subject: [PATCH 065/171] 8339983: [s390x] secondary_super_cache does not scale well: C1 and interpreter Reviewed-by: lucy, aph --- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 4 +- src/hotspot/cpu/s390/c1_Runtime1_s390.cpp | 7 +- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 359 ++++++++++++++---- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 59 ++- src/hotspot/cpu/s390/s390.ad | 50 ++- src/hotspot/cpu/s390/stubGenerator_s390.cpp | 8 +- 6 files changed, 379 insertions(+), 108 deletions(-) diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 213aa5efe1e..dee64d3db26 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -2539,13 +2539,11 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else { bool need_slow_path = !k->is_loaded() || ((int) k->super_check_offset() == in_bytes(Klass::secondary_super_cache_offset())); - intptr_t super_check_offset = k->is_loaded() ? k->super_check_offset() : -1L; __ load_klass(klass_RInfo, obj); // Perform the fast part of the checking logic. __ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, (need_slow_path ? success_target : nullptr), - failure_target, nullptr, - RegisterOrConstant(super_check_offset)); + failure_target, nullptr); if (need_slow_path) { // Call out-of-line instance of __ check_klass_subtype_slow_path(...): address a = Runtime1::entry_for (C1StubId::slow_subtype_check_id); diff --git a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp index 8b30adb4785..0ada76ccef7 100644 --- a/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp +++ b/src/hotspot/cpu/s390/c1_Runtime1_s390.cpp @@ -557,7 +557,12 @@ OopMapSet* Runtime1::generate_code_for(C1StubId id, StubAssembler* sasm) { __ z_lg(Rsubklass, 0*BytesPerWord + FrameMap::first_available_sp_in_frame + frame_size, Z_SP); __ z_lg(Rsuperklass, 1*BytesPerWord + FrameMap::first_available_sp_in_frame + frame_size, Z_SP); - __ check_klass_subtype_slow_path(Rsubklass, Rsuperklass, Rarray_ptr, Rlength, nullptr, &miss); + __ check_klass_subtype_slow_path(Rsubklass, + Rsuperklass, + Rarray_ptr /* temp_reg */, + Rlength /* temp2_reg */, + nullptr /* L_success */, + &miss /* L_failure */); // Match falls through here. i = 0; diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 9e1c5cbced3..aacfb894c72 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -2981,21 +2981,15 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, Label* L_success, Label* L_failure, Label* L_slow_path, - RegisterOrConstant super_check_offset) { + Register super_check_offset) { + // Input registers must not overlap. + assert_different_registers(sub_klass, super_klass, temp1_reg, super_check_offset); - const int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); const int sco_offset = in_bytes(Klass::super_check_offset_offset()); - - bool must_load_sco = (super_check_offset.constant_or_zero() == -1); - bool need_slow_path = (must_load_sco || - super_check_offset.constant_or_zero() == sc_offset); + bool must_load_sco = ! super_check_offset->is_valid(); // Input registers must not overlap. - assert_different_registers(sub_klass, super_klass, temp1_reg); - if (super_check_offset.is_register()) { - assert_different_registers(sub_klass, super_klass, - super_check_offset.as_register()); - } else if (must_load_sco) { + if (must_load_sco) { assert(temp1_reg != noreg, "supply either a temp or a register offset"); } @@ -3006,9 +3000,7 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, if (L_success == nullptr) { L_success = &L_fallthrough; label_nulls++; } if (L_failure == nullptr) { L_failure = &L_fallthrough; label_nulls++; } if (L_slow_path == nullptr) { L_slow_path = &L_fallthrough; label_nulls++; } - assert(label_nulls <= 1 || - (L_slow_path == &L_fallthrough && label_nulls <= 2 && !need_slow_path), - "at most one null in the batch, usually"); + assert(label_nulls <= 1 || (L_slow_path == &L_fallthrough && label_nulls <= 2), "at most one null in the batch, usually"); BLOCK_COMMENT("check_klass_subtype_fast_path {"); // If the pointers are equal, we are done (e.g., String[] elements). @@ -3023,10 +3015,12 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, // Check the supertype display, which is uint. if (must_load_sco) { z_llgf(Rsuper_check_offset, sco_offset, super_klass); - super_check_offset = RegisterOrConstant(Rsuper_check_offset); + super_check_offset = Rsuper_check_offset; } + Address super_check_addr(sub_klass, super_check_offset, 0); z_cg(super_klass, super_check_addr); // compare w/ displayed supertype + branch_optimized(Assembler::bcondEqual, *L_success); // This check has worked decisively for primary supers. // Secondary supers are sought in the super_cache ('super_cache_addr'). @@ -3044,46 +3038,27 @@ void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, if (&(label) == &L_fallthrough) { /*do nothing*/ } \ else { branch_optimized(Assembler::bcondAlways, label); } /*omit semicolon*/ - if (super_check_offset.is_register()) { - branch_optimized(Assembler::bcondEqual, *L_success); - z_cfi(super_check_offset.as_register(), sc_offset); - if (L_failure == &L_fallthrough) { - branch_optimized(Assembler::bcondEqual, *L_slow_path); - } else { - branch_optimized(Assembler::bcondNotEqual, *L_failure); - final_jmp(*L_slow_path); - } - } else if (super_check_offset.as_constant() == sc_offset) { - // Need a slow path; fast failure is impossible. - if (L_slow_path == &L_fallthrough) { - branch_optimized(Assembler::bcondEqual, *L_success); - } else { - branch_optimized(Assembler::bcondNotEqual, *L_slow_path); - final_jmp(*L_success); - } + z_cfi(super_check_offset, in_bytes(Klass::secondary_super_cache_offset())); + if (L_failure == &L_fallthrough) { + branch_optimized(Assembler::bcondEqual, *L_slow_path); } else { - // No slow path; it's a fast decision. - if (L_failure == &L_fallthrough) { - branch_optimized(Assembler::bcondEqual, *L_success); - } else { - branch_optimized(Assembler::bcondNotEqual, *L_failure); - final_jmp(*L_success); - } + branch_optimized(Assembler::bcondNotEqual, *L_failure); + final_jmp(*L_slow_path); } bind(L_fallthrough); -#undef local_brc #undef final_jmp BLOCK_COMMENT("} check_klass_subtype_fast_path"); // fallthru (to slow path) } -void MacroAssembler::check_klass_subtype_slow_path(Register Rsubklass, - Register Rsuperklass, - Register Rarray_ptr, // tmp - Register Rlength, // tmp - Label* L_success, - Label* L_failure) { +void MacroAssembler::check_klass_subtype_slow_path_linear(Register Rsubklass, + Register Rsuperklass, + Register Rarray_ptr, // tmp + Register Rlength, // tmp + Label* L_success, + Label* L_failure, + bool set_cond_codes /* unused */) { // Input registers must not overlap. // Also check for R1 which is explicitly used here. assert_different_registers(Z_R1, Rsubklass, Rsuperklass, Rarray_ptr, Rlength); @@ -3106,7 +3081,7 @@ void MacroAssembler::check_klass_subtype_slow_path(Register Rsubklass, NearLabel loop_iterate, loop_count, match; - BLOCK_COMMENT("check_klass_subtype_slow_path {"); + BLOCK_COMMENT("check_klass_subtype_slow_path_linear {"); z_lg(Rarray_ptr, ss_offset, Rsubklass); load_and_test_int(Rlength, Address(Rarray_ptr, length_offset)); @@ -3134,18 +3109,151 @@ void MacroAssembler::check_klass_subtype_slow_path(Register Rsubklass, branch_optimized(Assembler::bcondAlways, *L_failure); // Got a hit. Return success (zero result). Set cache. - // Cache load doesn't happen here. For speed it is directly emitted by the compiler. + // Cache load doesn't happen here. For speed, it is directly emitted by the compiler. BIND(match); - z_stg(Rsuperklass, sc_offset, Rsubklass); // Save result to cache. - + if (UseSecondarySupersCache) { + z_stg(Rsuperklass, sc_offset, Rsubklass); // Save result to cache. + } final_jmp(*L_success); // Exit to the surrounding code. BIND(L_fallthrough); -#undef local_brc #undef final_jmp + BLOCK_COMMENT("} check_klass_subtype_slow_path_linear"); +} + +// If Register r is invalid, remove a new register from +// available_regs, and add new register to regs_to_push. +Register MacroAssembler::allocate_if_noreg(Register r, + RegSetIterator &available_regs, + RegSet ®s_to_push) { + if (!r->is_valid()) { + r = *available_regs++; + regs_to_push += r; + } + return r; +} + +// check_klass_subtype_slow_path_table() looks for super_klass in the +// hash table belonging to super_klass, branching to L_success or +// L_failure as appropriate. This is essentially a shim which +// allocates registers as necessary and then calls +// lookup_secondary_supers_table() to do the work. Any of the temp +// regs may be noreg, in which case this logic will choose some +// registers push and pop them from the stack. +void MacroAssembler::check_klass_subtype_slow_path_table(Register sub_klass, + Register super_klass, + Register temp_reg, + Register temp2_reg, + Register temp3_reg, + Register temp4_reg, + Register result_reg, + Label* L_success, + Label* L_failure, + bool set_cond_codes) { + BLOCK_COMMENT("check_klass_subtype_slow_path_table {"); + + RegSet temps = RegSet::of(temp_reg, temp2_reg, temp3_reg, temp4_reg); + + assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg, temp4_reg); + + Label L_fallthrough; + int label_nulls = 0; + if (L_success == nullptr) { L_success = &L_fallthrough; label_nulls++; } + if (L_failure == nullptr) { L_failure = &L_fallthrough; label_nulls++; } + assert(label_nulls <= 1, "at most one null in the batch"); + + RegSetIterator available_regs + // Z_R0 will be used to hold Z_R15(Z_SP) while pushing a new frame, So don't use that here. + // Z_R1 will be used to hold r_bitmap in lookup_secondary_supers_table_var, so can't be used + // Z_R2, Z_R3, Z_R4 will be used in secondary_supers_verify, for the failure reporting + = (RegSet::range(Z_R0, Z_R15) - temps - sub_klass - super_klass - Z_R1_scratch - Z_R0_scratch - Z_R2 - Z_R3 - Z_R4).begin(); + + RegSet pushed_regs; + + temp_reg = allocate_if_noreg(temp_reg, available_regs, pushed_regs); + temp2_reg = allocate_if_noreg(temp2_reg, available_regs, pushed_regs); + temp3_reg = allocate_if_noreg(temp3_reg, available_regs, pushed_regs);; + temp4_reg = allocate_if_noreg(temp4_reg, available_regs, pushed_regs); + result_reg = allocate_if_noreg(result_reg, available_regs, pushed_regs); + + const int frame_size = pushed_regs.size() * BytesPerWord + frame::z_abi_160_size; + + // Push & save registers + { + int i = 0; + save_return_pc(); + push_frame(frame_size); + + for (auto it = pushed_regs.begin(); *it != noreg; i++) { + z_stg(*it++, i * BytesPerWord + frame::z_abi_160_size, Z_SP); + } + assert(i * BytesPerWord + frame::z_abi_160_size == frame_size, "sanity"); + } + + lookup_secondary_supers_table_var(sub_klass, + super_klass, + temp_reg, temp2_reg, temp3_reg, temp4_reg, result_reg); + + // NOTE: Condition Code should not be altered before jump instruction below !!!! + z_cghi(result_reg, 0); + + { + int i = 0; + for (auto it = pushed_regs.begin(); *it != noreg; ++i) { + z_lg(*it++, i * BytesPerWord + frame::z_abi_160_size, Z_SP); + } + assert(i * BytesPerWord + frame::z_abi_160_size == frame_size, "sanity"); + pop_frame(); + restore_return_pc(); + } + + // NB! Callers may assume that, when set_cond_codes is true, this + // code sets temp2_reg to a nonzero value. + if (set_cond_codes) { + z_lghi(temp2_reg, 1); + } + + branch_optimized(bcondNotEqual, *L_failure); + + if(L_success != &L_fallthrough) { + z_bru(*L_success); + } + + bind(L_fallthrough); + BLOCK_COMMENT("} check_klass_subtype_slow_path_table"); +} + +void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass, + Register super_klass, + Register temp_reg, + Register temp2_reg, + Label* L_success, + Label* L_failure, + bool set_cond_codes) { + BLOCK_COMMENT("check_klass_subtype_slow_path {"); + if (UseSecondarySupersTable) { + check_klass_subtype_slow_path_table(sub_klass, + super_klass, + temp_reg, + temp2_reg, + /*temp3*/noreg, + /*temp4*/noreg, + /*result*/noreg, + L_success, + L_failure, + set_cond_codes); + } else { + check_klass_subtype_slow_path_linear(sub_klass, + super_klass, + temp_reg, + temp2_reg, + L_success, + L_failure, + set_cond_codes); + } BLOCK_COMMENT("} check_klass_subtype_slow_path"); } @@ -3206,17 +3314,17 @@ do { \ } while(0) // Note: this method also kills Z_R1_scratch register on machines older than z15 -void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass, - Register r_super_klass, - Register r_temp1, - Register r_temp2, - Register r_temp3, - Register r_temp4, - Register r_result, - u1 super_klass_slot) { +void MacroAssembler::lookup_secondary_supers_table_const(Register r_sub_klass, + Register r_super_klass, + Register r_temp1, + Register r_temp2, + Register r_temp3, + Register r_temp4, + Register r_result, + u1 super_klass_slot) { NearLabel L_done, L_failure; - BLOCK_COMMENT("lookup_secondary_supers_table {"); + BLOCK_COMMENT("lookup_secondary_supers_table_const {"); const Register r_array_base = r_temp1, @@ -3291,7 +3399,7 @@ void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass, z_lghi(r_result, 1); bind(L_done); - BLOCK_COMMENT("} lookup_secondary_supers_table"); + BLOCK_COMMENT("} lookup_secondary_supers_table_const"); if (VerifySecondarySupers) { verify_secondary_supers_table(r_sub_klass, r_super_klass, r_result, @@ -3299,6 +3407,116 @@ void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass, } } +// At runtime, return 0 in result if r_super_klass is a superclass of +// r_sub_klass, otherwise return nonzero. Use this version of +// lookup_secondary_supers_table() if you don't know ahead of time +// which superclass will be searched for. Used by interpreter and +// runtime stubs. It is larger and has somewhat greater latency than +// the version above, which takes a constant super_klass_slot. +void MacroAssembler::lookup_secondary_supers_table_var(Register r_sub_klass, + Register r_super_klass, + Register temp1, + Register temp2, + Register temp3, + Register temp4, + Register result) { + assert_different_registers(r_sub_klass, r_super_klass, temp1, temp2, temp3, temp4, result, Z_R1_scratch); + + Label L_done, L_failure; + + BLOCK_COMMENT("lookup_secondary_supers_table_var {"); + + const Register + r_array_index = temp3, + slot = temp4, // NOTE: "slot" can't be Z_R0 otherwise z_sllg and z_rllg instructions below will mess up!!!! + r_bitmap = Z_R1_scratch; + + z_llgc(slot, Address(r_super_klass, Klass::hash_slot_offset())); + + // Initialize r_result with 0 (indicating success). If searching fails, r_result will be loaded + // with 1 (failure) at the end of this method. + clear_reg(result, true /* whole_reg */, false /* set_cc */); // result = 0 + + z_lg(r_bitmap, Address(r_sub_klass, Klass::secondary_supers_bitmap_offset())); + + // First check the bitmap to see if super_klass might be present. If + // the bit is zero, we are certain that super_klass is not one of + // the secondary supers. + z_xilf(slot, (u1)(Klass::SECONDARY_SUPERS_TABLE_SIZE - 1)); // slot ^ 63 === 63 - slot (mod 64) + z_sllg(r_array_index, r_bitmap, /*d2 = */ 0, /* b2 = */ slot); + + testbit(r_array_index, Klass::SECONDARY_SUPERS_TABLE_SIZE - 1); + branch_optimized(bcondAllZero, L_failure); + + const Register + r_array_base = temp1, + r_array_length = temp2; + + // Get the first array index that can contain super_klass into r_array_index. + // NOTE: Z_R1_scratch is holding bitmap (look above for r_bitmap). So let's try to save it. + // On the other hand, r_array_base/temp1 is free at current moment (look at the load operation below). + pop_count_long(r_array_index, r_array_index, temp1); // kills r_array_base/temp1 on machines older than z15 + + // The value i in r_array_index is >= 1, so even though r_array_base + // points to the length, we don't need to adjust it to point to the data. + assert(Array::base_offset_in_bytes() == wordSize, "Adjust this code"); + assert(Array::length_offset_in_bytes() == 0, "Adjust this code"); + + // We will consult the secondary-super array. + z_lg(r_array_base, Address(r_sub_klass, in_bytes(Klass::secondary_supers_offset()))); + + // NB! r_array_index is off by 1. It is compensated by keeping r_array_base off by 1 word. + z_sllg(r_array_index, r_array_index, LogBytesPerWord); // scale, r_array_index is loaded by popcnt above + + z_cg(r_super_klass, Address(r_array_base, r_array_index)); + branch_optimized(bcondEqual, L_done); // found a match + + // Note: this is a small hack: + // + // The operation "(slot ^ 63) === 63 - slot (mod 64)" has already been performed above. + // Since we lack a rotate-right instruction, we achieve the same effect by rotating left + // by "64 - slot" positions. This produces the result equivalent to a right rotation by "slot" positions. + // + // => initial slot value + // => slot = 63 - slot // done above with that z_xilf instruction + // => slot = 64 - slot // need to do for rotating right by "slot" positions + // => slot = 64 - (63 - slot) + // => slot = slot - 63 + 64 + // => slot = slot + 1 + // + // So instead of rotating-left by 64-slot times, we can, for now, just rotate left by slot+1 and it would be fine. + + // Linear probe. Rotate the bitmap so that the next bit to test is + // in Bit 1. + z_aghi(slot, 1); // slot = slot + 1 + + z_rllg(r_bitmap, r_bitmap, /*d2=*/ 0, /*b2=*/ slot); + testbit(r_bitmap, 1); + branch_optimized(bcondAllZero, L_failure); + + // The slot we just inspected is at secondary_supers[r_array_index - 1]. + // The next slot to be inspected, by the logic we're about to call, + // is secondary_supers[r_array_index]. Bits 0 and 1 in the bitmap + // have been checked. + lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index, + r_bitmap, /*temp=*/ r_array_length, result, /*is_stub*/false); + + // pass whatever we got from slow path + z_bru(L_done); + + bind(L_failure); + z_lghi(result, 1); // load 1 to represent failure + + bind(L_done); + + BLOCK_COMMENT("} lookup_secondary_supers_table_var"); + + if (VerifySecondarySupers) { + verify_secondary_supers_table(r_sub_klass, r_super_klass, result, + temp1, temp2, temp3); + } +} + // Called by code generated by check_klass_subtype_slow_path // above. This is called when there is a collision in the hashed // lookup in the secondary supers array. @@ -3306,15 +3524,18 @@ void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_kl Register r_array_base, Register r_array_index, Register r_bitmap, + Register r_temp, Register r_result, - Register r_temp1) { - assert_different_registers(r_super_klass, r_array_base, r_array_index, r_bitmap, r_result, r_temp1); + bool is_stub) { + assert_different_registers(r_super_klass, r_array_base, r_array_index, r_bitmap, r_result, r_temp); const Register - r_array_length = r_temp1, + r_array_length = r_temp, r_sub_klass = noreg; - LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + if(is_stub) { + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + } BLOCK_COMMENT("lookup_secondary_supers_table_slow_path {"); NearLabel L_done, L_failure; @@ -3343,8 +3564,10 @@ void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_kl { // This is conventional linear probing, but instead of terminating // when a null entry is found in the table, we maintain a bitmap // in which a 0 indicates missing entries. - // The check above guarantees there are 0s in the bitmap, so the loop - // eventually terminates. + // As long as the bitmap is not completely full, + // array_length == popcount(bitmap). The array_length check above + // guarantees there are 0s in the bitmap, so the loop eventually + // terminates. #ifdef ASSERT // r_result is set to 0 by lookup_secondary_supers_table. @@ -3417,8 +3640,6 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, const Register r_one = Z_R0_scratch; z_lghi(r_one, 1); // for locgr down there, to a load result for failure - LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; - BLOCK_COMMENT("verify_secondary_supers_table {"); Label L_passed, L_failure; diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 7806fef3ce8..91703fac994 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -694,7 +694,7 @@ class MacroAssembler: public Assembler { Label* L_success, Label* L_failure, Label* L_slow_path, - RegisterOrConstant super_check_offset = RegisterOrConstant(-1)); + Register super_check_offset = noreg); // The rest of the type check; must be wired to a corresponding fast path. // It does not repeat the fast path logic, so don't use it standalone. @@ -706,25 +706,62 @@ class MacroAssembler: public Assembler { Register Rarray_ptr, // tmp Register Rlength, // tmp Label* L_success, - Label* L_failure); + Label* L_failure, + bool set_cond_codes = false); + + void check_klass_subtype_slow_path_linear(Register sub_klass, + Register super_klass, + Register temp_reg, + Register temp2_reg, + Label* L_success, + Label* L_failure, + bool set_cond_codes = false); + + void check_klass_subtype_slow_path_table(Register sub_klass, + Register super_klass, + Register temp_reg, + Register temp2_reg, + Register temp3_reg, + Register temp4_reg, + Register result_reg, + Label* L_success, + Label* L_failure, + bool set_cond_codes = false); + + // If r is valid, return r. + // If r is invalid, remove a register r2 from available_regs, add r2 + // to regs_to_push, then return r2. + Register allocate_if_noreg(const Register r, + RegSetIterator &available_regs, + RegSet ®s_to_push); void repne_scan(Register r_addr, Register r_value, Register r_count, Register r_scratch); - void lookup_secondary_supers_table(Register r_sub_klass, - Register r_super_klass, - Register r_temp1, - Register r_temp2, - Register r_temp3, - Register r_temp4, - Register r_result, - u1 super_klass_slot); + // Secondary subtype checking + void lookup_secondary_supers_table_var(Register sub_klass, + Register r_super_klass, + Register temp1, + Register temp2, + Register temp3, + Register temp4, + Register result); + + void lookup_secondary_supers_table_const(Register r_sub_klass, + Register r_super_klass, + Register r_temp1, + Register r_temp2, + Register r_temp3, + Register r_temp4, + Register r_result, + u1 super_klass_slot); void lookup_secondary_supers_table_slow_path(Register r_super_klass, Register r_array_base, Register r_array_index, Register r_bitmap, + Register r_temp, Register r_result, - Register r_temp1); + bool is_stub); void verify_secondary_supers_table(Register r_sub_klass, Register r_super_klass, diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index e1a98139992..0f1d98d54b9 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -9979,8 +9979,9 @@ instruct ShouldNotReachHere() %{ instruct partialSubtypeCheck(rarg1RegP index, rarg2RegP sub, rarg3RegP super, flagsReg pcc, rarg4RegP scratch1, rarg5RegP scratch2) %{ match(Set index (PartialSubtypeCheck sub super)); + predicate(!UseSecondarySupersTable); effect(KILL pcc, KILL scratch1, KILL scratch2); - ins_cost(10 * DEFAULT_COST); + ins_cost(20 * DEFAULT_COST); // slightly larger than the next version // TODO: s390 port size(FIXED_SIZE); format %{ " CALL PartialSubtypeCheck\n" %} ins_encode %{ @@ -9991,21 +9992,45 @@ instruct partialSubtypeCheck(rarg1RegP index, rarg2RegP sub, rarg3RegP super, fl ins_pipe(pipe_class_dummy); %} +// Two versions of partialSubtypeCheck, both used when we need to +// search for a super class in the secondary supers array. The first +// is used when we don't know _a priori_ the class being searched +// for. The second, far more common, is used when we do know: this is +// used for instanceof, checkcast, and any case where C2 can determine +// it by constant propagation. +instruct partialSubtypeCheckVarSuper(rarg2RegP sub, rarg3RegP super, + r11TempRegP result, + rarg1RegP temp1, rarg4RegP temp2, rarg5RegP temp3, r10TempRegP temp4, + flagsReg pcc) %{ + match(Set result (PartialSubtypeCheck sub super)); + predicate(UseSecondarySupersTable); + effect(KILL pcc, TEMP temp1, TEMP temp2, TEMP temp3, TEMP temp4); + ins_cost(10 * DEFAULT_COST); // slightly larger than the next version + format %{ "partialSubtypeCheck $result, $sub, $super" %} + ins_encode %{ + __ lookup_secondary_supers_table_var($sub$$Register, $super$$Register, + $temp1$$Register, $temp2$$Register, $temp3$$Register, $temp4$$Register, + $result$$Register); + %} + ins_pipe(pipe_class_dummy); +%} + + instruct partialSubtypeCheckConstSuper(rarg2RegP sub, rarg1RegP super, immP super_con, r11TempRegP result, rarg5RegP temp1, rarg4RegP temp2, rarg3RegP temp3, r10TempRegP temp4, flagsReg pcc) %{ match(Set result (PartialSubtypeCheck sub (Binary super super_con))); predicate(UseSecondarySupersTable); effect(KILL pcc, TEMP temp1, TEMP temp2, TEMP temp3, TEMP temp4); - ins_cost(7 * DEFAULT_COST); // needs to be less than competing nodes + ins_cost(5 * DEFAULT_COST); // smaller than the next version format %{ "partialSubtypeCheck $result, $sub, $super, $super_con" %} ins_encode %{ u1 super_klass_slot = ((Klass*)$super_con$$constant)->hash_slot(); if (InlineSecondarySupersTest) { - __ lookup_secondary_supers_table($sub$$Register, $super$$Register, - $temp1$$Register, $temp2$$Register, $temp3$$Register, - $temp4$$Register, $result$$Register, super_klass_slot); + __ lookup_secondary_supers_table_const($sub$$Register, $super$$Register, + $temp1$$Register, $temp2$$Register, $temp3$$Register, + $temp4$$Register, $result$$Register, super_klass_slot); } else { AddressLiteral stub_address(StubRoutines::lookup_secondary_supers_table_stub(super_klass_slot)); __ load_const_optimized(Z_ARG4, stub_address); @@ -10017,21 +10042,6 @@ instruct partialSubtypeCheckConstSuper(rarg2RegP sub, rarg1RegP super, immP supe ins_pipe(pipe_class_dummy); %} -instruct partialSubtypeCheck_vs_zero(flagsReg pcc, rarg2RegP sub, rarg3RegP super, immP0 zero, - rarg1RegP index, rarg4RegP scratch1, rarg5RegP scratch2) %{ - match(Set pcc (CmpI (PartialSubtypeCheck sub super) zero)); - effect(KILL scratch1, KILL scratch2, KILL index); - ins_cost(10 * DEFAULT_COST); - // TODO: s390 port size(FIXED_SIZE); - format %{ "CALL PartialSubtypeCheck_vs_zero\n" %} - ins_encode %{ - AddressLiteral stub_address(StubRoutines::zarch::partial_subtype_check()); - __ load_const_optimized(Z_ARG4, stub_address); - __ z_basr(Z_R14, Z_ARG4); - %} - ins_pipe(pipe_class_dummy); -%} - // ============================================================================ // inlined locking and unlocking diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index dd9ed4c9546..9e33cd3abe8 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -635,9 +635,9 @@ class StubGenerator: public StubCodeGenerator { r_result = Z_R11; address start = __ pc(); - __ lookup_secondary_supers_table(r_sub_klass, r_super_klass, - r_array_base, r_array_length, r_array_index, - r_bitmap, r_result, super_klass_index); + __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, + r_array_base, r_array_length, r_array_index, + r_bitmap, r_result, super_klass_index); __ z_br(Z_R14); @@ -659,7 +659,7 @@ class StubGenerator: public StubCodeGenerator { r_result = Z_R11; __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, - r_array_index, r_bitmap, r_result, r_temp1); + r_array_index, r_bitmap, r_temp1, r_result, /* is_stub */ true); __ z_br(Z_R14); From 4ac2e477b9bb9995047718b7d8df36c3dc739a9d Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Tue, 3 Dec 2024 04:54:51 +0000 Subject: [PATCH 066/171] 8343800: Cleanup definition of NULL_WORD Reviewed-by: dholmes, kvn --- .../share/utilities/globalDefinitions.hpp | 3 +++ .../share/utilities/globalDefinitions_gcc.hpp | 18 ------------------ .../utilities/globalDefinitions_visCPP.hpp | 6 ------ 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 25af626c628..ccd3106b471 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -560,6 +560,9 @@ const jfloat min_jfloat = jfloat_cast(min_jintFloat); const jint max_jintFloat = (jint)(0x7f7fffff); const jfloat max_jfloat = jfloat_cast(max_jintFloat); +// A named constant for the integral representation of a Java null. +const intptr_t NULL_WORD = 0; + //---------------------------------------------------------------------------------------------------- // JVM spec restrictions diff --git a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp index e5decbcd536..863e588d180 100644 --- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp @@ -71,24 +71,6 @@ #include #endif // LINUX || _ALLBSD_SOURCE -// NULL vs NULL_WORD: -// On Linux NULL is defined as a special type '__null'. Assigning __null to -// integer variable will cause gcc warning. Use NULL_WORD in places where a -// pointer is stored as integer value. On some platforms, sizeof(intptr_t) > -// sizeof(void*), so here we want something which is integer type, but has the -// same size as a pointer. -#ifdef __GNUC__ - #ifdef _LP64 - #define NULL_WORD 0L - #else - // Cast 0 to intptr_t rather than int32_t since they are not the same type - // on platforms such as Mac OS X. - #define NULL_WORD ((intptr_t)0) - #endif -#else - #define NULL_WORD NULL -#endif - // checking for nanness #if defined(__APPLE__) inline int g_isnan(double f) { return isnan(f); } diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp index 237d12baa6d..5f26a082d7c 100644 --- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp @@ -72,12 +72,6 @@ // 64-bit integer-suffix (LL) instead. #define NULL 0LL -// NULL vs NULL_WORD: -// On Linux NULL is defined as a special type '__null'. Assigning __null to -// integer variable will cause gcc warning. Use NULL_WORD in places where a -// pointer is stored as integer value. -#define NULL_WORD NULL - typedef int64_t ssize_t; // Non-standard stdlib-like stuff: From 3eb54615783562f24e61983dfcc3cb823b27b0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 3 Dec 2024 06:59:06 +0000 Subject: [PATCH 067/171] 8343791: Socket.connect API should document whether the socket will be closed when hostname resolution fails or another error occurs Reviewed-by: dfuchs, alanb --- .../share/classes/java/net/Socket.java | 56 ++++-- .../share/classes/sun/nio/ch/Net.java | 25 +-- .../classes/sun/nio/ch/NioSocketImpl.java | 5 +- .../classes/sun/nio/ch/SocketAdaptor.java | 11 +- test/jdk/java/net/Socket/ConnectFailTest.java | 171 ++++++++++++++++++ 5 files changed, 226 insertions(+), 42 deletions(-) create mode 100644 test/jdk/java/net/Socket/ConnectFailTest.java diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 83c0dec682c..929e99b5303 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -454,6 +454,7 @@ private Socket(SocketAddress address, SocketAddress localAddr, boolean stream) throws IOException { Objects.requireNonNull(address); + assert address instanceof InetSocketAddress; // create the SocketImpl and the underlying socket SocketImpl impl = createImpl(); @@ -463,16 +464,13 @@ private Socket(SocketAddress address, SocketAddress localAddr, boolean stream) this.state = SOCKET_CREATED; try { - if (localAddr != null) + if (localAddr != null) { bind(localAddr); - connect(address); - } catch (IOException | IllegalArgumentException e) { - try { - close(); - } catch (IOException ce) { - e.addSuppressed(ce); } - throw e; + connect(address); + } catch (Throwable throwable) { + closeSuppressingExceptions(throwable); + throw throwable; } } @@ -571,6 +569,10 @@ void setConnected() { /** * Connects this socket to the server. * + *

If the endpoint is an unresolved {@link InetSocketAddress}, or the + * connection cannot be established, then the socket is closed, and an + * {@link IOException} is thrown. + * *

This method is {@linkplain Thread#interrupt() interruptible} in the * following circumstances: *

    @@ -589,6 +591,8 @@ void setConnected() { * @param endpoint the {@code SocketAddress} * @throws IOException if an error occurs during the connection, the socket * is already connected or the socket is closed + * @throws UnknownHostException if the endpoint is an unresolved + * {@link InetSocketAddress} * @throws java.nio.channels.IllegalBlockingModeException * if this socket has an associated channel, * and the channel is in non-blocking mode @@ -605,6 +609,11 @@ public void connect(SocketAddress endpoint) throws IOException { * A timeout of zero is interpreted as an infinite timeout. The connection * will then block until established or an error occurs. * + *

    If the endpoint is an unresolved {@link InetSocketAddress}, the + * connection cannot be established, or the timeout expires before the + * connection is established, then the socket is closed, and an + * {@link IOException} is thrown. + * *

    This method is {@linkplain Thread#interrupt() interruptible} in the * following circumstances: *

      @@ -625,6 +634,8 @@ public void connect(SocketAddress endpoint) throws IOException { * @throws IOException if an error occurs during the connection, the socket * is already connected or the socket is closed * @throws SocketTimeoutException if timeout expires before connecting + * @throws UnknownHostException if the endpoint is an unresolved + * {@link InetSocketAddress} * @throws java.nio.channels.IllegalBlockingModeException * if this socket has an associated channel, * and the channel is in non-blocking mode @@ -644,26 +655,25 @@ public void connect(SocketAddress endpoint, int timeout) throws IOException { if (isClosed(s)) throw new SocketException("Socket is closed"); if (isConnected(s)) - throw new SocketException("already connected"); + throw new SocketException("Already connected"); if (!(endpoint instanceof InetSocketAddress epoint)) throw new IllegalArgumentException("Unsupported address type"); + if (epoint.isUnresolved()) { + var uhe = new UnknownHostException(epoint.getHostName()); + closeSuppressingExceptions(uhe); + throw uhe; + } + InetAddress addr = epoint.getAddress(); - int port = epoint.getPort(); checkAddress(addr, "connect"); try { getImpl().connect(epoint, timeout); - } catch (SocketTimeoutException e) { - throw e; - } catch (InterruptedIOException e) { - Thread thread = Thread.currentThread(); - if (thread.isVirtual() && thread.isInterrupted()) { - close(); - throw new SocketException("Closed by interrupt"); - } - throw e; + } catch (IOException error) { + closeSuppressingExceptions(error); + throw error; } // connect will bind the socket if not previously bound @@ -1589,6 +1599,14 @@ public boolean getReuseAddress() throws SocketException { return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue(); } + private void closeSuppressingExceptions(Throwable parentException) { + try { + close(); + } catch (IOException exception) { + parentException.addSuppressed(exception); + } + } + /** * Closes this socket. *

      diff --git a/src/java.base/share/classes/sun/nio/ch/Net.java b/src/java.base/share/classes/sun/nio/ch/Net.java index 03dcf04a50f..5c922aff676 100644 --- a/src/java.base/share/classes/sun/nio/ch/Net.java +++ b/src/java.base/share/classes/sun/nio/ch/Net.java @@ -40,6 +40,7 @@ import java.net.StandardSocketOptions; import java.net.UnknownHostException; import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.NotYetBoundException; import java.nio.channels.NotYetConnectedException; @@ -166,6 +167,8 @@ else if (x instanceof NotYetConnectedException) nx = newSocketException("Socket is not connected"); else if (x instanceof AlreadyBoundException) nx = newSocketException("Already bound"); + else if (x instanceof AlreadyConnectedException) + nx = newSocketException("Already connected"); else if (x instanceof NotYetBoundException) nx = newSocketException("Socket is not bound yet"); else if (x instanceof UnsupportedAddressTypeException) @@ -190,32 +193,12 @@ private static SocketException newSocketException(String msg) { return new SocketException(msg); } - static void translateException(Exception x, - boolean unknownHostForUnresolved) - throws IOException - { + static void translateException(Exception x) throws IOException { if (x instanceof IOException ioe) throw ioe; - // Throw UnknownHostException from here since it cannot - // be thrown as a SocketException - if (unknownHostForUnresolved && - (x instanceof UnresolvedAddressException)) - { - throw new UnknownHostException(); - } translateToSocketException(x); } - static void translateException(Exception x) - throws IOException - { - translateException(x, false); - } - - private static InetSocketAddress getLoopbackAddress(int port) { - return new InetSocketAddress(InetAddress.getLoopbackAddress(), port); - } - private static final InetAddress ANY_LOCAL_INET4ADDRESS; private static final InetAddress ANY_LOCAL_INET6ADDRESS; private static final InetAddress INET4_LOOPBACK_ADDRESS; diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index 40bc3156680..7c64d6721e2 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -599,8 +599,11 @@ protected void connect(SocketAddress remote, int millis) throws IOException { } } catch (IOException ioe) { close(); - if (ioe instanceof InterruptedIOException) { + if (ioe instanceof SocketTimeoutException) { throw ioe; + } else if (ioe instanceof InterruptedIOException) { + assert Thread.currentThread().isVirtual(); + throw new SocketException("Closed by interrupt"); } else { throw SocketExceptions.of(ioe, isa); } diff --git a/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java b/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java index cbcfd79378c..d8ed1cfb675 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java @@ -35,6 +35,7 @@ import java.net.SocketException; import java.net.SocketOption; import java.net.StandardSocketOptions; +import java.net.UnknownHostException; import java.nio.channels.SocketChannel; import java.util.Set; @@ -85,6 +86,14 @@ public void connect(SocketAddress remote) throws IOException { public void connect(SocketAddress remote, int timeout) throws IOException { if (remote == null) throw new IllegalArgumentException("connect: The address can't be null"); + if (remote instanceof InetSocketAddress isa && isa.isUnresolved()) { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (sc.isConnected()) + throw new SocketException("Already connected"); + close(); + throw new UnknownHostException(remote.toString()); + } if (timeout < 0) throw new IllegalArgumentException("connect: timeout can't be negative"); try { @@ -95,7 +104,7 @@ public void connect(SocketAddress remote, int timeout) throws IOException { sc.blockingConnect(remote, Long.MAX_VALUE); } } catch (Exception e) { - Net.translateException(e, true); + Net.translateException(e); } } diff --git a/test/jdk/java/net/Socket/ConnectFailTest.java b/test/jdk/java/net/Socket/ConnectFailTest.java new file mode 100644 index 00000000000..7cc46ce4a4d --- /dev/null +++ b/test/jdk/java/net/Socket/ConnectFailTest.java @@ -0,0 +1,171 @@ +/* + * 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. + */ + +import jdk.test.lib.Utils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.channels.SocketChannel; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8343791 + * @summary verifies that `connect()` failures throw the expected exception and leave socket in the expected state + * @library /test/lib + * @run junit ConnectFailTest + */ +class ConnectFailTest { + + private static final int DEAD_SERVER_PORT = 0xDEAD; + + private static final InetSocketAddress REFUSING_SOCKET_ADDRESS = Utils.refusingEndpoint(); + + private static final InetSocketAddress UNRESOLVED_ADDRESS = + InetSocketAddress.createUnresolved("no.such.host", DEAD_SERVER_PORT); + + @Test + void testUnresolvedAddress() { + assertTrue(UNRESOLVED_ADDRESS.isUnresolved()); + } + + /** + * Verifies that an unbound socket is closed when {@code connect()} fails. + */ + @ParameterizedTest + @MethodSource("sockets") + void testUnboundSocket(Socket socket) throws IOException { + try (socket) { + assertFalse(socket.isBound()); + assertFalse(socket.isConnected()); + assertThrows(IOException.class, () -> socket.connect(REFUSING_SOCKET_ADDRESS)); + assertTrue(socket.isClosed()); + } + } + + /** + * Verifies that a bound socket is closed when {@code connect()} fails. + */ + @ParameterizedTest + @MethodSource("sockets") + void testBoundSocket(Socket socket) throws IOException { + try (socket) { + socket.bind(new InetSocketAddress(0)); + assertTrue(socket.isBound()); + assertFalse(socket.isConnected()); + assertThrows(IOException.class, () -> socket.connect(REFUSING_SOCKET_ADDRESS)); + assertTrue(socket.isClosed()); + } + } + + /** + * Verifies that a connected socket is not closed when {@code connect()} fails. + */ + @ParameterizedTest + @MethodSource("sockets") + void testConnectedSocket(Socket socket) throws Throwable { + try (socket; ServerSocket serverSocket = createEphemeralServerSocket()) { + socket.connect(serverSocket.getLocalSocketAddress()); + try (Socket _ = serverSocket.accept()) { + assertTrue(socket.isBound()); + assertTrue(socket.isConnected()); + SocketException exception = assertThrows( + SocketException.class, + () -> socket.connect(REFUSING_SOCKET_ADDRESS)); + assertEquals("Already connected", exception.getMessage()); + assertFalse(socket.isClosed()); + } + } + } + + /** + * Verifies that an unbound socket is closed when {@code connect()} is invoked using an unresolved address. + */ + @ParameterizedTest + @MethodSource("sockets") + void testUnboundSocketWithUnresolvedAddress(Socket socket) throws IOException { + try (socket) { + assertFalse(socket.isBound()); + assertFalse(socket.isConnected()); + assertThrows(UnknownHostException.class, () -> socket.connect(UNRESOLVED_ADDRESS)); + assertTrue(socket.isClosed()); + } + } + + /** + * Verifies that a bound socket is closed when {@code connect()} is invoked using an unresolved address. + */ + @ParameterizedTest + @MethodSource("sockets") + void testBoundSocketWithUnresolvedAddress(Socket socket) throws IOException { + try (socket) { + socket.bind(new InetSocketAddress(0)); + assertTrue(socket.isBound()); + assertFalse(socket.isConnected()); + assertThrows(UnknownHostException.class, () -> socket.connect(UNRESOLVED_ADDRESS)); + assertTrue(socket.isClosed()); + } + } + + /** + * Verifies that a connected socket is not closed when {@code connect()} is invoked using an unresolved address. + */ + @ParameterizedTest + @MethodSource("sockets") + void testConnectedSocketWithUnresolvedAddress(Socket socket) throws Throwable { + try (socket; ServerSocket serverSocket = createEphemeralServerSocket()) { + socket.connect(serverSocket.getLocalSocketAddress()); + try (Socket _ = serverSocket.accept()) { + assertTrue(socket.isBound()); + assertTrue(socket.isConnected()); + assertThrows(IOException.class, () -> socket.connect(UNRESOLVED_ADDRESS)); + assertFalse(socket.isClosed()); + } + } + } + + static List sockets() throws Exception { + Socket socket = new Socket(); + @SuppressWarnings("resource") + Socket channelSocket = SocketChannel.open().socket(); + return List.of(socket, channelSocket); + } + + private static ServerSocket createEphemeralServerSocket() throws IOException { + return new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); + } + +} From 5c8cb2edcb0a919bfcad11b3f2cb399402915a0c Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Tue, 3 Dec 2024 07:24:46 +0000 Subject: [PATCH 068/171] 8337199: Add jcmd Thread.vthread_scheduler and Thread.vthread_pollers diagnostic commands Reviewed-by: dholmes, kevinw --- src/hotspot/share/classfile/vmSymbols.hpp | 7 +- .../share/services/diagnosticCommand.cpp | 48 ++++++- .../share/services/diagnosticCommand.hpp | 27 ++++ .../share/classes/java/lang/System.java | 5 + .../classes/java/lang/VirtualThread.java | 10 +- .../jdk/internal/access/JavaLangAccess.java | 6 + .../jdk/internal/vm/JcmdVThreadCommands.java | 112 +++++++++++++++ .../share/classes/sun/nio/ch/Poller.java | 30 +++- src/jdk.jcmd/share/man/jcmd.md | 11 ++ .../src/share/conf/common.properties | 6 +- .../dcmd/thread/VThreadCommandsTest.java | 133 ++++++++++++++++++ 11 files changed, 383 insertions(+), 12 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/vm/JcmdVThreadCommands.java create mode 100644 test/hotspot/jtreg/serviceability/dcmd/thread/VThreadCommandsTest.java diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 6a6f7754c50..46c156a5445 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -739,10 +739,15 @@ class SerializeClosure; template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ template(url_array_classloader_void_signature, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V") \ \ - /* Thread.dump_to_file jcmd */ \ + /* jcmd Thread.dump_to_file */ \ template(jdk_internal_vm_ThreadDumper, "jdk/internal/vm/ThreadDumper") \ template(dumpThreads_name, "dumpThreads") \ template(dumpThreadsToJson_name, "dumpThreadsToJson") \ + \ + /* jcmd Thread.vthread_scheduler and Thread.vthread_pollers */ \ + template(jdk_internal_vm_JcmdVThreadCommands, "jdk/internal/vm/JcmdVThreadCommands") \ + template(printScheduler_name, "printScheduler") \ + template(printPollers_name, "printPollers") \ /*end*/ diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index b081ce29e26..e4dff47c84f 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -128,6 +128,8 @@ void DCmd::register_dcmds(){ #endif // INCLUDE_JVMTI DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); @@ -1073,13 +1075,6 @@ void ThreadDumpToFileDCmd::dumpToFile(Symbol* name, Symbol* signature, const cha Symbol* sym = vmSymbols::jdk_internal_vm_ThreadDumper(); Klass* k = SystemDictionary::resolve_or_fail(sym, true, CHECK); - InstanceKlass* ik = InstanceKlass::cast(k); - if (HAS_PENDING_EXCEPTION) { - java_lang_Throwable::print(PENDING_EXCEPTION, output()); - output()->cr(); - CLEAR_PENDING_EXCEPTION; - return; - } // invoke the ThreadDump method to dump to file JavaValue result(T_OBJECT); @@ -1110,6 +1105,45 @@ void ThreadDumpToFileDCmd::dumpToFile(Symbol* name, Symbol* signature, const cha output()->print_raw((const char*)addr, ba->length()); } +// Calls a static no-arg method on jdk.internal.vm.JcmdVThreadCommands that returns a byte[] with +// the output. If the method completes successfully then the bytes are copied to the output stream. +// If the method fails then the exception is printed to the output stream. +static void execute_vthread_command(Symbol* method_name, outputStream* output, TRAPS) { + ResourceMark rm(THREAD); + HandleMark hm(THREAD); + + Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_JcmdVThreadCommands(), true, CHECK); + + JavaValue result(T_OBJECT); + JavaCallArguments args; + JavaCalls::call_static(&result, + k, + method_name, + vmSymbols::void_byte_array_signature(), + &args, + THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, output); + output->cr(); + CLEAR_PENDING_EXCEPTION; + return; + } + + // copy the bytes to the output stream + oop res = cast_to_oop(result.get_jobject()); + typeArrayOop ba = typeArrayOop(res); + jbyte* addr = typeArrayOop(res)->byte_at_addr(0); + output->print_raw((const char*)addr, ba->length()); +} + +void VThreadSchedulerDCmd::execute(DCmdSource source, TRAPS) { + execute_vthread_command(vmSymbols::printScheduler_name(), output(), CHECK); +} + +void VThreadPollersDCmd::execute(DCmdSource source, TRAPS) { + execute_vthread_command(vmSymbols::printPollers_name(), output(), CHECK); +} + CompilationMemoryStatisticDCmd::CompilationMemoryStatisticDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), _human_readable("-H", "Human readable format", "BOOLEAN", false, "false"), diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 30b2be2a61b..83e818e16d2 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -776,6 +776,33 @@ class ThreadDumpToFileDCmd : public DCmdWithParser { virtual void execute(DCmdSource source, TRAPS); }; +class VThreadSchedulerDCmd : public DCmd { +public: + VThreadSchedulerDCmd(outputStream* output, bool heap) : DCmd(output, heap) { } + static const char* name() { + return "Thread.vthread_scheduler"; + } + static const char* description() { + return "Print the virtual thread scheduler, and the delayed task schedulers that support " + "virtual threads doing timed operations."; + } + static const char* impact() { return "Low"; } + virtual void execute(DCmdSource source, TRAPS); +}; + +class VThreadPollersDCmd : public DCmd { +public: + VThreadPollersDCmd(outputStream* output, bool heap) : DCmd(output, heap) { } + static const char* name() { + return "Thread.vthread_pollers"; + } + static const char* description() { + return "Print the I/O pollers that support virtual threads doing blocking network I/O operations."; + } + static const char* impact() { return "Low"; } + virtual void execute(DCmdSource source, TRAPS); +}; + class CompilationMemoryStatisticDCmd: public DCmdWithParser { protected: DCmdArgument _human_readable; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index e5e8d4df27a..11c77d48bf0 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -55,6 +55,7 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2304,6 +2305,10 @@ public Executor virtualThreadDefaultScheduler() { return VirtualThread.defaultScheduler(); } + public Stream virtualThreadDelayedTaskSchedulers() { + return VirtualThread.delayedTaskSchedulers(); + } + public StackWalker newStackWalkerInstance(Set options, ContinuationScope contScope, Continuation continuation) { diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 1f8e1941c1d..dc8c6a852d0 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -24,6 +24,7 @@ */ package java.lang; +import java.util.Arrays; import java.util.Locale; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -32,12 +33,12 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import jdk.internal.event.VirtualThreadEndEvent; import jdk.internal.event.VirtualThreadStartEvent; import jdk.internal.event.VirtualThreadSubmitFailedEvent; @@ -192,6 +193,13 @@ static Executor defaultScheduler() { return DEFAULT_SCHEDULER; } + /** + * Returns a stream of the delayed task schedulers used to support timed operations. + */ + static Stream delayedTaskSchedulers() { + return Arrays.stream(DELAYED_TASK_SCHEDULERS); + } + /** * Returns the continuation scope used for virtual threads. */ diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 62ce8020d80..f4ba247caf1 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -45,6 +45,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; import java.util.stream.Stream; import jdk.internal.loader.NativeLibraries; @@ -595,6 +596,11 @@ public interface JavaLangAccess { */ Executor virtualThreadDefaultScheduler(); + /** + * Returns a stream of the delayed task schedulers used for virtual threads. + */ + Stream virtualThreadDelayedTaskSchedulers(); + /** * Creates a new StackWalker */ diff --git a/src/java.base/share/classes/jdk/internal/vm/JcmdVThreadCommands.java b/src/java.base/share/classes/jdk/internal/vm/JcmdVThreadCommands.java new file mode 100644 index 00000000000..66ac6c2aca9 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/JcmdVThreadCommands.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package jdk.internal.vm; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.IntStream; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; +import sun.nio.ch.Poller; + +/** + * The implementation for the jcmd Thread.vthread_* diagnostic commands. These methods are + * called from the "Attach Listener" thread. + */ +public class JcmdVThreadCommands { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + private JcmdVThreadCommands() { } + + /** + * Invoked by the VM to print the virtual scheduler to a byte[]. + */ + private static byte[] printScheduler() { + StringBuilder sb = new StringBuilder(); + + // virtual thread scheduler + sb.append(JLA.virtualThreadDefaultScheduler()) + .append(System.lineSeparator()); + + // break + sb.append(System.lineSeparator()); + + // delayed task schedulers + sb.append("Delayed task schedulers:").append(System.lineSeparator()); + var delayedTaskSchedulers = JLA.virtualThreadDelayedTaskSchedulers().toList(); + IntStream.range(0, delayedTaskSchedulers.size()) + .forEach(i -> sb.append('[') + .append(i) + .append("] ") + .append(delayedTaskSchedulers.get(i)) + .append(System.lineSeparator())); + + return sb.toString().getBytes(StandardCharsets.UTF_8); + } + + /** + * Invoked by the VM to print the I/O pollers to a byte[]. + */ + private static byte[] printPollers() { + StringBuilder sb = new StringBuilder(); + + Poller masterPoller = Poller.masterPoller(); + List readPollers = Poller.readPollers(); + List writePollers = Poller.writePollers(); + + if (masterPoller != null) { + sb.append("Master I/O poller:") + .append(System.lineSeparator()) + .append(masterPoller) + .append(System.lineSeparator()); + + // break + sb.append(System.lineSeparator()); + } + + sb.append("Read I/O pollers:"); + sb.append(System.lineSeparator()); + IntStream.range(0, readPollers.size()) + .forEach(i -> sb.append('[') + .append(i) + .append("] ") + .append(readPollers.get(i)) + .append(System.lineSeparator())); + + // break + sb.append(System.lineSeparator()); + + sb.append("Write I/O pollers:"); + sb.append(System.lineSeparator()); + IntStream.range(0, writePollers.size()) + .forEach(i -> sb.append('[') + .append(i) + .append("] ") + .append(writePollers.get(i)) + .append(System.lineSeparator())); + + return sb.toString().getBytes(StandardCharsets.UTF_8); + } +} diff --git a/src/java.base/share/classes/sun/nio/ch/Poller.java b/src/java.base/share/classes/sun/nio/ch/Poller.java index d25dfec7f42..4a2cb4d8fdf 100644 --- a/src/java.base/share/classes/sun/nio/ch/Poller.java +++ b/src/java.base/share/classes/sun/nio/ch/Poller.java @@ -36,6 +36,7 @@ import java.util.concurrent.locks.LockSupport; import java.util.function.BooleanSupplier; import jdk.internal.misc.InnocuousThread; +import jdk.internal.vm.annotation.Stable; /** * Polls file descriptors. Virtual threads invoke the poll method to park @@ -53,6 +54,9 @@ public abstract class Poller { } } + // the poller or sub-poller thread + private @Stable Thread owner; + // maps file descriptors to parked Thread private final Map map = new ConcurrentHashMap<>(); @@ -238,6 +242,7 @@ private void wakeup(int fdVal) { * descriptor that is polled. */ private void pollerLoop() { + owner = Thread.currentThread(); try { for (;;) { poll(-1); @@ -258,6 +263,7 @@ private void pollerLoop() { */ private void subPollerLoop(Poller masterPoller) { assert Thread.currentThread().isVirtual(); + owner = Thread.currentThread(); try { int polled = 0; for (;;) { @@ -282,7 +288,8 @@ public int registered() { @Override public String toString() { - return Objects.toIdentityString(this) + " [registered = " + registered() + "]"; + return String.format("%s [registered = %d, owner = %s]", + Objects.toIdentityString(this), registered(), owner); } /** @@ -442,4 +449,25 @@ private void startPlatformThread(String name, Runnable task) { } } } + + /** + * Return the master poller or null if there is no master poller. + */ + public static Poller masterPoller() { + return POLLERS.masterPoller(); + } + + /** + * Return the list of read pollers. + */ + public static List readPollers() { + return POLLERS.readPollers(); + } + + /** + * Return the list of write pollers. + */ + public static List writePollers() { + return POLLERS.writePollers(); + } } diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index 2d2e08bc9f5..4e67e7a4502 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -737,6 +737,17 @@ The following commands are available: - `-e`: (Optional) Print extended thread information (BOOLEAN, false) - `-l`: (Optional) Prints `java.util.concurrent` locks (BOOLEAN, false) +`Thread.vthread_scheduler` +: Print the virtual thread scheduler, and the delayed task schedulers that support + virtual threads doing timed operations. + + Impact: Low + +`Thread.vthread_pollers` +: Print the I/O pollers that support virtual threads doing blocking network I/O operations. + + Impact: Low + `VM.cds` \[*arguments*\] : Dump a static or dynamic shared archive that includes all currently loaded classes. diff --git a/test/failure_handler/src/share/conf/common.properties b/test/failure_handler/src/share/conf/common.properties index dd51dc1add9..5cd2c1c13ca 100644 --- a/test/failure_handler/src/share/conf/common.properties +++ b/test/failure_handler/src/share/conf/common.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 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 @@ -34,7 +34,7 @@ onTimeout=\ jcmd.vm.classloader_stats jcmd.vm.stringtable \ jcmd.vm.symboltable jcmd.vm.uptime jcmd.vm.dynlibs \ jcmd.vm.system_properties jcmd.vm.info \ - jcmd.gc.heap_info jcmd.gc.class_histogram jcmd.gc.finalizer_info jcmd.thread.dump_to_file \ + jcmd.gc.heap_info jcmd.gc.class_histogram jcmd.gc.finalizer_info jcmd.thread.dump_to_file jcmd.thread.vthread_scheduler \ jstack jhsdb.jstack.live.default jhsdb.jstack.live.mixed jinfo.app=jinfo @@ -61,6 +61,8 @@ jcmd.thread.dump_to_file.args=%p Thread.dump_to_file -format=json JavaThread.dum jcmd.thread.dump_to_file.params.repeat=6 jcmd.thread.dump_to_file.params.successArtifacts=JavaThread.dump.%p.%iterCount +jcmd.thread.vthread_scheduler.args=%p Thread.vthread_scheduler + jstack.app=jstack jstack.args=-e -l %p jstack.params.repeat=6 diff --git a/test/hotspot/jtreg/serviceability/dcmd/thread/VThreadCommandsTest.java b/test/hotspot/jtreg/serviceability/dcmd/thread/VThreadCommandsTest.java new file mode 100644 index 00000000000..de2b7249612 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/thread/VThreadCommandsTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2023, 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 8337199 + * @summary Basic test for jcmd Thread.vthread_scheduler and Thread.vthread_pollers + * @requires vm.continuations + * @modules jdk.jcmd + * @library /test/lib + * @run junit/othervm VThreadCommandsTest + */ + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.lang.management.ManagementFactory; +import jdk.management.VirtualThreadSchedulerMXBean; + +import jdk.test.lib.dcmd.PidJcmdExecutor; +import jdk.test.lib.process.OutputAnalyzer; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class VThreadCommandsTest { + + /** + * Thread.vthread_scheduler + */ + @Test + void testVThreadScheduler() { + // ensure default scheduler and timeout schedulers are initialized + Thread.startVirtualThread(() -> { }); + + jcmd("Thread.vthread_scheduler") + .shouldContain(Objects.toIdentityString(defaultScheduler())) + .shouldContain("Delayed task schedulers:") + .shouldContain("[0] " + ScheduledThreadPoolExecutor.class.getName()); + } + + /** + * Thread.vthread_pollers + */ + @Test + void testVThreadPollers() throws Exception { + // do blocking I/O op on a virtual thread to ensure poller mechanism is initialized + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + executor.submit(() -> { + try (var listener = new ServerSocket()) { + InetAddress lb = InetAddress.getLoopbackAddress(); + listener.bind(new InetSocketAddress(lb, 0)); + listener.setSoTimeout(200); + try (Socket s = listener.accept()) { + System.err.format("Connection from %s ??%n", s.getRemoteSocketAddress()); + } catch (SocketTimeoutException e) { + // expected + } + } + return null; + }).get(); + } + + jcmd("Thread.vthread_pollers") + .shouldContain("Read I/O pollers:") + .shouldContain("Write I/O pollers:") + .shouldMatch("^\\[0\\] sun\\.nio\\.ch\\..+ \\[registered = [\\d]+, owner = .+\\]$"); + } + + private OutputAnalyzer jcmd(String cmd) { + return new PidJcmdExecutor().execute(cmd); + } + + /** + * Returns the virtual thread default scheduler. This implementation works by finding + * all FJ worker threads and mapping them to their pool. VirtualThreadSchedulerMXBean + * is used to temporarily changing target parallelism to an "unique" value, make it + * possbile to find the right pool. + */ + private ForkJoinPool defaultScheduler() { + var done = new AtomicBoolean(); + Thread vthread = Thread.startVirtualThread(() -> { + while (!done.get()) { + Thread.onSpinWait(); + } + }); + var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class); + int parallelism = bean.getParallelism(); + try { + bean.setParallelism(133); + return Thread.getAllStackTraces() + .keySet() + .stream() + .filter(ForkJoinWorkerThread.class::isInstance) + .map(t -> ((ForkJoinWorkerThread) t).getPool()) + .filter(p -> p.getParallelism() == 133) + .findAny() + .orElseThrow(); + } finally { + bean.setParallelism(parallelism); + done.set(true); + } + } +} From 659f70b37079ea2a54ebaaad5f47ce9600982d8d Mon Sep 17 00:00:00 2001 From: Andrey Turbanov Date: Tue, 3 Dec 2024 07:26:01 +0000 Subject: [PATCH 069/171] 8343418: Unnecessary Hashtable usage in CSS.htmlAttrToCssAttrMap Reviewed-by: honkar, aivanov --- .../classes/javax/swing/text/html/CSS.java | 101 +++++++++--------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index 8239565a53f..ef422a8d4e9 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Map; import java.util.Objects; import javax.swing.ImageIcon; @@ -1089,7 +1090,7 @@ private static int getTableBorder(AttributeSet tableAttr) { * Therefore, the value associated with each HTML.Attribute. * key ends up being an array of CSS.Attribute.* objects. */ - private static final Hashtable htmlAttrToCssAttrMap = new Hashtable(20); + private static final Map htmlAttrToCssAttrMap; /** * The hashtable and static initialization that follows sets @@ -1113,53 +1114,57 @@ private static int getTableBorder(AttributeSet tableAttr) { valueMap.put(Value.allValues[i].toString(), Value.allValues[i]); } - - htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR, - new CSS.Attribute[]{CSS.Attribute.COLOR}); - htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT, - new CSS.Attribute[]{CSS.Attribute.COLOR}); - htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR, - new CSS.Attribute[]{CSS.Attribute.CLEAR}); - htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND, - new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE}); - htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR, - new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR}); - htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH, - new CSS.Attribute[]{CSS.Attribute.WIDTH}); - htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT, - new CSS.Attribute[]{CSS.Attribute.HEIGHT}); - htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER, - new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, CSS.Attribute.BORDER_RIGHT_WIDTH, CSS.Attribute.BORDER_BOTTOM_WIDTH, CSS.Attribute.BORDER_LEFT_WIDTH}); - htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING, - new CSS.Attribute[]{CSS.Attribute.PADDING}); - htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING, - new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING}); - htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH, - new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT, - CSS.Attribute.MARGIN_RIGHT}); - htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT, - new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP, - CSS.Attribute.MARGIN_BOTTOM}); - htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE, - new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT, - CSS.Attribute.PADDING_RIGHT}); - htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE, - new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM, - CSS.Attribute.PADDING_TOP}); - htmlAttrToCssAttrMap.put(HTML.Attribute.FACE, - new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY}); - htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE, - new CSS.Attribute[]{CSS.Attribute.FONT_SIZE}); - htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN, - new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN}); - htmlAttrToCssAttrMap.put(HTML.Attribute.ALIGN, - new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN, - CSS.Attribute.TEXT_ALIGN, - CSS.Attribute.FLOAT}); - htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE, - new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE}); - htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP, - new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE}); + htmlAttrToCssAttrMap = Map.ofEntries( + Map.entry(HTML.Attribute.COLOR, + new CSS.Attribute[]{CSS.Attribute.COLOR}), + Map.entry(HTML.Attribute.TEXT, + new CSS.Attribute[]{CSS.Attribute.COLOR}), + Map.entry(HTML.Attribute.CLEAR, + new CSS.Attribute[]{CSS.Attribute.CLEAR}), + Map.entry(HTML.Attribute.BACKGROUND, + new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE}), + Map.entry(HTML.Attribute.BGCOLOR, + new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR}), + Map.entry(HTML.Attribute.WIDTH, + new CSS.Attribute[]{CSS.Attribute.WIDTH}), + Map.entry(HTML.Attribute.HEIGHT, + new CSS.Attribute[]{CSS.Attribute.HEIGHT}), + Map.entry(HTML.Attribute.BORDER, + new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, + CSS.Attribute.BORDER_RIGHT_WIDTH, + CSS.Attribute.BORDER_BOTTOM_WIDTH, + CSS.Attribute.BORDER_LEFT_WIDTH}), + Map.entry(HTML.Attribute.CELLPADDING, + new CSS.Attribute[]{CSS.Attribute.PADDING}), + Map.entry(HTML.Attribute.CELLSPACING, + new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING}), + Map.entry(HTML.Attribute.MARGINWIDTH, + new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT, + CSS.Attribute.MARGIN_RIGHT}), + Map.entry(HTML.Attribute.MARGINHEIGHT, + new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP, + CSS.Attribute.MARGIN_BOTTOM}), + Map.entry(HTML.Attribute.HSPACE, + new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT, + CSS.Attribute.PADDING_RIGHT}), + Map.entry(HTML.Attribute.VSPACE, + new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM, + CSS.Attribute.PADDING_TOP}), + Map.entry(HTML.Attribute.FACE, + new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY}), + Map.entry(HTML.Attribute.SIZE, + new CSS.Attribute[]{CSS.Attribute.FONT_SIZE}), + Map.entry(HTML.Attribute.VALIGN, + new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN}), + Map.entry(HTML.Attribute.ALIGN, + new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN, + CSS.Attribute.TEXT_ALIGN, + CSS.Attribute.FLOAT}), + Map.entry(HTML.Attribute.TYPE, + new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE}), + Map.entry(HTML.Attribute.NOWRAP, + new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE}) + ); // initialize StyleConstants mapping styleConstantToCssMap.put(StyleConstants.FontFamily, From 8dada7373fbe195abcc8b2ea7f876f3df6fee821 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Tue, 3 Dec 2024 08:28:04 +0000 Subject: [PATCH 070/171] 8345120: A likely bug in StringSupport::chunkedStrlenShort Reviewed-by: mcimadamore --- .../foreign/AbstractMemorySegmentImpl.java | 4 + .../foreign/SegmentBulkOperations.java | 62 ++-- .../jdk/internal/foreign/StringSupport.java | 318 +++++++++--------- test/jdk/java/foreign/TestStringEncoding.java | 59 +++- .../java/lang/foreign/InternalStrLen.java | 33 +- 5 files changed, 271 insertions(+), 205 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index 97417efaa8c..a52881b047b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -889,23 +889,27 @@ public void setAtIndex(AddressLayout layout, long index, MemorySegment value) { layout.varHandle().set((MemorySegment)this, index * layout.byteSize(), value); } + @ForceInline @Override public String getString(long offset) { return getString(offset, sun.nio.cs.UTF_8.INSTANCE); } + @ForceInline @Override public String getString(long offset, Charset charset) { Objects.requireNonNull(charset); return StringSupport.read(this, offset, charset); } + @ForceInline @Override public void setString(long offset, String str) { Objects.requireNonNull(str); setString(offset, str, sun.nio.cs.UTF_8.INSTANCE); } + @ForceInline @Override public void setString(long offset, String str, Charset charset) { Objects.requireNonNull(charset); diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java index d928f8fc425..068db1bf593 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentBulkOperations.java @@ -28,7 +28,6 @@ import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.util.Architecture; import jdk.internal.util.ArraysSupport; -import jdk.internal.util.ByteArrayLittleEndian; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; @@ -50,6 +49,7 @@ public final class SegmentBulkOperations { private SegmentBulkOperations() {} private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + private static final long LONG_MASK = ~7L; // The last three bits are zero // All the threshold values below MUST be a power of two and should preferably be // greater or equal to 2^3. @@ -75,21 +75,21 @@ public static MemorySegment fill(AbstractMemorySegmentImpl dst, byte value) { int offset = 0; // 0...0X...X000 final int limit = (int) (dst.length & (NATIVE_THRESHOLD_FILL - 8)); - for (; offset < limit; offset += 8) { + for (; offset < limit; offset += Long.BYTES) { SCOPED_MEMORY_ACCESS.putLongUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + offset, longValue, !Architecture.isLittleEndian()); } int remaining = (int) dst.length - limit; // 0...0X00 - if (remaining >= 4) { + if (remaining >= Integer.BYTES) { SCOPED_MEMORY_ACCESS.putIntUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + offset, (int) longValue, !Architecture.isLittleEndian()); - offset += 4; - remaining -= 4; + offset += Integer.BYTES; + remaining -= Integer.BYTES; } // 0...00X0 - if (remaining >= 2) { + if (remaining >= Short.BYTES) { SCOPED_MEMORY_ACCESS.putShortUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + offset, (short) longValue, !Architecture.isLittleEndian()); - offset += 2; - remaining -= 2; + offset += Short.BYTES; + remaining -= Short.BYTES; } // 0...000X if (remaining == 1) { @@ -123,26 +123,26 @@ public static void copy(AbstractMemorySegmentImpl src, long srcOffset, // is an overlap, we could tolerate one particular direction of overlap (but not the other). // 0...0X...X000 - final int limit = (int) (size & (NATIVE_THRESHOLD_COPY - 8)); + final int limit = (int) (size & (NATIVE_THRESHOLD_COPY - Long.BYTES)); int offset = 0; - for (; offset < limit; offset += 8) { + for (; offset < limit; offset += Long.BYTES) { final long v = SCOPED_MEMORY_ACCESS.getLongUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcOffset + offset, !Architecture.isLittleEndian()); SCOPED_MEMORY_ACCESS.putLongUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstOffset + offset, v, !Architecture.isLittleEndian()); } int remaining = (int) size - offset; // 0...0X00 - if (remaining >= 4) { + if (remaining >= Integer.BYTES) { final int v = SCOPED_MEMORY_ACCESS.getIntUnaligned(src.sessionImpl(), src.unsafeGetBase(),src.unsafeGetOffset() + srcOffset + offset, !Architecture.isLittleEndian()); SCOPED_MEMORY_ACCESS.putIntUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstOffset + offset, v, !Architecture.isLittleEndian()); - offset += 4; - remaining -= 4; + offset += Integer.BYTES; + remaining -= Integer.BYTES; } // 0...00X0 - if (remaining >= 2) { + if (remaining >= Short.BYTES) { final short v = SCOPED_MEMORY_ACCESS.getShortUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcOffset + offset, !Architecture.isLittleEndian()); SCOPED_MEMORY_ACCESS.putShortUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstOffset + offset, v, !Architecture.isLittleEndian()); - offset += 2; - remaining -=2; + offset += Short.BYTES; + remaining -= Short.BYTES; } // 0...000X if (remaining == 1) { @@ -202,9 +202,9 @@ public static int contentHash(AbstractMemorySegmentImpl segment, long fromOffset return 1; } int result = 1; - final long longBytes = length & ((1L << 62) - 8); + final long longBytes = length & LONG_MASK; final long limit = fromOffset + longBytes; - for (; fromOffset < limit; fromOffset += 8) { + for (; fromOffset < limit; fromOffset += Long.BYTES) { long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset, !Architecture.isLittleEndian()); result = result * POWERS_OF_31[7] + ((byte) (val >>> 56)) * POWERS_OF_31[6] @@ -218,24 +218,24 @@ public static int contentHash(AbstractMemorySegmentImpl segment, long fromOffset } int remaining = (int) (length - longBytes); // 0...0X00 - if (remaining >= 4) { + if (remaining >= Integer.BYTES) { int val = SCOPED_MEMORY_ACCESS.getIntUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset, !Architecture.isLittleEndian()); result = result * POWERS_OF_31[3] + ((byte) (val >>> 24)) * POWERS_OF_31[2] + ((byte) (val >>> 16)) * POWERS_OF_31[1] + ((byte) (val >>> 8)) * POWERS_OF_31[0] + ((byte) val); - fromOffset += 4; - remaining -= 4; + fromOffset += Integer.BYTES; + remaining -= Integer.BYTES; } // 0...00X0 - if (remaining >= 2) { + if (remaining >= Short.BYTES) { short val = SCOPED_MEMORY_ACCESS.getShortUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset, !Architecture.isLittleEndian()); result = result * POWERS_OF_31[1] + ((byte) (val >>> 8)) * POWERS_OF_31[0] + ((byte) val); - fromOffset += 2; - remaining -= 2; + fromOffset += Short.BYTES; + remaining -= Short.BYTES; } // 0...000X if (remaining == 1) { @@ -288,7 +288,7 @@ private static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, long start, int length, boolean srcAndDstBytesDiffer) { int offset = 0; final int limit = length & (NATIVE_THRESHOLD_MISMATCH - 8); - for (; offset < limit; offset += 8) { + for (; offset < limit; offset += Long.BYTES) { final long s = SCOPED_MEMORY_ACCESS.getLongUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, false); final long d = SCOPED_MEMORY_ACCESS.getLongUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, false); if (s != d) { @@ -298,24 +298,24 @@ private static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, int remaining = length - offset; // 0...0X00 - if (remaining >= 4) { + if (remaining >= Integer.BYTES) { final int s = SCOPED_MEMORY_ACCESS.getIntUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, false); final int d = SCOPED_MEMORY_ACCESS.getIntUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, false); if (s != d) { return start + offset + mismatch(s, d); } - offset += 4; - remaining -= 4; + offset += Integer.BYTES; + remaining -= Integer.BYTES; } // 0...00X0 - if (remaining >= 2) { + if (remaining >= Short.BYTES) { final short s = SCOPED_MEMORY_ACCESS.getShortUnaligned(src.sessionImpl(), src.unsafeGetBase(), src.unsafeGetOffset() + srcFromOffset + offset, false); final short d = SCOPED_MEMORY_ACCESS.getShortUnaligned(dst.sessionImpl(), dst.unsafeGetBase(), dst.unsafeGetOffset() + dstFromOffset + offset, false); if (s != d) { return start + offset + mismatch(s, d); } - offset += 2; - remaining -= 2; + offset += Short.BYTES; + remaining -= Short.BYTES; } // 0...000X if (remaining == 1) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java index 78550c56136..8f182f3b338 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java +++ b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java @@ -27,8 +27,10 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.util.Architecture; import jdk.internal.util.ArraysSupport; +import jdk.internal.vm.annotation.ForceInline; import java.lang.foreign.MemorySegment; import java.nio.charset.Charset; @@ -40,11 +42,14 @@ */ public final class StringSupport { - static final JavaLangAccess JAVA_LANG_ACCESS = SharedSecrets.getJavaLangAccess(); + private static final JavaLangAccess JAVA_LANG_ACCESS = SharedSecrets.getJavaLangAccess(); + private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + private static final long LONG_MASK = ~7L; // The last three bits are zero private StringSupport() {} - public static String read(MemorySegment segment, long offset, Charset charset) { + @ForceInline + public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset) { return switch (CharsetKind.of(charset)) { case SINGLE_BYTE -> readByte(segment, offset, charset); case DOUBLE_BYTE -> readShort(segment, offset, charset); @@ -52,7 +57,8 @@ public static String read(MemorySegment segment, long offset, Charset charset) { }; } - public static void write(MemorySegment segment, long offset, Charset charset, String string) { + @ForceInline + public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) { switch (CharsetKind.of(charset)) { case SINGLE_BYTE -> writeByte(segment, offset, charset, string); case DOUBLE_BYTE -> writeShort(segment, offset, charset, string); @@ -60,111 +66,183 @@ public static void write(MemorySegment segment, long offset, Charset charset, St } } - private static String readByte(MemorySegment segment, long offset, Charset charset) { - long len = chunkedStrlenByte(segment, offset); - byte[] bytes = new byte[(int)len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len); + @ForceInline + private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) { + final int len = strlenByte(segment, offset, segment.byteSize()); + final byte[] bytes = new byte[len]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); return new String(bytes, charset); } - private static void writeByte(MemorySegment segment, long offset, Charset charset, String string) { + @ForceInline + private static void writeByte(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) { int bytes = copyBytes(string, segment, charset, offset); segment.set(JAVA_BYTE, offset + bytes, (byte)0); } - private static String readShort(MemorySegment segment, long offset, Charset charset) { - long len = chunkedStrlenShort(segment, offset); - byte[] bytes = new byte[(int)len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len); + @ForceInline + private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) { + int len = strlenShort(segment, offset, segment.byteSize()); + byte[] bytes = new byte[len]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); return new String(bytes, charset); } - private static void writeShort(MemorySegment segment, long offset, Charset charset, String string) { + @ForceInline + private static void writeShort(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) { int bytes = copyBytes(string, segment, charset, offset); segment.set(JAVA_SHORT_UNALIGNED, offset + bytes, (short)0); } - private static String readInt(MemorySegment segment, long offset, Charset charset) { - long len = strlenInt(segment, offset); - byte[] bytes = new byte[(int)len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len); + @ForceInline + private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) { + int len = strlenInt(segment, offset, segment.byteSize()); + byte[] bytes = new byte[len]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); return new String(bytes, charset); } - private static void writeInt(MemorySegment segment, long offset, Charset charset, String string) { + @ForceInline + private static void writeInt(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) { int bytes = copyBytes(string, segment, charset, offset); segment.set(JAVA_INT_UNALIGNED, offset + bytes, 0); } /** - * {@return the shortest distance beginning at the provided {@code start} - * to the encountering of a zero byte in the provided {@code segment}} + * {@return the index of the first zero byte beginning at the provided + * {@code fromOffset} to the encountering of a zero byte in the provided + * {@code segment} checking bytes before the {@code toOffset}} *

      - * The method divides the region of interest into three distinct regions: - *

        - *
      • head (access made on a byte-by-byte basis) (if any)
      • - *
      • body (access made with eight bytes at a time at physically 64-bit-aligned memory) (if any)
      • - *
      • tail (access made on a byte-by-byte basis) (if any)
      • - *
      - *

      - * The body is using a heuristic method to determine if a long word - * contains a zero byte. The method might have false positives but - * never false negatives. + * The method is using a heuristic method to determine if a long word contains a + * zero byte. The method might have false positives but never false negatives. *

      * This method is inspired by the `glibc/string/strlen.c` implementation * - * @param segment to examine - * @param start from where examination shall begin + * @param segment to examine + * @param fromOffset from where examination shall begin (inclusive) + * @param toOffset to where examination shall end (exclusive) * @throws IllegalArgumentException if the examined region contains no zero bytes * within a length that can be accepted by a String */ - public static int chunkedStrlenByte(MemorySegment segment, long start) { - - // Handle the first unaligned "head" bytes separately - int headCount = (int)SharedUtils.remainsToAlignment(segment.address() + start, Long.BYTES); - - int offset = 0; - for (; offset < headCount; offset++) { - byte curr = segment.get(JAVA_BYTE, start + offset); - if (curr == 0) { - return offset; + @ForceInline + public static int strlenByte(final AbstractMemorySegmentImpl segment, + final long fromOffset, + final long toOffset) { + final long length = toOffset - fromOffset; + segment.checkBounds(fromOffset, length); + if (length == 0) { + // The state has to be checked explicitly for zero-length segments + segment.scope.checkValidState(); + throw nullNotFound(segment, fromOffset, toOffset); + } + final long longBytes = length & LONG_MASK; + final long longLimit = fromOffset + longBytes; + long offset = fromOffset; + for (; offset < longLimit; offset += Long.BYTES) { + long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian()); + if (mightContainZeroByte(val)) { + for (int j = 0; j < Long.BYTES; j++) { + if (SCOPED_MEMORY_ACCESS.getByte(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset + j) == 0) { + return requireWithinStringSize(offset + j - fromOffset, segment, fromOffset, toOffset); + } + } + } + } + // Handle the tail + for (; offset < toOffset; offset++) { + byte val = SCOPED_MEMORY_ACCESS.getByte(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset); + if (val == 0) { + return requireWithinStringSize(offset - fromOffset, segment, fromOffset, toOffset); } } + throw nullNotFound(segment, fromOffset, toOffset); + } - // We are now on a long-aligned boundary so this is the "body" - int bodyCount = bodyCount(segment.byteSize() - start - headCount); - - for (; offset < bodyCount; offset += Long.BYTES) { - // We know we are `long` aligned so, we can save on alignment checking here - long curr = segment.get(JAVA_LONG_UNALIGNED, start + offset); - // Is this a candidate? - if (mightContainZeroByte(curr)) { - for (int j = 0; j < 8; j++) { - if (segment.get(JAVA_BYTE, start + offset + j) == 0) { - return offset + j; + @ForceInline + public static int strlenShort(final AbstractMemorySegmentImpl segment, + final long fromOffset, + final long toOffset) { + final long length = toOffset - fromOffset; + segment.checkBounds(fromOffset, length); + if (length == 0) { + segment.scope.checkValidState(); + throw nullNotFound(segment, fromOffset, toOffset); + } + final long longBytes = length & LONG_MASK; + final long longLimit = fromOffset + longBytes; + long offset = fromOffset; + for (; offset < longLimit; offset += Long.BYTES) { + long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian()); + if (mightContainZeroShort(val)) { + for (int j = 0; j < Long.BYTES; j += Short.BYTES) { + if (SCOPED_MEMORY_ACCESS.getShortUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset + j, !Architecture.isLittleEndian()) == 0) { + return requireWithinStringSize(offset + j - fromOffset, segment, fromOffset, toOffset); } } } } + // Handle the tail + // Prevent over scanning as we step by 2 + final long endScan = toOffset & ~1; // The last bit is zero + for (; offset < endScan; offset += Short.BYTES) { + short val = SCOPED_MEMORY_ACCESS.getShortUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian()); + if (val == 0) { + return requireWithinStringSize(offset - fromOffset, segment, fromOffset, toOffset); + } + } + throw nullNotFound(segment, fromOffset, toOffset); + } - // Handle the "tail" - return requireWithinArraySize((long) offset + strlenByte(segment, start + offset)); + @ForceInline + public static int strlenInt(final AbstractMemorySegmentImpl segment, + final long fromOffset, + final long toOffset) { + final long length = toOffset - fromOffset; + segment.checkBounds(fromOffset, length); + if (length == 0) { + segment.scope.checkValidState(); + throw nullNotFound(segment, fromOffset, toOffset); + } + final long longBytes = length & LONG_MASK; + final long longLimit = fromOffset + longBytes; + long offset = fromOffset; + for (; offset < longLimit; offset += Long.BYTES) { + long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian()); + if (mightContainZeroInt(val)) { + for (int j = 0; j < Long.BYTES; j += Integer.BYTES) { + if (SCOPED_MEMORY_ACCESS.getIntUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset + j, !Architecture.isLittleEndian()) == 0) { + return requireWithinStringSize(offset + j - fromOffset, segment, fromOffset, toOffset); + } + } + } + } + // Handle the tail + // Prevent over scanning as we step by 4 + final long endScan = toOffset & ~3; // The last two bit are zero + for (; offset < endScan; offset += Integer.BYTES) { + int val = SCOPED_MEMORY_ACCESS.getIntUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + offset, !Architecture.isLittleEndian()); + if (val == 0) { + return requireWithinStringSize(offset - fromOffset, segment, fromOffset, toOffset); + } + } + throw nullNotFound(segment, fromOffset, toOffset); } - /* Bits 63 and N * 8 (N = 1..7) of this number are zero. Call these bits - the "holes". Note that there is a hole just to the left of - each byte, with an extra at the end: + /* + Bits 63 and N * 8 (N = 1..7) of this number are zero. Call these bits + the "holes". Note that there is a hole just to the left of + each byte, with an extra at the end: - bits: 01111110 11111110 11111110 11111110 11111110 11111110 11111110 11111111 - bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF GGGGGGGG HHHHHHHH + bits: 01111110 11111110 11111110 11111110 11111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF GGGGGGGG HHHHHHHH - The 1-bits make sure that carries propagate to the next 0-bit. - The 0-bits provide holes for carries to fall into. + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ private static final long HIMAGIC_FOR_BYTES = 0x8080_8080_8080_8080L; private static final long LOMAGIC_FOR_BYTES = 0x0101_0101_0101_0101L; - static boolean mightContainZeroByte(long l) { + private static boolean mightContainZeroByte(long l) { return ((l - LOMAGIC_FOR_BYTES) & (~l) & HIMAGIC_FOR_BYTES) != 0; } @@ -175,99 +253,40 @@ static boolean mightContainZeroShort(long l) { return ((l - LOMAGIC_FOR_SHORTS) & (~l) & HIMAGIC_FOR_SHORTS) != 0; } - static int requireWithinArraySize(long size) { - if (size > ArraysSupport.SOFT_MAX_ARRAY_LENGTH) { - throw newIaeStringTooLarge(); - } - return (int) size; - } - - static int bodyCount(long remaining) { - return (int) Math.min( - // Make sure we do not wrap around - Integer.MAX_VALUE - Long.BYTES, - // Remaining bytes to consider - remaining) - & -Long.BYTES; // Mask 0xFFFFFFF8 - } + private static final long HIMAGIC_FOR_INTS = 0x8000_0000_8000_0000L; + private static final long LOMAGIC_FOR_INTS = 0x0000_0001_0000_0001L; - private static int strlenByte(MemorySegment segment, long start) { - for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += 1) { - byte curr = segment.get(JAVA_BYTE, start + offset); - if (curr == 0) { - return offset; - } - } - throw newIaeStringTooLarge(); + static boolean mightContainZeroInt(long l) { + return ((l - LOMAGIC_FOR_INTS) & (~l) & HIMAGIC_FOR_INTS) != 0; } - /** - * {@return the shortest distance beginning at the provided {@code start} - * to the encountering of a zero short in the provided {@code segment}} - *

      - * Note: The inspected region must be short aligned. - * - * @see #chunkedStrlenByte(MemorySegment, long) for more information - * - * @param segment to examine - * @param start from where examination shall begin - * @throws IllegalArgumentException if the examined region contains no zero shorts - * within a length that can be accepted by a String - */ - public static int chunkedStrlenShort(MemorySegment segment, long start) { - - // Handle the first unaligned "head" bytes separately - int headCount = (int)SharedUtils.remainsToAlignment(segment.address() + start, Long.BYTES); - int offset = 0; - for (; offset < headCount; offset += Short.BYTES) { - short curr = segment.get(JAVA_SHORT_UNALIGNED, start + offset); - if (curr == 0) { - return offset; - } - } - - // We are now on a long-aligned boundary so this is the "body" - int bodyCount = bodyCount(segment.byteSize() - start - headCount); - - for (; offset < bodyCount; offset += Long.BYTES) { - // We know we are `long` aligned so, we can save on alignment checking here - long curr = segment.get(JAVA_LONG_UNALIGNED, start + offset); - // Is this a candidate? - if (mightContainZeroShort(curr)) { - for (int j = 0; j < Long.BYTES; j += Short.BYTES) { - if (segment.get(JAVA_SHORT_UNALIGNED, start + offset + j) == 0) { - return offset + j; - } - } - } + private static int requireWithinStringSize(long size, + AbstractMemorySegmentImpl segment, + long fromOffset, + long toOffset) { + if (size > ArraysSupport.SOFT_MAX_ARRAY_LENGTH) { + throw stringTooLarge(segment, fromOffset, toOffset); } + return (int) size; + } - // Handle the "tail" - return requireWithinArraySize((long) offset + strlenShort(segment, start + offset)); + private static IllegalArgumentException stringTooLarge(AbstractMemorySegmentImpl segment, + long fromOffset, + long toOffset) { + return new IllegalArgumentException("String too large: " + exceptionInfo(segment, fromOffset, toOffset)); } - private static int strlenShort(MemorySegment segment, long start) { - for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += Short.BYTES) { - short curr = segment.get(JAVA_SHORT_UNALIGNED, start + offset); - if (curr == (short)0) { - return offset; - } - } - throw newIaeStringTooLarge(); + private static IndexOutOfBoundsException nullNotFound(AbstractMemorySegmentImpl segment, + long fromOffset, + long toOffset) { + return new IndexOutOfBoundsException("No null terminator found: " + exceptionInfo(segment, fromOffset, toOffset)); } - // The gain of using `long` wide operations for `int` is lower than for the two other `byte` and `short` variants - // so, there is only one method for ints. - public static int strlenInt(MemorySegment segment, long start) { - for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += Integer.BYTES) { - // We are guaranteed to be aligned here so, we can use unaligned access. - int curr = segment.get(JAVA_INT_UNALIGNED, start + offset); - if (curr == 0) { - return offset; - } - } - throw newIaeStringTooLarge(); + private static String exceptionInfo(AbstractMemorySegmentImpl segment, + long fromOffset, + long toOffset) { + return segment + " using region [" + fromOffset + ", " + toOffset + ")"; } public enum CharsetKind { @@ -323,9 +342,4 @@ public static int copyBytes(String string, MemorySegment segment, Charset charse public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) { JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset); } - - private static IllegalArgumentException newIaeStringTooLarge() { - return new IllegalArgumentException("String too large"); - } - } diff --git a/test/jdk/java/foreign/TestStringEncoding.java b/test/jdk/java/foreign/TestStringEncoding.java index 4caef6fbd09..94732943b9d 100644 --- a/test/jdk/java/foreign/TestStringEncoding.java +++ b/test/jdk/java/foreign/TestStringEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -39,6 +39,7 @@ import java.util.Random; import java.util.function.UnaryOperator; +import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.StringSupport; import org.testng.annotations.*; @@ -53,6 +54,20 @@ public class TestStringEncoding { + @Test + public void emptySegment() { + for (Charset charset : standardCharsets()) { + for (Arena arena : arenas()) { + try (arena) { + var segment = arena.allocate(0); + var e = expectThrows(IndexOutOfBoundsException.class, () -> + segment.getString(0, charset)); + assertTrue(e.getMessage().contains("No null terminator found")); + } + } + } + } + @Test(dataProvider = "strings") public void testStrings(String testString) { for (Charset charset : Charset.availableCharsets().values()) { @@ -87,7 +102,6 @@ public void testStrings(String testString) { } } - @Test(dataProvider = "strings") public void testStringsHeap(String testString) { for (Charset charset : singleByteCharsets()) { @@ -198,8 +212,9 @@ public void testOffset(String testString) { try (arena) { MemorySegment inSegment = arena.allocateFrom(testString, charset); for (int i = 0; i < 3; i++) { + String expected = testString.substring(i); String actual = inSegment.getString(i, charset); - assertEquals(actual, testString.substring(i)); + assertEquals(actual, expected); } } } @@ -249,6 +264,32 @@ public void segmentationFault() { } } + // This test ensures that we do not address outside the segment even though there + // are odd bytes at the end. + @Test(dataProvider = "strings") + public void offBoundaryTrailingBytes(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : standardCharsets()) { + for (var arena: arenas()) { + try (arena) { + MemorySegment strSegment = arena.allocateFrom(testString, charset); + // Add an odd byte at the end + MemorySegment inSegment = arena.allocate(strSegment.byteSize() + 1); + // Make sure there are no null-terminators so that we will try to scan + // the entire segment. + inSegment.fill((byte) 1); + for (int i = 0; i < 4; i++) { + final int offset = i; + var e = expectThrows(IndexOutOfBoundsException.class, () -> inSegment.getString(offset, charset)); + assertTrue(e.getMessage().contains("No null terminator found")); + } + } + } + } + } + private static final int TEST_LENGTH_MAX = 277; private Random deterministicRandom() { @@ -271,9 +312,15 @@ public void chunked_strlen_byte() { } segment.setAtIndex(JAVA_BYTE, len, (byte) 0); for (int j = 0; j < len; j++) { - int actual = StringSupport.chunkedStrlenByte(segment, j); + int actual = StringSupport.strlenByte((AbstractMemorySegmentImpl) segment, j, segment.byteSize()); assertEquals(actual, len - j); } + // Test end offset + for (int j = 0; j < len - 1; j++) { + final long toOffset = j; + expectThrows(IndexOutOfBoundsException.class, () -> + StringSupport.strlenByte((AbstractMemorySegmentImpl) segment, 0, toOffset)); + } } } } @@ -295,7 +342,7 @@ public void chunked_strlen_short() { } segment.setAtIndex(JAVA_SHORT, len, (short) 0); for (int j = 0; j < len; j++) { - int actual = StringSupport.chunkedStrlenShort(segment, j * Short.BYTES); + int actual = StringSupport.strlenShort((AbstractMemorySegmentImpl) segment, j * Short.BYTES, segment.byteSize()); assertEquals(actual, (len - j) * Short.BYTES); } } @@ -319,7 +366,7 @@ public void strlen_int() { } segment.setAtIndex(JAVA_INT, len, 0); for (int j = 0; j < len; j++) { - int actual = StringSupport.strlenInt(segment, j * Integer.BYTES); + int actual = StringSupport.strlenInt((AbstractMemorySegmentImpl) segment, j * Integer.BYTES, segment.byteSize()); assertEquals(actual, (len - j) * Integer.BYTES); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/InternalStrLen.java b/test/micro/org/openjdk/bench/java/lang/foreign/InternalStrLen.java index 2db15bfe265..b7867efd771 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/InternalStrLen.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/InternalStrLen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -22,6 +22,8 @@ */ package org.openjdk.bench.java.lang.foreign; +import jdk.internal.foreign.AbstractMemorySegmentImpl; +import jdk.internal.foreign.StringSupport; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -43,20 +45,20 @@ import java.util.stream.Stream; import static java.lang.foreign.ValueLayout.*; -import static jdk.internal.foreign.StringSupport.*; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(Scope.Benchmark) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(value = 3, jvmArgs = {"--add-exports=java.base/jdk.internal.foreign=ALL-UNNAMED", "--enable-native-access=ALL-UNNAMED", "--enable-preview"}) +@Fork(value = 3, jvmArgs = {"--add-exports=java.base/jdk.internal.foreign=ALL-UNNAMED", + "--enable-native-access=ALL-UNNAMED"}) public class InternalStrLen { - private MemorySegment singleByteSegment; - private MemorySegment singleByteSegmentMisaligned; - private MemorySegment doubleByteSegment; - private MemorySegment quadByteSegment; + private AbstractMemorySegmentImpl singleByteSegment; + private AbstractMemorySegmentImpl singleByteSegmentMisaligned; + private AbstractMemorySegmentImpl doubleByteSegment; + private AbstractMemorySegmentImpl quadByteSegment; @Param({"1", "4", "16", "251", "1024"}) int size; @@ -64,10 +66,9 @@ public class InternalStrLen { @Setup public void setup() { var arena = Arena.ofAuto(); - singleByteSegment = arena.allocate((size + 1L) * Byte.BYTES); - singleByteSegmentMisaligned = arena.allocate((size + 1L) * Byte.BYTES); - doubleByteSegment = arena.allocate((size + 1L) * Short.BYTES); - quadByteSegment = arena.allocate((size + 1L) * Integer.BYTES); + singleByteSegment = (AbstractMemorySegmentImpl) arena.allocate((size + 1L) * Byte.BYTES); + doubleByteSegment = (AbstractMemorySegmentImpl) arena.allocate((size + 1L) * Short.BYTES); + quadByteSegment = (AbstractMemorySegmentImpl) arena.allocate((size + 1L) * Integer.BYTES); Stream.of(singleByteSegment, doubleByteSegment, quadByteSegment) .forEach(s -> IntStream.range(0, (int) s.byteSize() - 1) .forEach(i -> s.set( @@ -78,7 +79,7 @@ public void setup() { singleByteSegment.set(ValueLayout.JAVA_BYTE, singleByteSegment.byteSize() - Byte.BYTES, (byte) 0); doubleByteSegment.set(ValueLayout.JAVA_SHORT, doubleByteSegment.byteSize() - Short.BYTES, (short) 0); quadByteSegment.set(ValueLayout.JAVA_INT, quadByteSegment.byteSize() - Integer.BYTES, 0); - singleByteSegmentMisaligned = arena.allocate(singleByteSegment.byteSize() + 1). + singleByteSegmentMisaligned = (AbstractMemorySegmentImpl) arena.allocate(singleByteSegment.byteSize() + 1). asSlice(1); MemorySegment.copy(singleByteSegment, 0, singleByteSegmentMisaligned, 0, singleByteSegment.byteSize()); } @@ -105,22 +106,22 @@ public int elementQuad() { @Benchmark public int chunkedSingle() { - return chunkedStrlenByte(singleByteSegment, 0); + return StringSupport.strlenByte(singleByteSegment, 0, singleByteSegment.byteSize()); } @Benchmark public int chunkedSingleMisaligned() { - return chunkedStrlenByte(singleByteSegmentMisaligned, 0); + return StringSupport.strlenByte(singleByteSegmentMisaligned, 0, singleByteSegment.byteSize()); } @Benchmark public int chunkedDouble() { - return chunkedStrlenShort(doubleByteSegment, 0); + return StringSupport.strlenShort(doubleByteSegment, 0, doubleByteSegment.byteSize()); } @Benchmark public int changedElementQuad() { - return strlenInt(quadByteSegment, 0); + return StringSupport.strlenInt(quadByteSegment, 0, quadByteSegment.byteSize()); } // These are the legacy methods From c330b90b9f43f80c322153585fa78704358f0224 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Tue, 3 Dec 2024 09:06:07 +0000 Subject: [PATCH 071/171] 8343780: Add since checker tests to the Tools area modules and add missing @since to jdk.jfr Reviewed-by: cstein, egahlin --- .../share/classes/jdk/jfr/Recording.java | 1 + .../JavaScriptingCheckSince.java | 30 +++++++++++++++++++ .../jdk.dynalink/JdkDynalinkCheckSince.java | 30 +++++++++++++++++++ .../jdk.jartool/JdkJartoolCheckSince.java | 30 +++++++++++++++++++ .../modules/jdk.jlink/JdkJlinkCheckSince.java | 30 +++++++++++++++++++ .../jdk.jsobject/JdkJsobjectCheckSince.java | 30 +++++++++++++++++++ 6 files changed, 151 insertions(+) create mode 100644 test/jdk/tools/sincechecker/modules/java.scripting/JavaScriptingCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.dynalink/JdkDynalinkCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.jartool/JdkJartoolCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.jlink/JdkJlinkCheckSince.java create mode 100644 test/jdk/tools/sincechecker/modules/jdk.jsobject/JdkJsobjectCheckSince.java diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java index f1dcadf8b68..6168ddb2832 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java @@ -97,6 +97,7 @@ public Map toMap() { * support, or if the file repository can't be created or accessed) * * @see jdk.jfr + * @since 11 */ public Recording(Map settings) { Objects.requireNonNull(settings, "settings"); diff --git a/test/jdk/tools/sincechecker/modules/java.scripting/JavaScriptingCheckSince.java b/test/jdk/tools/sincechecker/modules/java.scripting/JavaScriptingCheckSince.java new file mode 100644 index 00000000000..06458b07907 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java.scripting/JavaScriptingCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343780 + * @summary Test for `@since` in java.scripting module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker java.scripting + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.dynalink/JdkDynalinkCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.dynalink/JdkDynalinkCheckSince.java new file mode 100644 index 00000000000..5d38713eb0a --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.dynalink/JdkDynalinkCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343780 + * @summary Test for `@since` in jdk.dynalik module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.dynalink + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.jartool/JdkJartoolCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.jartool/JdkJartoolCheckSince.java new file mode 100644 index 00000000000..628aa8d2f34 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.jartool/JdkJartoolCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343780 + * @summary Test for `@since` in jdk.jartool module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.jartool + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.jlink/JdkJlinkCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.jlink/JdkJlinkCheckSince.java new file mode 100644 index 00000000000..effeb778a3a --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.jlink/JdkJlinkCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343780 + * @summary Test for `@since` in jdk.jlink module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.jlink + */ diff --git a/test/jdk/tools/sincechecker/modules/jdk.jsobject/JdkJsobjectCheckSince.java b/test/jdk/tools/sincechecker/modules/jdk.jsobject/JdkJsobjectCheckSince.java new file mode 100644 index 00000000000..705c7259b71 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/jdk.jsobject/JdkJsobjectCheckSince.java @@ -0,0 +1,30 @@ +/* + * 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 8343780 + * @summary Test for `@since` in jdk.jsobject module + * @library /test/lib /test/jdk/tools/sincechecker + * @run main SinceChecker jdk.jsobject + */ From ec93cc50988c4bd58bf599e007d09824702720b2 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 3 Dec 2024 09:18:58 +0000 Subject: [PATCH 072/171] 8343932: Error when parsing qualified generic type test pattern in switch Reviewed-by: jlahoda --- .../sun/tools/javac/parser/JavacParser.java | 2 +- .../tools/javac/patterns/T8343932.java | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/patterns/T8343932.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index c55c2db98de..a02945dee3c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -3428,7 +3428,7 @@ PatternResult analyzePattern(int lookahead) { case GTGT: typeDepth--; case GT: typeDepth--; - if (typeDepth == 0) { + if (typeDepth == 0 && !peekToken(lookahead, DOT)) { return peekToken(lookahead, LAX_IDENTIFIER) || peekToken(lookahead, tk -> tk == LPAREN) ? PatternResult.PATTERN : PatternResult.EXPRESSION; diff --git a/test/langtools/tools/javac/patterns/T8343932.java b/test/langtools/tools/javac/patterns/T8343932.java new file mode 100644 index 00000000000..346d0d6f79c --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8343932.java @@ -0,0 +1,57 @@ +/* + * 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 8343932 + * @summary Error when parsing qualified generic type test pattern in switch + * @compile T8343932.java + */ +public class T8343932 { + abstract sealed class J permits X.S, A {} + final class A extends J {} + + public class X { + final class S extends J { + abstract sealed class J permits XX.SS, AA {} + final class AA extends J {} + + public class XX { + final class SS extends J {} + } + } + + static int test(J ji) { + return switch (ji) { + case A a -> 42; + case X.S e -> 4200; // level 1 + }; + } + + static int test(X.S.J ji) { + return switch (ji) { + case X.S.AA a -> 42; + case X.S.XX.SS e -> 4200; // level 2 + }; + } + } +} \ No newline at end of file From 077b8422bb5bf70fb6201b71911741e2aff9a520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Tue, 3 Dec 2024 09:53:37 +0000 Subject: [PATCH 073/171] 8345074: java.net.InterfaceAddress constructor could be made private Reviewed-by: jpai --- src/java.base/share/classes/java/net/InterfaceAddress.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/net/InterfaceAddress.java b/src/java.base/share/classes/java/net/InterfaceAddress.java index f5b76ec9f90..979ed9cc7f5 100644 --- a/src/java.base/share/classes/java/net/InterfaceAddress.java +++ b/src/java.base/share/classes/java/net/InterfaceAddress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -42,10 +42,9 @@ public class InterfaceAddress { private short maskLength = 0; /* - * Package private constructor. Can't be built directly, instances are - * obtained through the NetworkInterface class. + * This constructor is called via JNI in NetworkInterface.c */ - InterfaceAddress() { + private InterfaceAddress() { } /** From 63af2f42b7abe9504897d7c3f3b4cc0b57123694 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 3 Dec 2024 10:42:23 +0000 Subject: [PATCH 074/171] 8344414: ZGC: Another division by zero in rule_major_allocation_rate Reviewed-by: eosterlund, stefank --- src/hotspot/share/gc/z/zDirector.cpp | 33 +++++++++++++++++----------- src/hotspot/share/gc/z/zStat.cpp | 7 ++++-- src/hotspot/share/gc/z/zStat.hpp | 4 ++-- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index 3c0cb660206..48e426f068a 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -33,6 +33,8 @@ #include "gc/z/zStat.hpp" #include "logging/log.hpp" +#include + ZDirector* ZDirector::_director; constexpr double one_in_1000 = 3.290527; @@ -453,16 +455,22 @@ static double calculate_extra_young_gc_time(const ZDirectorStats& stats) { // relocation headroom into account to avoid in-place relocation. const size_t old_used = stats._old_stats._general._used; const size_t old_live = stats._old_stats._stat_heap._live_at_mark_end; - const size_t old_garbage = old_used - old_live; + const double old_garbage = double(old_used - old_live); const double young_gc_time = gc_time(stats._young_stats); // Calculate how much memory young collections are predicted to free. - const size_t reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg; + const double reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg; // Calculate current YC time and predicted YC time after an old collection. - const double current_young_gc_time_per_bytes_freed = double(young_gc_time) / double(reclaimed_per_young_gc); - const double potential_young_gc_time_per_bytes_freed = double(young_gc_time) / double(reclaimed_per_young_gc + old_garbage); + const double current_young_gc_time_per_bytes_freed = young_gc_time / reclaimed_per_young_gc; + const double potential_young_gc_time_per_bytes_freed = young_gc_time / (reclaimed_per_young_gc + old_garbage); + + if (current_young_gc_time_per_bytes_freed == std::numeric_limits::infinity()) { + // Young collection's are not reclaiming any memory. Return infinity as a signal + // to trigger an old collection, regardless of the amount of old garbage. + return std::numeric_limits::infinity(); + } // Calculate extra time per young collection inflicted by *not* doing an // old collection that frees up memory in the old generation. @@ -483,13 +491,12 @@ static bool rule_major_allocation_rate(const ZDirectorStats& stats) { const double young_gc_time = gc_time(stats._young_stats); // Calculate how much memory collections are predicted to free. - const size_t reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg; - const size_t reclaimed_per_old_gc = stats._old_stats._stat_heap._reclaimed_avg; + const double reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg; + const double reclaimed_per_old_gc = stats._old_stats._stat_heap._reclaimed_avg; // Calculate the GC cost for each reclaimed byte - const double current_young_gc_time_per_bytes_freed = double(young_gc_time) / double(reclaimed_per_young_gc); - const double current_old_gc_time_per_bytes_freed = reclaimed_per_old_gc == 0 ? std::numeric_limits::infinity() - : (double(old_gc_time) / double(reclaimed_per_old_gc)); + const double current_young_gc_time_per_bytes_freed = young_gc_time / reclaimed_per_young_gc; + const double current_old_gc_time_per_bytes_freed = old_gc_time / reclaimed_per_old_gc; // Calculate extra time per young collection inflicted by *not* doing an // old collection that frees up memory in the old generation. @@ -531,10 +538,10 @@ static double calculate_young_to_old_worker_ratio(const ZDirectorStats& stats) { const double young_gc_time = gc_time(stats._young_stats); const double old_gc_time = gc_time(stats._old_stats); - const size_t reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg; - const size_t reclaimed_per_old_gc = stats._old_stats._stat_heap._reclaimed_avg; - const double current_young_bytes_freed_per_gc_time = double(reclaimed_per_young_gc) / double(young_gc_time); - const double current_old_bytes_freed_per_gc_time = double(reclaimed_per_old_gc) / double(old_gc_time); + const double reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg; + const double reclaimed_per_old_gc = stats._old_stats._stat_heap._reclaimed_avg; + const double current_young_bytes_freed_per_gc_time = reclaimed_per_young_gc / young_gc_time; + const double current_old_bytes_freed_per_gc_time = reclaimed_per_old_gc / old_gc_time; if (current_young_bytes_freed_per_gc_time == 0.0) { if (current_old_bytes_freed_per_gc_time == 0.0) { diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index 56b3590960f..96cfa7d3a37 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -46,6 +46,8 @@ #include "utilities/debug.hpp" #include "utilities/ticks.hpp" +#include + #define ZSIZE_FMT SIZE_FORMAT "M(%.0f%%)" #define ZSIZE_ARGS_WITH_MAX(size, max) ((size) / M), (percent_of(size, max)) #define ZSIZE_ARGS(size) ZSIZE_ARGS_WITH_MAX(size, ZStatHeap::max_capacity()) @@ -1849,8 +1851,9 @@ void ZStatHeap::at_relocate_end(const ZPageAllocatorStats& stats, bool record_st } } -size_t ZStatHeap::reclaimed_avg() { - return (size_t)_reclaimed_bytes.davg(); +double ZStatHeap::reclaimed_avg() { + // Make sure the reclaimed average is greater than 0.0 to avoid division by zero. + return _reclaimed_bytes.davg() + std::numeric_limits::denorm_min(); } size_t ZStatHeap::max_capacity() { diff --git a/src/hotspot/share/gc/z/zStat.hpp b/src/hotspot/share/gc/z/zStat.hpp index d7fff6d7b8e..d1693857918 100644 --- a/src/hotspot/share/gc/z/zStat.hpp +++ b/src/hotspot/share/gc/z/zStat.hpp @@ -588,7 +588,7 @@ class ZStatReferences : public AllStatic { struct ZStatHeapStats { size_t _live_at_mark_end; size_t _used_at_relocate_end; - size_t _reclaimed_avg; + double _reclaimed_avg; }; // @@ -699,7 +699,7 @@ class ZStatHeap { size_t stalls_at_relocate_start() const; size_t stalls_at_relocate_end() const; - size_t reclaimed_avg(); + double reclaimed_avg(); ZStatHeapStats stats(); From 8cad0431ff17992fadbb593319ad3821b32e3b7e Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Tue, 3 Dec 2024 12:28:17 +0000 Subject: [PATCH 075/171] 8336768: Allow captureCallState and critical linker options to be combined Reviewed-by: mcimadamore --- .../classes/java/lang/foreign/Linker.java | 2 - .../internal/foreign/abi/CallingSequence.java | 6 +- .../foreign/abi/CallingSequenceBuilder.java | 15 +++- .../internal/foreign/abi/DowncallLinker.java | 3 +- .../internal/foreign/abi/LinkerOptions.java | 6 +- .../foreign/abi/NativeEntryPoint.java | 31 +++++-- .../foreign/abi/fallback/FallbackLinker.java | 9 +- .../foreign/abi/fallback/LibFallback.java | 5 +- .../native/libfallbackLinker/fallbackLinker.c | 26 ++++-- test/jdk/java/foreign/TestIllegalLink.java | 5 -- .../TestCaptureCallState.java | 83 ++++++++++++------- .../java/foreign/critical/TestCritical.java | 56 ++++++++----- test/jdk/java/foreign/critical/libCritical.c | 5 ++ .../passheapsegment/TestPassHeapSegment.java | 16 +++- 14 files changed, 180 insertions(+), 88 deletions(-) diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index bfa205e2fad..c8e4cf4746b 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -852,8 +852,6 @@ static Option firstVariadicArg(int index) { * // use errno * } * } - *

      - * This linker option can not be combined with {@link #critical}. * * @param capturedState the names of the values to save * @throws IllegalArgumentException if at least one of the provided diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java index e301f692167..5aad4ccc890 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -195,6 +195,10 @@ public boolean needsTransition() { return !linkerOptions.isCritical(); } + public boolean usingAddressPairs() { + return linkerOptions.allowsHeapAccess(); + } + public int numLeadingParams() { return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java index f24de62c807..a03c20d3173 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequenceBuilder.java @@ -108,9 +108,18 @@ public CallingSequence build() { MethodType calleeMethodType; if (!forUpcall) { if (linkerOptions.hasCapturedCallState()) { - addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( - Binding.unboxAddress(), - Binding.vmStore(abi.capturedStateStorage(), long.class))); + if (linkerOptions.allowsHeapAccess()) { + addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( + Binding.dup(), + Binding.segmentBase(), + Binding.vmStore(abi.capturedStateStorage(), Object.class), + Binding.segmentOffsetAllowHeap(), + Binding.vmStore(null, long.class))); + } else { + addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( + Binding.unboxAddress(), + Binding.vmStore(abi.capturedStateStorage(), long.class))); + } } addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of( Binding.unboxAddress(), diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java index 627de9f0765..2df30edb326 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java @@ -84,7 +84,8 @@ public MethodHandle getBoundMethodHandle() { leafType, callingSequence.needsReturnBuffer(), callingSequence.capturedStateMask(), - callingSequence.needsTransition() + callingSequence.needsTransition(), + callingSequence.usingAddressPairs() ); MethodHandle handle = JLIA.nativeMethodHandle(nep); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index fcc98ecccc0..9a19b5a8511 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -63,11 +63,7 @@ private static LinkerOptions forShared(BiConsumer 1 != needsReturnBuffer) { throw new AssertionError("Multiple register return, but needsReturnBuffer was false"); } - checkType(methodType, needsReturnBuffer, capturedStateMask); + checkMethodType(methodType, needsReturnBuffer, capturedStateMask, usingAddressPairs); CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer, capturedStateMask, needsTransition); @@ -80,14 +81,26 @@ public static NativeEntryPoint make(ABIDescriptor abi, }); } - private static void checkType(MethodType methodType, boolean needsReturnBuffer, int savedValueMask) { - if (methodType.parameterType(0) != long.class) { - throw new AssertionError("Address expected as first param: " + methodType); + private static void checkMethodType(MethodType methodType, boolean needsReturnBuffer, int savedValueMask, + boolean usingAddressPairs) { + int checkIdx = 0; + checkParamType(methodType, checkIdx++, long.class, "Function address"); + if (needsReturnBuffer) { + checkParamType(methodType, checkIdx++, long.class, "Return buffer address"); } - int checkIdx = 1; - if ((needsReturnBuffer && methodType.parameterType(checkIdx++) != long.class) - || (savedValueMask != 0 && methodType.parameterType(checkIdx) != long.class)) { - throw new AssertionError("return buffer and/or preserved value address expected: " + methodType); + if (savedValueMask != 0) { // capturing call state + if (usingAddressPairs) { + checkParamType(methodType, checkIdx++, Object.class, "Capture state heap base"); + checkParamType(methodType, checkIdx, long.class, "Capture state offset"); + } else { + checkParamType(methodType, checkIdx, long.class, "Capture state address"); + } + } + } + + private static void checkParamType(MethodType methodType, int checkIdx, Class expectedType, String name) { + if (methodType.parameterType(checkIdx) != expectedType) { + throw new AssertionError(name + " expected at index " + checkIdx + ": " + methodType); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java index ad5e4b97175..54906d9fef2 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/FallbackLinker.java @@ -163,8 +163,14 @@ private static Object doDowncall(SegmentAllocator returnAllocator, Object[] args acquiredSessions.add(targetImpl); MemorySegment capturedState = null; + Object captureStateHeapBase = null; if (invData.capturedStateMask() != 0) { capturedState = SharedUtils.checkCaptureSegment((MemorySegment) args[argStart++]); + if (!invData.allowsHeapAccess) { + SharedUtils.checkNative(capturedState); + } else { + captureStateHeapBase = capturedState.heapBase().orElse(null); + } MemorySessionImpl capturedStateImpl = ((AbstractMemorySegmentImpl) capturedState).sessionImpl(); capturedStateImpl.acquire0(); acquiredSessions.add(capturedStateImpl); @@ -199,7 +205,8 @@ private static Object doDowncall(SegmentAllocator returnAllocator, Object[] args retSeg = (invData.returnLayout() instanceof GroupLayout ? returnAllocator : arena).allocate(invData.returnLayout); } - LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, capturedState, invData.capturedStateMask(), + LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, + captureStateHeapBase, capturedState, invData.capturedStateMask(), heapBases, args.length); Reference.reachabilityFence(invData.cif()); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java index b68b1ed4bad..6fe8dd5f91c 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/fallback/LibFallback.java @@ -90,10 +90,11 @@ private static boolean tryLoadLibrary() { * @see jdk.internal.foreign.abi.CapturableState */ static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs, - MemorySegment capturedState, int capturedStateMask, + Object captureStateHeapBase, MemorySegment capturedState, int capturedStateMask, Object[] heapBases, int numArgs) { doDowncall(cif.address(), target.address(), retPtr == null ? 0 : retPtr.address(), argPtrs.address(), + captureStateHeapBase, capturedState == null ? 0 : capturedState.address(), capturedStateMask, heapBases, numArgs); } @@ -212,7 +213,7 @@ private static void checkStatus(int code) { private static native int createClosure(long cif, Object userData, long[] ptrs); private static native void freeClosure(long closureAddress, long globalTarget); private static native void doDowncall(long cif, long fn, long rvalue, long avalues, - long capturedState, int capturedStateMask, + Object captureStateHeapBase, long capturedState, int capturedStateMask, Object[] heapBases, int numArgs); private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes); diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c index 2ee64fb05bc..1548fb49e26 100644 --- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -112,12 +112,16 @@ static void do_capture_state(int32_t* value_ptr, int captured_state_mask) { JNIEXPORT void JNICALL Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclass cls, jlong cif, jlong fn, jlong rvalue, - jlong avalues, jlong jcaptured_state, jint captured_state_mask, + jlong avalues, + jarray capture_state_heap_base, jlong captured_state_offset, + jint captured_state_mask, jarray heapBases, jint numArgs) { void** carrays; + int capture_state_hb_offset = numArgs; + int32_t* captured_state_addr = jlong_to_ptr(captured_state_offset); if (heapBases != NULL) { void** aptrs = jlong_to_ptr(avalues); - carrays = malloc(sizeof(void*) * numArgs); + carrays = malloc(sizeof(void*) * (numArgs + 1)); for (int i = 0; i < numArgs; i++) { jarray hb = (jarray) (*env)->GetObjectArrayElement(env, heapBases, i); if (hb != NULL) { @@ -130,10 +134,20 @@ Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclas *((void**)aptrs[i]) = arrayPtr + offset; } } + if (capture_state_heap_base != NULL) { + jboolean isCopy; + jbyte* arrayPtr = (*env)->GetPrimitiveArrayCritical(env, capture_state_heap_base, &isCopy); + carrays[capture_state_hb_offset] = arrayPtr; + captured_state_addr = (int32_t*) (arrayPtr + captured_state_offset); + } } ffi_call(jlong_to_ptr(cif), jlong_to_ptr(fn), jlong_to_ptr(rvalue), jlong_to_ptr(avalues)); + if (captured_state_mask != 0) { + do_capture_state(captured_state_addr, captured_state_mask); + } + if (heapBases != NULL) { for (int i = 0; i < numArgs; i++) { jarray hb = (jarray) (*env)->GetObjectArrayElement(env, heapBases, i); @@ -141,13 +155,11 @@ Java_jdk_internal_foreign_abi_fallback_LibFallback_doDowncall(JNIEnv* env, jclas (*env)->ReleasePrimitiveArrayCritical(env, hb, carrays[i], JNI_COMMIT); } } + if (capture_state_heap_base != NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, capture_state_heap_base, carrays[capture_state_hb_offset], JNI_COMMIT); + } free(carrays); } - - if (captured_state_mask != 0) { - int32_t* captured_state = jlong_to_ptr(jcaptured_state); - do_capture_state(captured_state, captured_state_mask); - } } static void do_upcall(ffi_cif* cif, void* ret, void** args, void* user_data) { diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java index b5052b4346c..2a9d5ad3782 100644 --- a/test/jdk/java/foreign/TestIllegalLink.java +++ b/test/jdk/java/foreign/TestIllegalLink.java @@ -192,11 +192,6 @@ public static Object[][] types() { NO_OPTIONS, "has unexpected size" }, - { - FunctionDescriptor.ofVoid(), - new Linker.Option[]{Linker.Option.critical(false), Linker.Option.captureCallState("errno")}, - "Incompatible linker options: captureCallState, critical" - }, })); for (ValueLayout illegalLayout : List.of(C_CHAR, ValueLayout.JAVA_CHAR, C_BOOL, C_SHORT, C_FLOAT)) { diff --git a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java index 51c91ce0f96..a1bf1183fb5 100644 --- a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java +++ b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java @@ -61,12 +61,18 @@ public class TestCaptureCallState extends NativeTestHelper { } } - private record SaveValuesCase(String nativeTarget, FunctionDescriptor nativeDesc, String threadLocalName, Consumer resultCheck) {} + private record SaveValuesCase(String nativeTarget, FunctionDescriptor nativeDesc, String threadLocalName, + Consumer resultCheck, boolean critical) {} @Test(dataProvider = "cases") public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { - Linker.Option stl = Linker.Option.captureCallState(testCase.threadLocalName()); - MethodHandle handle = downcallHandle(testCase.nativeTarget(), testCase.nativeDesc(), stl); + List options = new ArrayList<>(); + options.add(Linker.Option.captureCallState(testCase.threadLocalName())); + if (testCase.critical()) { + options.add(Linker.Option.critical(false)); + } + MethodHandle handle = downcallHandle(testCase.nativeTarget(), testCase.nativeDesc(), + options.toArray(Linker.Option[]::new)); StructLayout capturedStateLayout = Linker.Option.captureStateLayout(); VarHandle errnoHandle = capturedStateLayout.varHandle(groupElement(testCase.threadLocalName())); @@ -86,9 +92,14 @@ public void testSavedThreadLocal(SaveValuesCase testCase) throws Throwable { @Test(dataProvider = "invalidCaptureSegmentCases") public void testInvalidCaptureSegment(MemorySegment captureSegment, - Class expectedExceptionType, String expectedExceptionMessage) { - Linker.Option stl = Linker.Option.captureCallState("errno"); - MethodHandle handle = downcallHandle("set_errno_V", FunctionDescriptor.ofVoid(C_INT), stl); + Class expectedExceptionType, String expectedExceptionMessage, + Linker.Option[] extraOptions) { + List options = new ArrayList<>(); + options.add(Linker.Option.captureCallState("errno")); + for (Linker.Option extra : extraOptions) { + options.add(extra); + } + MethodHandle handle = downcallHandle("set_errno_V", FunctionDescriptor.ofVoid(C_INT), options.toArray(Linker.Option[]::new)); try { int testValue = 42; @@ -103,32 +114,39 @@ public void testInvalidCaptureSegment(MemorySegment captureSegment, public static Object[][] cases() { List cases = new ArrayList<>(); - cases.add(new SaveValuesCase("set_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), "errno", o -> {})); - cases.add(new SaveValuesCase("set_errno_I", FunctionDescriptor.of(JAVA_INT, JAVA_INT), "errno", o -> assertEquals((int) o, 42))); - cases.add(new SaveValuesCase("set_errno_D", FunctionDescriptor.of(JAVA_DOUBLE, JAVA_INT), "errno", o -> assertEquals((double) o, 42.0))); - - cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L))); - cases.add(structCase("SLL", Map.of(JAVA_LONG.withName("x"), 42L, - JAVA_LONG.withName("y"), 42L))); - cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, - JAVA_LONG.withName("y"), 42L, - JAVA_LONG.withName("z"), 42L))); - cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D))); - cases.add(structCase("SDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, - JAVA_DOUBLE.withName("y"), 42D))); - cases.add(structCase("SDDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, - JAVA_DOUBLE.withName("y"), 42D, - JAVA_DOUBLE.withName("z"), 42D))); - - if (IS_WINDOWS) { - cases.add(new SaveValuesCase("SetLastError", FunctionDescriptor.ofVoid(JAVA_INT), "GetLastError", o -> {})); - cases.add(new SaveValuesCase("WSASetLastError", FunctionDescriptor.ofVoid(JAVA_INT), "WSAGetLastError", o -> {})); + for (boolean critical : new boolean[]{ true, false }) { + cases.add(new SaveValuesCase("set_errno_V", FunctionDescriptor.ofVoid(JAVA_INT), + "errno", o -> {}, critical)); + cases.add(new SaveValuesCase("set_errno_I", FunctionDescriptor.of(JAVA_INT, JAVA_INT), + "errno", o -> assertEquals((int) o, 42), critical)); + cases.add(new SaveValuesCase("set_errno_D", FunctionDescriptor.of(JAVA_DOUBLE, JAVA_INT), + "errno", o -> assertEquals((double) o, 42.0), critical)); + + cases.add(structCase("SL", Map.of(JAVA_LONG.withName("x"), 42L), critical)); + cases.add(structCase("SLL", Map.of(JAVA_LONG.withName("x"), 42L, + JAVA_LONG.withName("y"), 42L), critical)); + cases.add(structCase("SLLL", Map.of(JAVA_LONG.withName("x"), 42L, + JAVA_LONG.withName("y"), 42L, + JAVA_LONG.withName("z"), 42L), critical)); + cases.add(structCase("SD", Map.of(JAVA_DOUBLE.withName("x"), 42D), critical)); + cases.add(structCase("SDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, + JAVA_DOUBLE.withName("y"), 42D), critical)); + cases.add(structCase("SDDD", Map.of(JAVA_DOUBLE.withName("x"), 42D, + JAVA_DOUBLE.withName("y"), 42D, + JAVA_DOUBLE.withName("z"), 42D), critical)); + + if (IS_WINDOWS) { + cases.add(new SaveValuesCase("SetLastError", FunctionDescriptor.ofVoid(JAVA_INT), + "GetLastError", o -> {}, critical)); + cases.add(new SaveValuesCase("WSASetLastError", FunctionDescriptor.ofVoid(JAVA_INT), + "WSAGetLastError", o -> {}, critical)); + } } return cases.stream().map(tc -> new Object[] {tc}).toArray(Object[][]::new); } - static SaveValuesCase structCase(String name, Map fields) { + static SaveValuesCase structCase(String name, Map fields, boolean critical) { StructLayout layout = MemoryLayout.structLayout(fields.keySet().toArray(MemoryLayout[]::new)); Consumer check = o -> {}; @@ -139,16 +157,19 @@ static SaveValuesCase structCase(String name, Map fields) check = check.andThen(o -> assertEquals(fieldHandle.get(o, 0L), value)); } - return new SaveValuesCase("set_errno_" + name, FunctionDescriptor.of(layout, JAVA_INT), "errno", check); + return new SaveValuesCase("set_errno_" + name, FunctionDescriptor.of(layout, JAVA_INT), + "errno", check, critical); } @DataProvider public static Object[][] invalidCaptureSegmentCases() { return new Object[][]{ - {Arena.ofAuto().allocate(1), IndexOutOfBoundsException.class, ".*Out of bound access on segment.*"}, - {MemorySegment.NULL, IllegalArgumentException.class, ".*Capture segment is NULL.*"}, + {Arena.ofAuto().allocate(1), IndexOutOfBoundsException.class, ".*Out of bound access on segment.*", new Linker.Option[0]}, + {MemorySegment.NULL, IllegalArgumentException.class, ".*Capture segment is NULL.*", new Linker.Option[0]}, {Arena.ofAuto().allocate(Linker.Option.captureStateLayout().byteSize() + 3).asSlice(3), // misaligned - IllegalArgumentException.class, ".*Target offset incompatible with alignment constraints.*"}, + IllegalArgumentException.class, ".*Target offset incompatible with alignment constraints.*", new Linker.Option[0]}, + {MemorySegment.ofArray(new byte[(int) Linker.Option.captureStateLayout().byteSize()]), // misaligned + IllegalArgumentException.class, ".*Target offset incompatible with alignment constraints.*", new Linker.Option[0]}, }; } } diff --git a/test/jdk/java/foreign/critical/TestCritical.java b/test/jdk/java/foreign/critical/TestCritical.java index ab5d6017aba..85aa962383b 100644 --- a/test/jdk/java/foreign/critical/TestCritical.java +++ b/test/jdk/java/foreign/critical/TestCritical.java @@ -45,12 +45,16 @@ import java.util.ArrayList; import java.util.List; import java.util.function.IntFunction; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.testng.Assert.assertEquals; public class TestCritical extends NativeTestHelper { + static final MemoryLayout CAPTURE_STATE_LAYOUT = Linker.Option.captureStateLayout(); + static final VarHandle ERRNO_HANDLE = CAPTURE_STATE_LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("errno")); + static { System.loadLibrary("Critical"); } @@ -87,11 +91,16 @@ public void testWithReturnBuffer() throws Throwable { } public record AllowHeapCase(IntFunction newArraySegment, ValueLayout elementLayout, - String fName, FunctionDescriptor fDesc, boolean readOnly) {} + String fName, FunctionDescriptor fDesc, boolean readOnly, boolean captureErrno) {} @Test(dataProvider = "allowHeapCases") public void testAllowHeap(AllowHeapCase testCase) throws Throwable { - MethodHandle handle = downcallHandle(testCase.fName(), testCase.fDesc(), Linker.Option.critical(true)); + List options = new ArrayList<>(); + options.add(Linker.Option.critical(true)); + if (testCase.captureErrno()) { + options.add(Linker.Option.captureCallState("errno")); + } + MethodHandle handle = downcallHandle(testCase.fName(), testCase.fDesc(), options.toArray(Linker.Option[]::new)); int elementCount = 10; MemorySegment heapSegment = testCase.newArraySegment().apply(elementCount); if (testCase.readOnly()) { @@ -101,29 +110,36 @@ public void testAllowHeap(AllowHeapCase testCase) throws Throwable { try (Arena arena = Arena.ofConfined()) { TestValue[] tvs = genTestArgs(testCase.fDesc(), arena); - Object[] args = Stream.of(tvs).map(TestValue::value).toArray(); + List args = Stream.of(tvs).map(TestValue::value).collect(Collectors.toCollection(ArrayList::new)); + MemorySegment captureSegment = testCase.captureErrno() + ? MemorySegment.ofArray(new int[((int) CAPTURE_STATE_LAYOUT.byteSize() + 3) / 4]) + : null; // inject our custom last three arguments - args[args.length - 1] = (int) sequence.byteSize(); + args.set(args.size() - 1, (int) sequence.byteSize()); TestValue sourceSegment = genTestValue(sequence, arena); - args[args.length - 2] = sourceSegment.value(); - args[args.length - 3] = heapSegment; + args.set(args.size() - 2, sourceSegment.value()); + args.set(args.size() - 3, heapSegment); + if (testCase.captureErrno()) { + args.add(0, captureSegment); + } if (handle.type().parameterType(0) == SegmentAllocator.class) { - Object[] newArgs = new Object[args.length + 1]; - newArgs[0] = arena; - System.arraycopy(args, 0, newArgs, 1, args.length); - args = newArgs; + args.add(0, arena); } Object o = handle.invokeWithArguments(args); - if (o != null) { tvs[0].check(o); } // check that writes went through to array sourceSegment.check(heapSegment); + + if (testCase.captureErrno()) { + int errno = (int) ERRNO_HANDLE.get(captureSegment, 0L); + assertEquals(errno, 42); + } } } @@ -149,14 +165,16 @@ public Object[][] allowHeapCases() { List cases = new ArrayList<>(); - for (HeapSegmentFactory hsf : HeapSegmentFactory.values()) { - cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_void", voidDesc, false)); - cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_int", intDesc, false)); - cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_return_buffer", L2Desc, false)); - cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_imr", L3Desc, false)); - cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_void_stack", stackDesc, false)); - // readOnly - cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_void", voidDesc, true)); + for (boolean doCapture : new boolean[]{ true, false }) { + for (HeapSegmentFactory hsf : HeapSegmentFactory.values()) { + cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_void", voidDesc, false, doCapture)); + cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_int", intDesc, false, doCapture)); + cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_return_buffer", L2Desc, false, doCapture)); + cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_imr", L3Desc, false, doCapture)); + cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_void_stack", stackDesc, false, doCapture)); + // readOnly + cases.add(new AllowHeapCase(hsf.newArray, hsf.elementLayout, "test_allow_heap_void", voidDesc, true, doCapture)); + } } return cases.stream().map(e -> new Object[]{ e }).toArray(Object[][]::new); diff --git a/test/jdk/java/foreign/critical/libCritical.c b/test/jdk/java/foreign/critical/libCritical.c index cc03db3c43c..c901df0a368 100644 --- a/test/jdk/java/foreign/critical/libCritical.c +++ b/test/jdk/java/foreign/critical/libCritical.c @@ -53,12 +53,14 @@ EXPORT void test_allow_heap_void(unsigned char* heapArr, unsigned char* nativeAr for (int i = 0; i < numBytes; i++) { heapArr[i] = nativeArr[i]; } + errno = 42; } EXPORT int test_allow_heap_int(int a0, unsigned char* heapArr, unsigned char* nativeArr, int numBytes) { for (int i = 0; i < numBytes; i++) { heapArr[i] = nativeArr[i]; } + errno = 42; return a0; } @@ -71,6 +73,7 @@ EXPORT struct L2 test_allow_heap_return_buffer(struct L2 a0, unsigned char* heap for (int i = 0; i < numBytes; i++) { heapArr[i] = nativeArr[i]; } + errno = 42; return a0; } @@ -84,6 +87,7 @@ EXPORT struct L3 test_allow_heap_imr(struct L3 a0, unsigned char* heapArr, unsig for (int i = 0; i < numBytes; i++) { heapArr[i] = nativeArr[i]; } + errno = 42; return a0; } @@ -94,4 +98,5 @@ EXPORT void test_allow_heap_void_stack(long long a0, long long a1, long long a2, for (int i = 0; i < numBytes; i++) { heapArr[i] = nativeArr[i]; } + errno = 42; } diff --git a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java index fadcdf1ba24..375c672a606 100644 --- a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java +++ b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java @@ -31,8 +31,7 @@ import org.testng.annotations.Test; import java.io.IOException; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.MemorySegment; +import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import static java.lang.foreign.ValueLayout.ADDRESS; @@ -51,6 +50,19 @@ public void testNoHeapArgs() throws Throwable { handle.invoke(segment); // should throw } + @Test(expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") + public void testNoHeapCaptureCallState() throws Throwable { + MethodHandle handle = downcallHandle("test_args", FunctionDescriptor.ofVoid(ADDRESS), + Linker.Option.captureCallState("errno")); + try (Arena arena = Arena.ofConfined()) { + assert Linker.Option.captureStateLayout().byteAlignment() % 4 == 0; + MemorySegment captureHeap = MemorySegment.ofArray(new int[(int) Linker.Option.captureStateLayout().byteSize() / 4]); + MemorySegment segment = arena.allocateFrom(C_CHAR, new byte[]{ 0, 1, 2 }); + handle.invoke(captureHeap, segment); // should throw for captureHeap + } + } + @Test(dataProvider = "specs") public void testNoHeapReturns(boolean spec) throws IOException, InterruptedException { runInNewProcess(Runner.class, spec) From 65b5a2e3e4f9882adca587b9fed90223b93302a0 Mon Sep 17 00:00:00 2001 From: Daniel Skantz Date: Tue, 3 Dec 2024 13:32:28 +0000 Subject: [PATCH 076/171] 8345158: IGV: local scheduling should not place successors before predecessors Reviewed-by: rcastanedalo, chagedorn --- .../hotspot/igv/servercompiler/ServerCompilerScheduler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java index 378e3bb6d40..95a0b71c16d 100644 --- a/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java +++ b/src/utils/IdealGraphVisualizer/ServerCompiler/src/main/java/com/sun/hotspot/igv/servercompiler/ServerCompilerScheduler.java @@ -382,7 +382,7 @@ private void scheduleLocal() { }; private List scheduleBlock(Collection nodes) { - List schedule = new ArrayList<>(); + LinkedHashSet schedule = new LinkedHashSet(); // Initialize ready priority queue with nodes without predecessors. Queue ready = new PriorityQueue<>(schedulePriority); @@ -407,7 +407,7 @@ private List scheduleBlock(Collection nodes) { } boolean allPredsScheduled = true; for (Node p : s.preds) { - if (!visited.contains(p)) { + if (!schedule.contains(p.inputNode)) { allPredsScheduled = false; break; } @@ -419,7 +419,7 @@ private List scheduleBlock(Collection nodes) { } } assert(schedule.size() == nodes.size()); - return schedule; + return new ArrayList(schedule); } // Return latest block that dominates all successors of n, or null if any From dfa5620ff3d57b71ccaf09ca8e71fa85d93ceb00 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Tue, 3 Dec 2024 13:32:48 +0000 Subject: [PATCH 077/171] 8345164: Remove residual --enable-preview in FFM tests and benchmarks Reviewed-by: mcimadamore, jvernee --- .../TestEnableNativeAccessJarManifest.java | 5 ++--- .../openjdk/bench/java/lang/foreign/ToJavaStringTest.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java index d75e827fbc6..abd793b31da 100644 --- a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java +++ b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -21,7 +21,7 @@ * questions. */ -/** +/* * @test * @summary Basic test for Enable-Native-Access attribute in the * manifest of a main application JAR @@ -78,7 +78,6 @@ public void testEnableNativeAccessInJarManifest(String action, String cls, Resul // java -jar test.jar List command = new ArrayList<>(List.of( - "--enable-preview", "-Djava.library.path=" + System.getProperty("java.library.path") )); command.addAll(vmArgs); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java index 02b1a47d03f..7a0639b29bb 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -43,7 +43,7 @@ @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(Scope.Benchmark) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(value = 3, jvmArgs = { "--enable-native-access=ALL-UNNAMED", "--enable-preview", "-Djava.library.path=micro/native" }) +@Fork(value = 3, jvmArgs = { "--enable-native-access=ALL-UNNAMED", "-Djava.library.path=micro/native" }) public class ToJavaStringTest { private MemorySegment strSegment; From eac00f6d112b24b62b067a1e9cee342ab07ef021 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Tue, 3 Dec 2024 14:32:22 +0000 Subject: [PATCH 078/171] 8345396: Fix headers after JDK-8345164 Reviewed-by: rriggs --- .../enablenativeaccess/TestEnableNativeAccessJarManifest.java | 2 +- .../org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java index abd793b31da..aa923b5a623 100644 --- a/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java +++ b/test/jdk/java/foreign/enablenativeaccess/TestEnableNativeAccessJarManifest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java index 7a0639b29bb..f38a1481158 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 From fcf185c8b425a6984eb145c3127f97e811d345d7 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Tue, 3 Dec 2024 14:59:30 +0000 Subject: [PATCH 079/171] 8345325: SM cleanup of GetPropertyAction in java.base Reviewed-by: alanb, lancea, naoto, mchung --- .../share/classes/java/lang/ScopedValue.java | 3 +- .../classes/java/lang/StackStreamFactory.java | 10 +- .../share/classes/java/lang/ThreadLocal.java | 3 +- .../java/lang/invoke/MethodHandleNatives.java | 5 +- .../classes/jdk/internal/vm/Continuation.java | 5 +- .../jdk/internal/vm/ThreadContainers.java | 5 +- .../security/action/GetPropertyAction.java | 163 ------------------ .../util/calendar/LocalGregorianCalendar.java | 6 +- .../provider/LocaleProviderAdapter.java | 3 +- .../util/locale/provider/LocaleResources.java | 3 +- test/jdk/sun/security/action/Generify.java | 48 ------ 11 files changed, 13 insertions(+), 241 deletions(-) delete mode 100644 src/java.base/share/classes/sun/security/action/GetPropertyAction.java delete mode 100644 test/jdk/sun/security/action/Generify.java diff --git a/src/java.base/share/classes/java/lang/ScopedValue.java b/src/java.base/share/classes/java/lang/ScopedValue.java index ac9b598b531..3730326ee3c 100644 --- a/src/java.base/share/classes/java/lang/ScopedValue.java +++ b/src/java.base/share/classes/java/lang/ScopedValue.java @@ -38,7 +38,6 @@ import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Hidden; import jdk.internal.vm.ScopedValueContainer; -import sun.security.action.GetPropertyAction; /** * A value that may be safely and efficiently shared to methods without using method @@ -740,7 +739,7 @@ private static final class Cache { static { final String propertyName = "java.lang.ScopedValue.cacheSize"; - var sizeString = GetPropertyAction.privilegedGetProperty(propertyName, "16"); + var sizeString = System.getProperty(propertyName, "16"); var cacheSize = Integer.valueOf(sizeString); if (cacheSize < 2 || cacheSize > MAX_CACHE_SIZE) { cacheSize = MAX_CACHE_SIZE; diff --git a/src/java.base/share/classes/java/lang/StackStreamFactory.java b/src/java.base/share/classes/java/lang/StackStreamFactory.java index e2ec089e30d..404a74e96c7 100644 --- a/src/java.base/share/classes/java/lang/StackStreamFactory.java +++ b/src/java.base/share/classes/java/lang/StackStreamFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -45,7 +45,6 @@ import java.util.stream.StreamSupport; import jdk.internal.vm.Continuation; import jdk.internal.vm.ContinuationScope; -import sun.security.action.GetPropertyAction; import static java.lang.StackStreamFactory.WalkerState.*; @@ -82,13 +81,8 @@ private StackStreamFactory() {} @Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM @Native private static final int FILL_LIVE_STACK_FRAMES = 0x100; - /* - * For Throwable to use StackWalker, set useNewThrowable to true. - * Performance work and extensive testing is needed to replace the - * VM built-in backtrace filled in Throwable with the StackWalker. - */ static final boolean isDebug = - "true".equals(GetPropertyAction.privilegedGetProperty("stackwalk.debug")); + "true".equals(System.getProperty("stackwalk.debug")); static StackFrameTraverser makeStackTraverser(StackWalker walker, Function, ? extends T> function) diff --git a/src/java.base/share/classes/java/lang/ThreadLocal.java b/src/java.base/share/classes/java/lang/ThreadLocal.java index 8a9aa7998fd..fae322d2b73 100644 --- a/src/java.base/share/classes/java/lang/ThreadLocal.java +++ b/src/java.base/share/classes/java/lang/ThreadLocal.java @@ -33,7 +33,6 @@ import jdk.internal.misc.CarrierThreadLocal; import jdk.internal.misc.TerminatingThreadLocal; -import sun.security.action.GetPropertyAction; /** * This class provides thread-local variables. These variables differ from @@ -804,7 +803,7 @@ private void expungeStaleEntries() { * a stack trace should be printed when a virtual thread sets a thread local. */ private static boolean traceVirtualThreadLocals() { - String propValue = GetPropertyAction.privilegedGetProperty("jdk.traceVirtualThreadLocals"); + String propValue = System.getProperty("jdk.traceVirtualThreadLocals"); return (propValue != null) && (propValue.isEmpty() || Boolean.parseBoolean(propValue)); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java index 4ffbf826317..0743b3362f2 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -28,11 +28,9 @@ import jdk.internal.misc.VM; import jdk.internal.ref.CleanerFactory; import sun.invoke.util.Wrapper; -import sun.security.action.GetPropertyAction; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; -import java.util.Properties; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE; @@ -707,8 +705,7 @@ static boolean canBeCalledVirtual(MemberName symbolicRef, Class definingClass static final boolean USE_SOFT_CACHE; static { - Properties props = GetPropertyAction.privilegedGetProperties(); USE_SOFT_CACHE = Boolean.parseBoolean( - props.getProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "true")); + System.getProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "true")); } } diff --git a/src/java.base/share/classes/jdk/internal/vm/Continuation.java b/src/java.base/share/classes/jdk/internal/vm/Continuation.java index 05a32cc3b21..a97f9ac9ea4 100644 --- a/src/java.base/share/classes/jdk/internal/vm/Continuation.java +++ b/src/java.base/share/classes/jdk/internal/vm/Continuation.java @@ -28,7 +28,6 @@ import jdk.internal.misc.Unsafe; import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.IntrinsicCandidate; -import sun.security.action.GetPropertyAction; import java.util.EnumSet; import java.util.Set; @@ -51,7 +50,7 @@ public class Continuation { StackChunk.init(); // ensure StackChunk class is initialized - String value = GetPropertyAction.privilegedGetProperty("jdk.preserveScopedValueCache"); + String value = System.getProperty("jdk.preserveScopedValueCache"); PRESERVE_SCOPED_VALUE_CACHE = (value == null) || Boolean.parseBoolean(value); } @@ -503,7 +502,7 @@ private void dump() { } private static boolean isEmptyOrTrue(String property) { - String value = GetPropertyAction.privilegedGetProperty(property); + String value = System.getProperty(property); if (value == null) return false; return value.isEmpty() || Boolean.parseBoolean(value); diff --git a/src/java.base/share/classes/jdk/internal/vm/ThreadContainers.java b/src/java.base/share/classes/jdk/internal/vm/ThreadContainers.java index e1672febc3c..aeae6b36520 100644 --- a/src/java.base/share/classes/jdk/internal/vm/ThreadContainers.java +++ b/src/java.base/share/classes/jdk/internal/vm/ThreadContainers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -33,7 +33,6 @@ import java.util.stream.Stream; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; -import sun.security.action.GetPropertyAction; /** * This class consists exclusively of static methods to support groupings of threads. @@ -52,7 +51,7 @@ public class ThreadContainers { private static final ReferenceQueue QUEUE = new ReferenceQueue<>(); static { - String s = GetPropertyAction.privilegedGetProperty("jdk.trackAllThreads"); + String s = System.getProperty("jdk.trackAllThreads"); if (s == null || s.isEmpty() || Boolean.parseBoolean(s)) { TRACK_ALL_THREADS = true; ROOT_CONTAINER = new RootContainer.TrackingRootContainer(); diff --git a/src/java.base/share/classes/sun/security/action/GetPropertyAction.java b/src/java.base/share/classes/sun/security/action/GetPropertyAction.java deleted file mode 100644 index 8954c615cba..00000000000 --- a/src/java.base/share/classes/sun/security/action/GetPropertyAction.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 1998, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.security.action; - -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Properties; - -/** - * A convenience class for retrieving the string value of a system - * property as a privileged action. - * - *

      An instance of this class can be used as the argument of - * AccessController.doPrivileged. - * - *

      The following code retrieves the value of the system - * property named "prop" as a privileged action: - * - *

      - * String s = java.security.AccessController.doPrivileged
      - *                      (new GetPropertyAction("prop"));
      - * 
      - * - * @author Roland Schemers - * @see java.security.PrivilegedAction - * @see java.security.AccessController - * @since 1.2 - */ - -public class GetPropertyAction implements PrivilegedAction { - private final String theProp; - private final String defaultVal; - - /** - * Constructor that takes the name of the system property whose - * string value needs to be determined. - * - * @param theProp the name of the system property. - */ - public GetPropertyAction(String theProp) { - this.theProp = theProp; - this.defaultVal = null; - } - - /** - * Constructor that takes the name of the system property and the default - * value of that property. - * - * @param theProp the name of the system property. - * @param defaultVal the default value. - */ - public GetPropertyAction(String theProp, String defaultVal) { - this.theProp = theProp; - this.defaultVal = defaultVal; - } - - /** - * Determines the string value of the system property whose - * name was specified in the constructor. - * - * @return the string value of the system property, - * or the default value if there is no property with that key. - */ - public String run() { - String value = System.getProperty(theProp); - return (value == null) ? defaultVal : value; - } - - /** - * Convenience method to get a property without going through doPrivileged - * if no security manager is present. This is unsafe for inclusion in a - * public API but allowable here since this class is now encapsulated. - * - * Note that this method performs a privileged action using caller-provided - * inputs. The caller of this method should take care to ensure that the - * inputs are not tainted and the returned property is not made accessible - * to untrusted code if it contains sensitive information. - * - * @param theProp the name of the system property. - */ - @SuppressWarnings("removal") - public static String privilegedGetProperty(String theProp) { - if (System.getSecurityManager() == null) { - return System.getProperty(theProp); - } else { - return AccessController.doPrivileged( - new GetPropertyAction(theProp)); - } - } - - /** - * Convenience method to get a property without going through doPrivileged - * if no security manager is present. This is unsafe for inclusion in a - * public API but allowable here since this class is now encapsulated. - * - * Note that this method performs a privileged action using caller-provided - * inputs. The caller of this method should take care to ensure that the - * inputs are not tainted and the returned property is not made accessible - * to untrusted code if it contains sensitive information. - * - * @param theProp the name of the system property. - * @param defaultVal the default value. - */ - @SuppressWarnings("removal") - public static String privilegedGetProperty(String theProp, - String defaultVal) { - if (System.getSecurityManager() == null) { - return System.getProperty(theProp, defaultVal); - } else { - return AccessController.doPrivileged( - new GetPropertyAction(theProp, defaultVal)); - } - } - - /** - * Convenience method to call System.getProperties without - * having to go through doPrivileged if no security manager is present. - * This is unsafe for inclusion in a public API but allowable here since - * this class is now encapsulated. - * - * Note that this method performs a privileged action, and callers of - * this method should take care to ensure that the returned properties - * are not made accessible to untrusted code since it may contain - * sensitive information. - */ - @SuppressWarnings("removal") - public static Properties privilegedGetProperties() { - if (System.getSecurityManager() == null) { - return System.getProperties(); - } else { - return AccessController.doPrivileged( - new PrivilegedAction() { - public Properties run() { - return System.getProperties(); - } - } - ); - } - } -} diff --git a/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java b/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java index b9d520d4083..850f198c8b1 100644 --- a/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java +++ b/src/java.base/share/classes/sun/util/calendar/LocalGregorianCalendar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -28,7 +28,6 @@ import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; -import sun.security.action.GetPropertyAction; /** * @@ -146,8 +145,7 @@ static LocalGregorianCalendar getLocalGregorianCalendar(String name) { } // Append an era to the predefined eras if it's given by the property. - String prop = GetPropertyAction - .privilegedGetProperty("jdk.calendar.japanese.supplemental.era"); + String prop = System.getProperty("jdk.calendar.japanese.supplemental.era"); if (prop != null) { Era era = parseEraEntry(prop); if (era != null) { diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java index a027191e45f..5e7a4c719d1 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java @@ -48,7 +48,6 @@ import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; -import sun.security.action.GetPropertyAction; import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.spi.CalendarProvider; @@ -115,7 +114,7 @@ public String getTextResourcesPackage() { adapterCache = new ConcurrentHashMap<>(); static { - String order = GetPropertyAction.privilegedGetProperty("java.locale.providers"); + String order = System.getProperty("java.locale.providers"); ArrayList typeList = new ArrayList<>(); String invalidTypeMessage = null; String compatWarningMessage = null; diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java index ee6ec0f72ba..4ae0275fdda 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java @@ -63,7 +63,6 @@ import java.util.stream.Stream; import jdk.internal.util.StaticProperty; -import sun.security.action.GetPropertyAction; import sun.util.resources.LocaleData; import sun.util.resources.OpenListResourceBundle; import sun.util.resources.ParallelListResourceBundle; @@ -899,7 +898,7 @@ String getCacheKey() { } private static final boolean TRACE_ON = Boolean.parseBoolean( - GetPropertyAction.privilegedGetProperty("locale.resources.debug", "false")); + System.getProperty("locale.resources.debug", "false")); public static void trace(String format, Object... params) { if (TRACE_ON) { diff --git a/test/jdk/sun/security/action/Generify.java b/test/jdk/sun/security/action/Generify.java deleted file mode 100644 index 4ddbc299a47..00000000000 --- a/test/jdk/sun/security/action/Generify.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2004, 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 5057136 - * @summary Generify sun.security.action.GetPropertyAction and friends - * @modules java.base/sun.security.action - */ - -import java.security.*; -import sun.security.action.*; - -public class Generify { - - public static void main(String[] args) throws Exception { - - System.setProperty("property", "propertyvalue"); - - String prop = AccessController.doPrivileged - (new GetPropertyAction("property")); - if (prop.equals("propertyvalue")) { - System.out.println("property test passed"); - } else { - throw new SecurityException("property test failed"); - } - } -} From 8647c00114385f74939bf705c9c07e709f41a18d Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Tue, 3 Dec 2024 15:00:47 +0000 Subject: [PATCH 080/171] 8342602: Remove JButton/PressedButtonRightClickTest test Reviewed-by: dnguyen, prr --- .../JButton/PressedButtonRightClickTest.java | 147 ------------------ test/jdk/javax/swing/JButton/bug4490179.java | 2 +- 2 files changed, 1 insertion(+), 148 deletions(-) delete mode 100644 test/jdk/javax/swing/JButton/PressedButtonRightClickTest.java diff --git a/test/jdk/javax/swing/JButton/PressedButtonRightClickTest.java b/test/jdk/javax/swing/JButton/PressedButtonRightClickTest.java deleted file mode 100644 index 8f3da452c21..00000000000 --- a/test/jdk/javax/swing/JButton/PressedButtonRightClickTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ - - -import java.awt.AWTException; -import java.awt.BorderLayout; -import java.awt.EventQueue; -import java.awt.Point; -import java.awt.Robot; -import java.awt.event.InputEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.SwingUtilities; - -/* - * @test - * @key headful - * @bug 8049069 - * @summary Tests whether right mouse click releases a pressed JButton - */ - -public class PressedButtonRightClickTest { - - private static Robot testRobot; - private static JFrame myFrame; - private static JButton myButton; - - public static void main(String[] args) throws Throwable { - - SwingUtilities.invokeAndWait(PressedButtonRightClickTest::constructTestUI); - - try { - testRobot = new Robot(); - } catch (AWTException ex) { - throw new RuntimeException("Exception in Robot creation"); - } - - testRobot.waitForIdle(); - testRobot.delay(500); - - // Method performing auto test operation - try { - test(); - } finally { - EventQueue.invokeAndWait(PressedButtonRightClickTest::disposeTestUI); - } - } - - private static void test() { - Point loc = myFrame.getLocationOnScreen(); - - testRobot.mouseMove((loc.x + 100), (loc.y + 100)); - - // Press the left mouse button - System.out.println("press BUTTON1_DOWN_MASK"); - testRobot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - myButton.setText("Left button pressed"); - testRobot.delay(500); - - // Press the right mouse button - System.out.println("press BUTTON3_DOWN_MASK"); - testRobot.mousePress(InputEvent.BUTTON3_DOWN_MASK); - myButton.setText("Left button pressed + Right button pressed"); - testRobot.delay(500); - - // Release the right mouse button - System.out.println("release BUTTON3_DOWN_MASK"); - testRobot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); - myButton.setText("Right button released"); - testRobot.waitForIdle(); - testRobot.delay(500); - - // Test whether the button is still pressed - boolean pressed = myButton.getModel().isPressed(); - System.out.println("release BUTTON1_DOWN_MASK"); - testRobot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - if (!pressed) { - disposeTestUI(); - throw new RuntimeException("Test Failed!"); - } - } - - private static void disposeTestUI() { - myFrame.setVisible(false); - myFrame.dispose(); - } - - public static void constructTestUI() { - myFrame = new JFrame(); - myFrame.setLayout(new BorderLayout()); - myButton = new JButton("Whatever"); - myButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - System.out.println(e); - } - - @Override - public void mousePressed(MouseEvent e) { - System.out.println(e); - } - - @Override - public void mouseReleased(MouseEvent e) { - System.out.println(e); - } - - @Override - public void mouseEntered(MouseEvent e) { - System.out.println(e); - } - - @Override - public void mouseExited(MouseEvent e) { - System.out.println(e); - } - }); - myFrame.add(myButton, BorderLayout.CENTER); - myFrame.setSize(400, 300); - myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - myFrame.setLocationRelativeTo(null); - myFrame.setVisible(true); - } -} - diff --git a/test/jdk/javax/swing/JButton/bug4490179.java b/test/jdk/javax/swing/JButton/bug4490179.java index e36e01e2d95..bf2df9e7bcc 100644 --- a/test/jdk/javax/swing/JButton/bug4490179.java +++ b/test/jdk/javax/swing/JButton/bug4490179.java @@ -23,7 +23,7 @@ /* * @test - * @bug 4490179 + * @bug 4490179 8049069 * @summary Tests that JButton only responds to left mouse clicks. * @key headful * @run main bug4490179 From caf053b3ad53e4ce86d07adee6d71ea1ff3e8965 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Tue, 3 Dec 2024 15:05:13 +0000 Subject: [PATCH 081/171] 8337287: Update image in javax.swing.text.Document.insert Reviewed-by: serb, prr, abhiscxk --- .../classes/javax/swing/text/Document.java | 35 ++-- .../swing/text/doc-files/Document-insert.gif | Bin 3747 -> 0 bytes .../swing/text/doc-files/Document-insert.svg | 150 ++++++++++++++++++ 3 files changed, 171 insertions(+), 14 deletions(-) delete mode 100644 src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.gif create mode 100644 src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.svg diff --git a/src/java.desktop/share/classes/javax/swing/text/Document.java b/src/java.desktop/share/classes/javax/swing/text/Document.java index c9658aa3b21..801d6bcbbc3 100644 --- a/src/java.desktop/share/classes/javax/swing/text/Document.java +++ b/src/java.desktop/share/classes/javax/swing/text/Document.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -471,24 +471,31 @@ public interface Document { public void remove(int offs, int len) throws BadLocationException; /** - * Inserts a string of content. This will cause a DocumentEvent - * of type DocumentEvent.EventType.INSERT to be sent to the - * registered DocumentListeners, unless an exception is thrown. - * The DocumentEvent will be delivered by calling the - * insertUpdate method on the DocumentListener. - * The offset and length of the generated DocumentEvent - * will indicate what change was actually made to the Document. - *

      + * For example, if the document contains the text + * ‘The brown fox’, + * calling {@code insert(4, "quick ", null)} will insert the word + * ‘quick’ and the following space into the text, + * and all the marks at 4 and above will be moved by 6 (the number + * of inserted characters). + *

      Diagram shows insertion of 'quick' in 'The quick brown fox' *

      - * If the Document structure changed as result of the insertion, - * the details of what Elements were inserted and removed in + * If the {@code Document} structure changed as result of the insertion, + * the details of what {@code Element}s were inserted and removed in * response to the change will also be contained in the generated - * DocumentEvent. It is up to the implementation of a Document + * {@code DocumentEvent}. It is up to the implementation of a {@code Document} * to decide how the structure should change in response to an * insertion. *

      - * If the Document supports undo/redo, an UndoableEditEvent will + * If the {@code Document} supports undo/redo, an {@code UndoableEditEvent} will * also be generated. * * @param offset the offset into the document to insert the content >= 0. @@ -496,7 +503,7 @@ public interface Document { * will move. * @param str the string to insert * @param a the attributes to associate with the inserted - * content. This may be null if there are no attributes. + * content. This may be {@code null} if there are no attributes. * @throws BadLocationException the given insert position is not a valid * position within the document * @see javax.swing.event.DocumentEvent diff --git a/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.gif b/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.gif deleted file mode 100644 index 7e943ce77aaf70c2fe0bf2933ddd77c846f857fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3747 zcmbu)i9geg1Hkd`W@C#qQ%KUBxzk*8HYQABlAMJ{B;=8!*(k#>a^E9{Iku%Db01O4 zRj5aigeZp6;d~_f{eFML@AC(|U$2if-U?&r(+pw(ZvlYM=UZA@Mn*=it*xb`q&Pb} z&&jErzN8~^|i2!yt_Hl0rQ^Yd$OZ%3g}{G$MkM(gP4(9zKm6B8p6iSqLDFJHd& z_xJz!@ng?)Pe4>adS$wZsEC`to34p&WKyJ*j8t4+oRXRn))vct%0BOUzG9U!F?#uJxzPtnfIxIZE!%?IELhiGIWju-`dXf- zC@j4Z4u!(+WgY^Q&FKY?Rr#|gY=7VzTAQj#~;01^K%<0>TdUF&3 zuvKs1#X)<{hnCRip^8{61q>A!5No|ut_pNb^ofG%%Ks+x$|h2;QszLdAu-~OnxWbc zzYG^K_hPUCSOF$T!~ahZ7b{y+X6|94LC*t)WQkT~Xu35tzibU(oq5&K@_n`^LHLAy zXY0>}fn0U3kV*4wNUr)mqD`unVpgv53k>Sps1{ zbigOs9RI0;1|V!IV^4ux$Wi|+U+KYfIX0E5ekA=jUusG^ zEK__T$gKB%1;kXt&xnYrlU!T7d>BP}e9wxufEGMCY+?z;f*#nO ze^&ES4Jw;pQa^+_;S;8FjL&X_n9K%$0mNOkd!U+!WR}I=TBZ9=0DvZ1EJh^Gd_a^) zz6K7SpoV_v^u~T=h}R#)&X_TBu%!|Z?-tAvgUc-l zuj^iun2&>&#=OqDbkyYIEW4NVL()0{lYFg4+g{NDavd3SNGs*Sz<8FRn)??=Xr=S0KaCu^SAxQ}JV z!^WB2jlZvNFmQkWLEs$`jT6J+GkYte0`=Y7xO$3DXdHT*KIq940HBM4J^Q^56r2hL zy0i2U9AF!2dTw^@sgUM=!$<-^DYsO{oB@hX0pFi`MTIpzL2^z-kkIrR#n5i#8qF_q zp5A97d1i-hcLWc=lBWrk<=u%*l|rCKP~@TVt?BdF_VD`K0I?={YCm`v0&qGjYp)@ zvq112$4v5paX>NjjPWi9mKdxkm957-WB)kPhV~hi-(+~|flXG1iMPix*2n;EkDd{*C9I2Z%VB>;%*BTY-!!-thzBD$1zSg+9k){I^2k}8 zq1QhnHkc#tRcx%Mj%DX;RJEQ~wRMr^OXfe`sCK-mYIkh~Nq@6Z!>?DhC+>=r{@SQz z{7@w%+m2UCZ?bHh!F`u<$7|G{0#<{SPPInK*4!+P>+J+n3;DNoZ#2ktEt1nos`)fcBA{*p9C zJV$Reu){R2p7d}Sa&ImzY^w9o5q|$Fte~OnkH%j(b@!(^&0LD9X5b&r)LZGNK;*!@ z?5;LU1+YLEgr5t#eE%&`#0Qx_X_?F=LO(59sdnZ+!@G)!_-psHwPIdq1l|;0KEEy5 zD`j>3YWCgBip)b}-(H2}stcy>Q<<83bIGRfvJWv?L&p<8S(z-xWp2*M}2S6^qj8X;kzKP@VYDKPo_wA!wdUe;*1Ks|Se%IU` zc|Wl{$W>fryvqMt!kA%jv_!N;?|vrR;Mj~H$t2~ zeCVoK)aySVVrQWL_Tl!k~+t_8|4IK^;-06bG3v zdJd6b{U5*bKNzp@ood_OHl4St7BX2fI%NEh_!X+lbIyX70cL6i%Z9qz-t|4PJE$(Z zd8O85#^byGJGT95$Nl!zk2=h0p;Nc&m!?-O_^nf$k`sP{(97V)Pfv$^nh zf+lmI{Kj1Y6jl#r2K;nO=w7K8G>0J(V6i^(cWJ5HJkuvX9^z&KeRo|BkXq}fWmCL> zfr)OZfa+N2*R1@LzY(JT{WH;szmUwy7ZH6fmbbq8l64dY6}=yWl zJLx+pituf(De30wzCRhYu%(+fp>{y!Dp~ie2TniT;QXGPpZH@U{uB#z5)0NM-r*U5_mas?{ikn+Yk5QBl@twg7EI>> zOnibcJj(=0Gvnt*`6NdTE>Vw6s^y0pMx9lVpNzuST59PUfe1$2!)j}_2`97@L|nr4 z2ksc}Bv}>38qpFiB#W9yCG-1Fzi*_*5tHT`jT)VdTW}UPs7dNJ=R5;wVHYX=2{Bm5 zq}N*(C65fTW$kF)R-#H4P@0_T*?$_&#ZskiyiqYfYzdNpAcZk$89OOT#5B@{ z%ru(KNp7^v;gXT`H_IslH8 zl~$PYM$cOmyi{d+#?M-iQ;O&x$qc?{N?-%#uH3&F zMe!it4azz%B(RDq``JnvVr{Z~M!U*R5R#REEF@i3GI?h0jJXZF9wqByQ?PUU7%oU@ zE{s`JzEo5GiJ;Q8O+3=pT}6)dCK&@X!m!3y+k)zx-Q1l)^x62lX|V z!#U-!)C!sQ3R&CWhnv?+1uw`ORUUV)R4#T@DXu)yRH-5DsI^;p@CXxKOwe^_LL-?N zX@XHZ^UpZbY{kKR$O&sy_0zG+N{xUgJK3~Xt-P+X4 + + + + + + + + + + + + Before Insert + + + + + + 4 + + After Insert + + + + T + + h + + e + + + + + + + q + + u + + i + + c + + k + + + + + + + b + + r + + o + + w + + n + + + + f + + o + + x + + + + + + 10 + + From ba5093935ddedfecaaa80d3107dc0d84d4d18756 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Tue, 3 Dec 2024 15:44:00 +0000 Subject: [PATCH 082/171] 8341649: Regressions with large metaspace apps after 8338526 Reviewed-by: liach, stuefe --- .../classes/java/lang/invoke/InvokerBytecodeGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index ffd368f47c1..cbcd4a7b433 100644 --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -248,7 +248,7 @@ private byte[] classFileSetup(Consumer config) { return ClassFile.of().build(classEntry, pool, new Consumer<>() { @Override public void accept(ClassBuilder clb) { - clb.withFlags(ACC_ABSTRACT | ACC_SUPER) + clb.withFlags(ACC_FINAL | ACC_SUPER) .withSuperclass(INVOKER_SUPER_DESC) .with(SourceFileAttribute.of(clb.constantPool().utf8Entry(SOURCE_PREFIX + name))); config.accept(clb); From 2be27e1545a36628eef063d5a20c5e1f23e5c9ec Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 3 Dec 2024 15:56:32 +0000 Subject: [PATCH 083/171] 8345393: ProblemList java/util/concurrent/locks/StampedLock/OOMEInStampedLock.java on generic-all JTREG_TEST_THREAD_FACTORY=Virtual Reviewed-by: alanb --- test/jdk/ProblemList-Virtual.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt index d7692b578cd..eabac31ca34 100644 --- a/test/jdk/ProblemList-Virtual.txt +++ b/test/jdk/ProblemList-Virtual.txt @@ -44,6 +44,8 @@ javax/management/remote/mandatory/connection/DeadLockTest.java 8309069 windows-x javax/management/remote/mandatory/connection/ConnectionTest.java 8308352 windows-x64 +java/util/concurrent/locks/StampedLock/OOMEInStampedLock.java 8345266 generic-all + ########## ## Tests incompatible with virtual test thread factory. ## There is no goal to run all test with virtual test thread factory. From 60bd73a5957f26742e3c326cca0b45395b9470af Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Tue, 3 Dec 2024 16:18:38 +0000 Subject: [PATCH 084/171] 8342089: Require --enable-native-access to be the same between CDS dump time and run time Reviewed-by: ccheung, dholmes --- src/hotspot/share/cds/cdsConfig.cpp | 4 +- src/hotspot/share/cds/metaspaceShared.cpp | 8 +- src/hotspot/share/classfile/modules.cpp | 141 +++++++++++------- src/hotspot/share/classfile/modules.hpp | 14 +- src/hotspot/share/runtime/arguments.cpp | 35 +++-- src/hotspot/share/runtime/arguments.hpp | 6 +- .../AOTClassLinkingVMOptions.java | 2 +- .../appcds/jigsaw/addmods/AddmodsOption.java | 8 +- .../jigsaw/module/EnableNativeAccessCDS.java | 133 +++++++++++++++++ .../appcds/jigsaw/module/ModuleOption.java | 6 +- 10 files changed, 263 insertions(+), 94 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/EnableNativeAccessCDS.java diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index ba5d12e6450..6ab77bf0007 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -250,9 +250,7 @@ void CDSConfig::init_shared_archive_paths() { } void CDSConfig::check_internal_module_property(const char* key, const char* value) { - if (Arguments::is_internal_module_property(key) && - !Arguments::is_module_path_property(key) && - !Arguments::is_add_modules_property(key)) { + if (Arguments::is_incompatible_cds_internal_module_property(key)) { stop_using_optimized_module_handling(); log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value); } diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 3571f11bb6e..ba17ccddb52 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -423,8 +423,7 @@ void MetaspaceShared::write_method_handle_intrinsics() { void MetaspaceShared::early_serialize(SerializeClosure* soc) { int tag = 0; soc->do_tag(--tag); - CDS_JAVA_HEAP_ONLY(Modules::serialize(soc);) - CDS_JAVA_HEAP_ONLY(Modules::serialize_addmods_names(soc);) + CDS_JAVA_HEAP_ONLY(Modules::serialize_archived_module_info(soc);) soc->do_tag(666); } @@ -567,10 +566,7 @@ class StaticArchiveBuilder : public ArchiveBuilder { char* VM_PopulateDumpSharedSpace::dump_early_read_only_tables() { ArchiveBuilder::OtherROAllocMark mark; - // Write module name into archive - CDS_JAVA_HEAP_ONLY(Modules::dump_main_module_name();) - // Write module names from --add-modules into archive - CDS_JAVA_HEAP_ONLY(Modules::dump_addmods_names();) + CDS_JAVA_HEAP_ONLY(Modules::dump_archived_module_info()); DumpRegion* ro_region = ArchiveBuilder::current()->ro_region(); char* start = ro_region->top(); diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index 2a8f8bd2424..9c430e77d09 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -562,13 +562,55 @@ void Modules::verify_archived_modules() { char* Modules::_archived_main_module_name = nullptr; char* Modules::_archived_addmods_names = nullptr; +char* Modules::_archived_native_access_flags = nullptr; void Modules::dump_main_module_name() { const char* module_name = Arguments::get_property("jdk.module.main"); if (module_name != nullptr) { _archived_main_module_name = ArchiveBuilder::current()->ro_strdup(module_name); } - ArchivePtrMarker::mark_pointer(&_archived_main_module_name); +} + +void Modules::check_archived_flag_consistency(char* archived_flag, const char* runtime_flag, const char* property) { + log_info(cds)("%s %s", property, + archived_flag != nullptr ? archived_flag : "(null)"); + bool disable = false; + if (runtime_flag == nullptr) { + if (archived_flag != nullptr) { + log_info(cds)("Mismatched values for property %s: %s specified during dump time but not during runtime", property, archived_flag); + disable = true; + } + } else { + if (archived_flag == nullptr) { + log_info(cds)("Mismatched values for property %s: %s specified during runtime but not during dump time", property, runtime_flag); + disable = true; + } else if (strcmp(runtime_flag, archived_flag) != 0) { + log_info(cds)("Mismatched values for property %s: runtime %s dump time %s", property, runtime_flag, archived_flag); + disable = true; + } + } + + if (disable) { + log_info(cds)("Disabling optimized module handling"); + CDSConfig::stop_using_optimized_module_handling(); + } + log_info(cds)("optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled"); + log_info(cds)("full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled"); +} + +void Modules::dump_archived_module_info() { + // Write module name into archive + CDS_JAVA_HEAP_ONLY(Modules::dump_main_module_name();) + // Write module names from --add-modules into archive + CDS_JAVA_HEAP_ONLY(Modules::dump_addmods_names();) + // Write native enable-native-access flag into archive + CDS_JAVA_HEAP_ONLY(Modules::dump_native_access_flag()); +} + +void Modules::serialize_archived_module_info(SerializeClosure* soc) { + CDS_JAVA_HEAP_ONLY(Modules::serialize(soc);) + CDS_JAVA_HEAP_ONLY(Modules::serialize_addmods_names(soc);) + CDS_JAVA_HEAP_ONLY(Modules::serialize_native_access_flags(soc);) } void Modules::serialize(SerializeClosure* soc) { @@ -577,89 +619,71 @@ void Modules::serialize(SerializeClosure* soc) { const char* runtime_main_module = Arguments::get_property("jdk.module.main"); log_info(cds)("_archived_main_module_name %s", _archived_main_module_name != nullptr ? _archived_main_module_name : "(null)"); - bool disable = false; - if (runtime_main_module == nullptr) { - if (_archived_main_module_name != nullptr) { - log_info(cds)("Module %s specified during dump time but not during runtime", _archived_main_module_name); - disable = true; - } - } else { - if (_archived_main_module_name == nullptr) { - log_info(cds)("Module %s specified during runtime but not during dump time", runtime_main_module); - disable = true; - } else if (strcmp(runtime_main_module, _archived_main_module_name) != 0) { - log_info(cds)("Mismatched modules: runtime %s dump time %s", runtime_main_module, _archived_main_module_name); - disable = true; - } - } - if (disable) { - log_info(cds)("Disabling optimized module handling"); - CDSConfig::stop_using_optimized_module_handling(); - } - log_info(cds)("optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled"); - log_info(cds)("full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled"); + check_archived_flag_consistency(_archived_main_module_name, runtime_main_module, "jdk.module.main"); // Don't hold onto the pointer, in case we might decide to unmap the archive. _archived_main_module_name = nullptr; } } +void Modules::dump_native_access_flag() { + const char* native_access_names = get_native_access_flags_as_sorted_string(); + if (native_access_names != nullptr) { + _archived_native_access_flags = ArchiveBuilder::current()->ro_strdup(native_access_names); + } +} + +const char* Modules::get_native_access_flags_as_sorted_string() { + return get_numbered_property_as_sorted_string("jdk.module.enable.native.access"); +} + +void Modules::serialize_native_access_flags(SerializeClosure* soc) { + soc->do_ptr(&_archived_native_access_flags); + if (soc->reading()) { + check_archived_flag_consistency(_archived_native_access_flags, get_native_access_flags_as_sorted_string(), "jdk.module.enable.native.access"); + + // Don't hold onto the pointer, in case we might decide to unmap the archive. + _archived_native_access_flags = nullptr; + } +} + void Modules::dump_addmods_names() { - unsigned int count = Arguments::addmods_count(); const char* addmods_names = get_addmods_names_as_sorted_string(); if (addmods_names != nullptr) { _archived_addmods_names = ArchiveBuilder::current()->ro_strdup(addmods_names); } - ArchivePtrMarker::mark_pointer(&_archived_addmods_names); +} + +const char* Modules::get_addmods_names_as_sorted_string() { + return get_numbered_property_as_sorted_string("jdk.module.addmods"); } void Modules::serialize_addmods_names(SerializeClosure* soc) { soc->do_ptr(&_archived_addmods_names); if (soc->reading()) { - bool disable = false; - if (_archived_addmods_names[0] != '\0') { - if (Arguments::addmods_count() == 0) { - log_info(cds)("--add-modules module name(s) found in archive but not specified during runtime: %s", - _archived_addmods_names); - disable = true; - } else { - const char* addmods_names = get_addmods_names_as_sorted_string(); - if (strcmp((const char*)_archived_addmods_names, addmods_names) != 0) { - log_info(cds)("Mismatched --add-modules module name(s)."); - log_info(cds)(" dump time: %s runtime: %s", _archived_addmods_names, addmods_names); - disable = true; - } - } - } else { - if (Arguments::addmods_count() > 0) { - log_info(cds)("--add-modules module name(s) specified during runtime but not found in archive: %s", - get_addmods_names_as_sorted_string()); - disable = true; - } - } - if (disable) { - log_info(cds)("Disabling optimized module handling"); - CDSConfig::stop_using_optimized_module_handling(); - } - log_info(cds)("optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled"); - log_info(cds)("full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled"); + check_archived_flag_consistency(_archived_addmods_names, get_addmods_names_as_sorted_string(), "jdk.module.addmods"); // Don't hold onto the pointer, in case we might decide to unmap the archive. _archived_addmods_names = nullptr; } } -const char* Modules::get_addmods_names_as_sorted_string() { +const char* Modules::get_numbered_property_as_sorted_string(const char* property) { ResourceMark rm; - const int max_digits = 3; + // theoretical string size limit for decimal int, but the following loop will end much sooner due to + // OS command-line size limit. + const int max_digits = 10; const int extra_symbols_count = 2; // includes '.', '\0' - size_t prop_len = strlen("jdk.module.addmods") + max_digits + extra_symbols_count; + size_t prop_len = strlen(property) + max_digits + extra_symbols_count; char* prop_name = resource_allocate_bytes(prop_len); GrowableArray list; - for (unsigned int i = 0; i < Arguments::addmods_count(); i++) { - jio_snprintf(prop_name, prop_len, "jdk.module.addmods.%d", i); + for (unsigned int i = 0;; i++) { + jio_snprintf(prop_name, prop_len, "%s.%d", property, i); const char* prop_value = Arguments::get_property(prop_name); + if (prop_value == nullptr) { + break; + } char* p = resource_allocate_bytes(strlen(prop_value) + 1); strcpy(p, prop_value); while (*p == ',') p++; // skip leading commas @@ -695,11 +719,12 @@ const char* Modules::get_addmods_names_as_sorted_string() { if (strcmp(m, last_string) != 0) { // filter out duplicates st.print("%s%s", prefix, m); last_string = m; - prefix = "\n"; + prefix = ","; } } - return (const char*)os::strdup(st.as_string()); // Example: "java.base,java.compiler" + const char* result = (const char*)os::strdup(st.as_string()); // Example: "java.base,java.compiler" + return strcmp(result, "") != 0 ? result : nullptr; } void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) { diff --git a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp index 3ef6f57a57b..03069bd3452 100644 --- a/src/hotspot/share/classfile/modules.hpp +++ b/src/hotspot/share/classfile/modules.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2016, 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 @@ -59,15 +59,25 @@ class Modules : AllStatic { static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN; + static void dump_archived_module_info() NOT_CDS_JAVA_HEAP_RETURN; + static void serialize_archived_module_info(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; static void dump_main_module_name() NOT_CDS_JAVA_HEAP_RETURN; static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static void check_archived_flag_consistency(char* archived_flag, const char* runtime_flag, const char* property) NOT_CDS_JAVA_HEAP_RETURN; + + static void dump_native_access_flag() NOT_CDS_JAVA_HEAP_RETURN; + static const char* get_native_access_flags_as_sorted_string() NOT_CDS_JAVA_HEAP_RETURN_(nullptr); + static void serialize_native_access_flags(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static void dump_addmods_names() NOT_CDS_JAVA_HEAP_RETURN; - static void serialize_addmods_names(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; static const char* get_addmods_names_as_sorted_string() NOT_CDS_JAVA_HEAP_RETURN_(nullptr); + static void serialize_addmods_names(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static const char* get_numbered_property_as_sorted_string(const char* property) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); #if INCLUDE_CDS_JAVA_HEAP static char* _archived_main_module_name; static char* _archived_addmods_names; + static char* _archived_native_access_flags; #endif // Provides the java.lang.Module for the unnamed module defined diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 1917108519e..8df81ca0f94 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -320,33 +320,40 @@ static bool matches_property_suffix(const char* option, const char* property, si // any of the reserved module properties. // property should be passed without the leading "-D". bool Arguments::is_internal_module_property(const char* property) { - if (strncmp(property, MODULE_PROPERTY_PREFIX, MODULE_PROPERTY_PREFIX_LEN) == 0) { + return internal_module_property_helper(property, false); +} + +// Returns true if property is one of those recognized by is_internal_module_property() but +// is not supported by CDS archived full module graph. +bool Arguments::is_incompatible_cds_internal_module_property(const char* property) { + return internal_module_property_helper(property, true); +} + +bool Arguments::internal_module_property_helper(const char* property, bool check_for_cds) { + if (strncmp(property, MODULE_PROPERTY_PREFIX, MODULE_PROPERTY_PREFIX_LEN) == 0) { const char* property_suffix = property + MODULE_PROPERTY_PREFIX_LEN; if (matches_property_suffix(property_suffix, ADDEXPORTS, ADDEXPORTS_LEN) || matches_property_suffix(property_suffix, ADDREADS, ADDREADS_LEN) || matches_property_suffix(property_suffix, ADDOPENS, ADDOPENS_LEN) || matches_property_suffix(property_suffix, PATCH, PATCH_LEN) || - matches_property_suffix(property_suffix, ADDMODS, ADDMODS_LEN) || matches_property_suffix(property_suffix, LIMITMODS, LIMITMODS_LEN) || - matches_property_suffix(property_suffix, PATH, PATH_LEN) || matches_property_suffix(property_suffix, UPGRADE_PATH, UPGRADE_PATH_LEN) || - matches_property_suffix(property_suffix, ILLEGAL_NATIVE_ACCESS, ILLEGAL_NATIVE_ACCESS_LEN) || - matches_property_suffix(property_suffix, ENABLE_NATIVE_ACCESS, ENABLE_NATIVE_ACCESS_LEN)) { + matches_property_suffix(property_suffix, ILLEGAL_NATIVE_ACCESS, ILLEGAL_NATIVE_ACCESS_LEN)) { return true; } + + if (!check_for_cds) { + // CDS notes: these properties are supported by CDS archived full module graph. + if (matches_property_suffix(property_suffix, PATH, PATH_LEN) || + matches_property_suffix(property_suffix, ADDMODS, ADDMODS_LEN) || + matches_property_suffix(property_suffix, ENABLE_NATIVE_ACCESS, ENABLE_NATIVE_ACCESS_LEN)) { + return true; + } + } } return false; } -bool Arguments::is_add_modules_property(const char* key) { - return (strcmp(key, MODULE_PROPERTY_PREFIX ADDMODS) == 0); -} - -// Return true if the key matches the --module-path property name ("jdk.module.path"). -bool Arguments::is_module_path_property(const char* key) { - return (strcmp(key, MODULE_PROPERTY_PREFIX PATH) == 0); -} - // Process java launcher properties. void Arguments::process_sun_java_launcher_properties(JavaVMInitArgs* args) { // See if sun.java.launcher or sun.java.launcher.is_altjvm is defined. diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 0b4d89f3ad2..cac0296b327 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -365,6 +365,8 @@ class Arguments : AllStatic { static const char* handle_aliases_and_deprecation(const char* arg); static size_t _default_SharedBaseAddress; // The default value specified in globals.hpp + static bool internal_module_property_helper(const char* property, bool check_for_cds); + public: // Parses the arguments, first phase static jint parse(const JavaVMInitArgs* args); @@ -463,9 +465,7 @@ class Arguments : AllStatic { static int PropertyList_readable_count(SystemProperty* pl); static bool is_internal_module_property(const char* option); - static bool is_add_modules_property(const char* key); - static unsigned int addmods_count() { return _addmods_count; } - static bool is_module_path_property(const char* key); + static bool is_incompatible_cds_internal_module_property(const char* property); // Miscellaneous System property value getter and setters. static void set_dll_dir(const char *value) { _sun_boot_library_path->set_value(value); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java index 05fdf7c06d6..0d75d16d2b0 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java @@ -125,7 +125,7 @@ static void modulePathTests() throws Exception { "--module-path", goodModulePath + "/bad", "--add-modules", CDSModulePathUtils.TEST_MODULE, CDSModulePathUtils.MAIN_CLASS) - .assertAbnormalExit("Mismatched --add-modules module name(s)", + .assertAbnormalExit("Mismatched values for property jdk.module.addmods", "CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used."); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java index 2d8f90e61fe..7ce3e37cc8a 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java @@ -80,8 +80,8 @@ public static void main(String[] args) throws Exception { "-m", moduleOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Mismatched --add-modules module name(s).") - .shouldContain("dump time: jdk.jconsole runtime: jdk.incubator.vector") + .shouldContain("Mismatched values for property jdk.module.addmods") + .shouldContain("runtime jdk.incubator.vector dump time jdk.jconsole") .shouldContain(subgraphCannotBeUsed); // no module specified during runtime @@ -89,7 +89,7 @@ public static void main(String[] args) throws Exception { loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Module jdk.httpserver specified during dump time but not during runtime") + .shouldContain("jdk.httpserver specified during dump time but not during runtime") .shouldContain(subgraphCannotBeUsed); // dump an archive without the --add-modules option @@ -109,7 +109,7 @@ public static void main(String[] args) throws Exception { "-m", moduleOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("--add-modules module name(s) specified during runtime but not found in archive: jdk.jconsole") + .shouldContain("jdk.jconsole specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea .shouldMatch(versionPattern) .shouldContain(subgraphCannotBeUsed); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/EnableNativeAccessCDS.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/EnableNativeAccessCDS.java new file mode 100644 index 00000000000..10c4000649b --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/EnableNativeAccessCDS.java @@ -0,0 +1,133 @@ +/* + * 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 8342089 + * @summary Test consistency of --enable-native-access option for CDS dump time and runtime + * @requires vm.cds.write.archived.java.heap + * @requires vm.flagless + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @run driver EnableNativeAccessCDS + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class EnableNativeAccessCDS { + public static void main(String[] args) throws Exception { + final String module0 = "java.base"; + final String module1 = "jdk.httpserver"; + final String disabledOptimizedModule = "Disabling optimized module handling"; + final String loggingOption = "-Xlog:cds=debug"; + + String archiveName = TestCommon.getNewArchiveName("native-access"); + TestCommon.setCurrentArchiveName(archiveName); + + // dump a base archive with --enable-native-access=java.base + OutputAnalyzer oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--enable-native-access", module0, + "-version"); + oa.shouldHaveExitValue(0); + + // same module specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "--enable-native-access", module0, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("use_full_module_graph = true"); + + // different module specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "--enable-native-access", module1, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Mismatched values for property jdk.module.enable.native.access: runtime jdk.httpserver dump time java.base") + .shouldContain(disabledOptimizedModule); + + // no module specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Mismatched values for property jdk.module.enable.native.access: java.base specified during dump time but not during runtime") + .shouldContain(disabledOptimizedModule); + + // dump an archive without --enable-native-access option + archiveName = TestCommon.getNewArchiveName("no-native-access-modules"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "-version"); + oa.shouldHaveExitValue(0); + + // run with --enable-native-access + oa = TestCommon.execCommon( + loggingOption, + "--enable-native-access", module0, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Mismatched values for property jdk.module.enable.native.access: java.base specified during runtime but not during dump time") + .shouldContain(disabledOptimizedModule); + + // dump an archive with multiple modules with native access + archiveName = TestCommon.getNewArchiveName("multiple-native-access-modules"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--enable-native-access", module0 + "," + module1, + "-version"); + oa.shouldHaveExitValue(0); + + // same module specified during runtime but in a different order + oa = TestCommon.execCommon( + loggingOption, + "--enable-native-access", module1 + "," + module0, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("use_full_module_graph = true"); + + // same module specified during runtime but specifying --enable-native-access twice + oa = TestCommon.execCommon( + loggingOption, + "--enable-native-access", module0, + "--enable-native-access", module1, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("use_full_module_graph = true"); + + // run with only one same module + oa = TestCommon.execCommon( + loggingOption, + "--enable-native-access", module0, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Mismatched values for property jdk.module.enable.native.access: runtime java.base dump time java.base,jdk.httpserver") + .shouldContain(disabledOptimizedModule); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java index fa4fa5321ee..d9dcd593430 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java @@ -70,7 +70,7 @@ public static void main(String[] args) throws Exception { "-m", "jdk.compiler/com.sun.tools.javac.Main", "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Mismatched modules: runtime jdk.compiler dump time jdk.httpserver") + .shouldContain("Mismatched values for property jdk.module.main: runtime jdk.compiler dump time jdk.httpserver") .shouldContain(subgraphCannotBeUsed); // no module specified during runtime @@ -78,7 +78,7 @@ public static void main(String[] args) throws Exception { loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Module jdk.httpserver specified during dump time but not during runtime") + .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during dump time but not during runtime") .shouldContain(subgraphCannotBeUsed); // dump an archive without the module option @@ -96,7 +96,7 @@ public static void main(String[] args) throws Exception { "-m", moduleOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Module jdk.httpserver specified during runtime but not during dump time") + .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea .shouldMatch(versionPattern) .shouldContain(subgraphCannotBeUsed); From 3eaa7615cd7dc67eb78fb0a8f89d4e6662a0db37 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Tue, 3 Dec 2024 16:19:51 +0000 Subject: [PATCH 085/171] 8342086: FileInputStream.available() fails with "Incorrect function" for "nul" path (win) Reviewed-by: alanb --- .../windows/native/libjava/io_util_md.c | 12 +----- .../java/io/FileInputStream/Available.java | 39 ++++++++++++------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/java.base/windows/native/libjava/io_util_md.c b/src/java.base/windows/native/libjava/io_util_md.c index 4709bbbae24..bae9803a564 100644 --- a/src/java.base/windows/native/libjava/io_util_md.c +++ b/src/java.base/windows/native/libjava/io_util_md.c @@ -342,16 +342,8 @@ handleNonSeekAvailable(FD fd, long *pbytes) { return FALSE; } - if (! PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) { - /* PeekNamedPipe fails when at EOF. In that case we - * simply make *pbytes = 0 which is consistent with the - * behavior we get on Solaris when an fd is at EOF. - * The only alternative is to raise and Exception, - * which isn't really warranted. - */ - if (GetLastError() != ERROR_BROKEN_PIPE) { - return FALSE; - } + if (!PeekNamedPipe(han, NULL, 0, NULL, pbytes, NULL)) { + // If PeekNamedPipe fails, set the number of available bytes to zero. *pbytes = 0; } return TRUE; diff --git a/test/jdk/java/io/FileInputStream/Available.java b/test/jdk/java/io/FileInputStream/Available.java index 8759d8d1d27..e5b5b8825d3 100644 --- a/test/jdk/java/io/FileInputStream/Available.java +++ b/test/jdk/java/io/FileInputStream/Available.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -23,25 +23,38 @@ /* * @test - * @bug 4129479 - * @summary Test if available would throw an IOException - * when the stream is closed. + * @bug 4129479 8342086 + * @summary Test that available throws an IOException if the stream is + * closed, and that available works correctly with the NUL + * device on Windows + * @run junit Available */ -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import static org.junit.jupiter.api.Assertions.*; public class Available { - public static void main(String args[]) throws Exception { + @Test + void throwAfterClose() throws IOException { File file = new File(System.getProperty("test.src", "."), "Available.java"); FileInputStream fis = new FileInputStream(file); fis.close(); - try { - fis.available(); - throw new Exception - ("available should throw an exception after stream is closed"); - } - catch (IOException e) { - } + assertThrows(IOException.class, () -> fis.available()); + } + + @Test + @EnabledOnOs(OS.WINDOWS) + void nulDevice() throws IOException { + File file = new File("nul"); + FileInputStream fis = new FileInputStream(file); + int n = fis.available(); + assertEquals(0, n, "available() returned non-zero value"); } } From 3c60f0b2bb75150d49da9ab94d88b767275de5e2 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 3 Dec 2024 16:28:41 +0000 Subject: [PATCH 086/171] 8345296: AArch64: VM crashes with SIGILL when prctl is disallowed Reviewed-by: eastigeevich, phh, aph --- src/hotspot/cpu/aarch64/vm_version_aarch64.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index cf16fe3a08d..87c7862e250 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -444,7 +444,19 @@ void VM_Version::initialize() { } if (UseSVE > 0) { - _initial_sve_vector_length = get_current_sve_vector_length(); + int vl = get_current_sve_vector_length(); + if (vl < 0) { + warning("Unable to get SVE vector length on this system. " + "Disabling SVE. Specify -XX:UseSVE=0 to shun this warning."); + FLAG_SET_DEFAULT(UseSVE, 0); + } else if ((vl == 0) || ((vl % FloatRegister::sve_vl_min) != 0) || !is_power_of_2(vl)) { + warning("Detected SVE vector length (%d) should be a power of two and a multiple of %d. " + "Disabling SVE. Specify -XX:UseSVE=0 to shun this warning.", + vl, FloatRegister::sve_vl_min); + FLAG_SET_DEFAULT(UseSVE, 0); + } else { + _initial_sve_vector_length = vl; + } } // This machine allows unaligned memory accesses From e1910f2d19fce5cc78058154c7ddaaa8718973dc Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Tue, 3 Dec 2024 16:31:05 +0000 Subject: [PATCH 087/171] 8345397: Remove from g1HeapRegionRemSet.cpp Reviewed-by: shade --- src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index fe1590b94a8..c1343fd7dbf 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -22,8 +22,6 @@ * */ -#include - #include "precompiled.hpp" #include "gc/g1/g1BlockOffsetTable.inline.hpp" #include "gc/g1/g1CardSetContainers.inline.hpp" From e9f6ba05264ecb2f1ca3983ea503778f301bf280 Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Tue, 3 Dec 2024 16:45:50 +0000 Subject: [PATCH 088/171] 8345293: Fix generational Shenandoah with compact headers Reviewed-by: shade, stuefe, ysr --- src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index b2c8e0405e0..2b34b2d5cfc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -332,6 +332,11 @@ void ShenandoahHeap::increase_object_age(oop obj, uint additional_age) { uint ShenandoahHeap::get_object_age(oop obj) { markWord w = obj->mark(); assert(!w.is_marked(), "must not be forwarded"); + if (UseObjectMonitorTable) { + assert(LockingMode == LM_LIGHTWEIGHT, "Must use LW locking, too"); + assert(w.age() <= markWord::max_age, "Impossible!"); + return w.age(); + } if (w.has_monitor()) { w = w.monitor()->header(); } else if (w.is_being_inflated() || w.has_displaced_mark_helper()) { From 76e874c08e6434747ac4f4cb4d2e2edcde163b2a Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 3 Dec 2024 16:59:57 +0000 Subject: [PATCH 089/171] 8345319: Fix the tag type in PoolEntry and AnnotationValue Reviewed-by: asotona --- .../java/lang/classfile/AnnotationValue.java | 6 ++-- .../classfile/constantpool/PoolEntry.java | 2 +- .../classfile/impl/AbstractPoolEntry.java | 36 +++++++++---------- .../classfile/impl/AnnotationImpl.java | 26 +++++++------- .../classfile/impl/AnnotationReader.java | 2 +- .../com/sun/tools/javap/AnnotationWriter.java | 13 +++---- .../AnnotationDefault/ExpectedValues.java | 4 +-- 7 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java index 8e92ef59a50..45ed2fbbe81 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java @@ -456,9 +456,11 @@ default ClassDesc classSymbol() { * * @apiNote * {@code TAG_}-prefixed constants in this class, such as {@link #TAG_INT}, - * describe the possible return values of this method. + * describe the possible return values of this method. The return type is + * {@code int} for consistency with union indicator items in other union + * structures in the {@code class} file format. */ - char tag(); + int tag(); /** * {@return an enum value for an element-value pair} diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java index fdb8b497ff9..5762a92a061 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java @@ -98,7 +98,7 @@ public sealed interface PoolEntry * {@code TAG_}-prefixed constants in this class, such as {@link #TAG_UTF8}, * describe the possible return values of this method. */ - byte tag(); + int tag(); /** * {@return the index within the constant pool corresponding to this entry} diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 35b9f2dd1bb..3e50773d59e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -104,7 +104,7 @@ public int hashCode() { return hash; } - public abstract byte tag(); + public abstract int tag(); public int width() { return 1; @@ -181,7 +181,7 @@ enum State { RAW, BYTE, CHAR, STRING } } @Override - public byte tag() { + public int tag() { return TAG_UTF8; } @@ -522,7 +522,7 @@ public static final class ClassEntryImpl extends AbstractNamedEntry implements C } @Override - public byte tag() { + public int tag() { return TAG_CLASS; } @@ -582,7 +582,7 @@ public static final class PackageEntryImpl extends AbstractNamedEntry implements } @Override - public byte tag() { + public int tag() { return TAG_PACKAGE; } @@ -613,7 +613,7 @@ public static final class ModuleEntryImpl extends AbstractNamedEntry implements } @Override - public byte tag() { + public int tag() { return TAG_MODULE; } @@ -645,7 +645,7 @@ public static final class NameAndTypeEntryImpl extends AbstractRefsEntry values) } @Override - public char tag() { + public int tag() { return TAG_ARRAY; } } @@ -201,7 +201,7 @@ public char tag() { public record OfEnumImpl(Utf8Entry className, Utf8Entry constantName) implements AnnotationValue.OfEnum { @Override - public char tag() { + public int tag() { return TAG_ENUM; } } @@ -209,7 +209,7 @@ public char tag() { public record OfAnnotationImpl(Annotation annotation) implements AnnotationValue.OfAnnotation { @Override - public char tag() { + public int tag() { return TAG_ANNOTATION; } } @@ -217,7 +217,7 @@ public char tag() { public record OfClassImpl(Utf8Entry className) implements AnnotationValue.OfClass { @Override - public char tag() { + public int tag() { return TAG_CLASS; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index 5c4058e6dce..8abc221940a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -354,7 +354,7 @@ public static void writeTypeAnnotations(BufWriter buf, List list public static void writeAnnotationValue(BufWriterImpl buf, AnnotationValue value) { var tag = value.tag(); buf.writeU1(tag); - switch (value.tag()) { + switch (tag) { case TAG_BOOLEAN, TAG_BYTE, TAG_CHAR, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_LONG, TAG_SHORT, TAG_STRING -> buf.writeIndex(((AnnotationValue.OfConstant) value).constant()); case TAG_CLASS -> buf.writeIndex(((AnnotationValue.OfClass) value).className()); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AnnotationWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AnnotationWriter.java index 66b978f252c..d480cbb1690 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AnnotationWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AnnotationWriter.java @@ -225,11 +225,12 @@ private void writeIndex(PoolEntry entry, boolean resolveIndices) { } public void write(AnnotationValue value, boolean resolveIndices) { + var tagChar = (char) value.tag(); switch (value) { case AnnotationValue.OfConstant ev -> { if (resolveIndices) { var entry = ev.constant(); - switch (ev.tag()) { + switch (tagChar) { case 'B': print("(byte) "); print(constantWriter.stringValue(entry)); @@ -258,11 +259,11 @@ public void write(AnnotationValue value, boolean resolveIndices) { print("\""); break; default: - print(ev.tag() + "#" + entry.index()); + print(tagChar + "#" + entry.index()); break; } } else { - print(ev.tag() + "#" + ev.constant().index()); + print(tagChar + "#" + ev.constant().index()); } } case AnnotationValue.OfEnum ev -> { @@ -271,7 +272,7 @@ public void write(AnnotationValue value, boolean resolveIndices) { print("."); writeIndex(ev.constantName(), resolveIndices); } else { - print(ev.tag() + "#" + ev.className().index() + ".#" + print(tagChar + "#" + ev.className().index() + ".#" + ev.constantName().index()); } } @@ -280,11 +281,11 @@ public void write(AnnotationValue value, boolean resolveIndices) { print("class "); writeIndex(ev.className(), resolveIndices); } else { - print(ev.tag() + "#" + ev.className().index()); + print(tagChar + "#" + ev.className().index()); } } case AnnotationValue.OfAnnotation ev -> { - print(ev.tag()); + print(tagChar); AnnotationWriter.this.write(ev.annotation(), resolveIndices); } case AnnotationValue.OfArray ev -> { diff --git a/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/ExpectedValues.java b/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/ExpectedValues.java index 752ebc844eb..f5e23f65e88 100644 --- a/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/ExpectedValues.java +++ b/test/langtools/tools/javac/classfiles/attributes/AnnotationDefault/ExpectedValues.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -26,7 +26,7 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ExpectedValues { - char tag(); + int tag(); String name(); String[] values(); } From 293323c3e210bc2a3e45a0a9bc99b55378be91d2 Mon Sep 17 00:00:00 2001 From: Dean Long Date: Tue, 3 Dec 2024 17:05:49 +0000 Subject: [PATCH 090/171] 8340141: C1: rework ciMethod::equals following 8338471 Reviewed-by: kvn, vlivanov --- src/hotspot/share/c1/c1_GraphBuilder.cpp | 2 +- src/hotspot/share/ci/ciMethod.cpp | 36 +++++++++++++----------- src/hotspot/share/ci/ciMethod.hpp | 3 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index 02be6f8d49e..0179923bc30 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -2121,7 +2121,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) { } if (cha_monomorphic_target != nullptr) { - assert(!target->can_be_statically_bound() || target->equals(cha_monomorphic_target), ""); + assert(!target->can_be_statically_bound() || target == cha_monomorphic_target, ""); assert(!cha_monomorphic_target->is_abstract(), ""); if (!cha_monomorphic_target->can_be_statically_bound(actual_recv)) { // If we inlined because CHA revealed only a single target method, diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index a74a812c6a2..80277b91d22 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -742,6 +742,13 @@ ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller, if (target() == nullptr) { return nullptr; } + + // Redefinition support. + if (this->is_old() || root_m->is_old() || target->is_old()) { + guarantee(CURRENT_THREAD_ENV->jvmti_state_changed(), "old method not detected"); + return nullptr; + } + if (target() == root_m->get_Method()) { return root_m; } @@ -781,22 +788,6 @@ bool ciMethod::can_omit_stack_trace() const { return _can_omit_stack_trace; } -// ------------------------------------------------------------------ -// ciMethod::equals -// -// Returns true if the methods are the same, taking redefined methods -// into account. -bool ciMethod::equals(const ciMethod* m) const { - if (this == m) return true; - VM_ENTRY_MARK; - Method* m1 = this->get_Method(); - Method* m2 = m->get_Method(); - if (m1->is_old()) m1 = m1->get_new_method(); - if (m2->is_old()) m2 = m2->get_new_method(); - return m1 == m2; -} - - // ------------------------------------------------------------------ // ciMethod::resolve_invoke // @@ -835,6 +826,12 @@ ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver, boo ciMethod* result = this; if (m != get_Method()) { + // Redefinition support. + if (this->is_old() || m->is_old()) { + guarantee(CURRENT_THREAD_ENV->jvmti_state_changed(), "old method not detected"); + return nullptr; + } + result = CURRENT_THREAD_ENV->get_method(m); } @@ -1495,3 +1492,10 @@ bool ciMethod::is_consistent_info(ciMethod* declared_method, ciMethod* resolved_ } // ------------------------------------------------------------------ +// ciMethod::is_old +// +// Return true for redefined methods +bool ciMethod::is_old() const { + ASSERT_IN_VM; + return get_Method()->is_old(); +} diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index cc524930192..eecd9427585 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -360,13 +360,12 @@ class ciMethod : public ciMetadata { bool is_vector_method() const; bool is_object_initializer() const; bool is_scoped() const; + bool is_old() const; bool can_be_statically_bound(ciInstanceKlass* context) const; bool can_omit_stack_trace() const; - bool equals(const ciMethod* m) const; - // Replay data methods static void dump_name_as_ascii(outputStream* st, Method* method); void dump_name_as_ascii(outputStream* st); From 9267dfa63b1d6b3f339782d2b720055a3da8ae6a Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Tue, 3 Dec 2024 17:16:09 +0000 Subject: [PATCH 091/171] 8344589: Update IANA Language Subtag Registry to Version 2024-11-19 Reviewed-by: iris, lancea, naoto --- .../share/data/lsrdata/language-subtag-registry.txt | 12 +++++++++++- .../java/util/Locale/LanguageSubtagRegistryTest.java | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/data/lsrdata/language-subtag-registry.txt b/src/java.base/share/data/lsrdata/language-subtag-registry.txt index 3079d77ed8b..b00ea67e7e8 100644 --- a/src/java.base/share/data/lsrdata/language-subtag-registry.txt +++ b/src/java.base/share/data/lsrdata/language-subtag-registry.txt @@ -1,4 +1,4 @@ -File-Date: 2024-06-14 +File-Date: 2024-11-19 %% Type: language Subtag: aa @@ -47991,6 +47991,16 @@ Added: 2008-10-14 Prefix: kw %% Type: variant +Subtag: kleinsch +Description: Kleinschmidt orthography +Description: Allattaasitaamut +Added: 2024-07-20 +Prefix: kl +Prefix: kl-tunumiit +Comments: Orthography for Greenlandic designed by Samuel Kleinschmidt, + used from 1851 to 1973. +%% +Type: variant Subtag: kociewie Description: The Kociewie dialect of Polish Added: 2014-11-27 diff --git a/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java b/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java index cb3d4dde914..d143e025dd5 100644 --- a/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java +++ b/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java @@ -25,9 +25,9 @@ * @test * @bug 8025703 8040211 8191404 8203872 8222980 8225435 8241082 8242010 8247432 * 8258795 8267038 8287180 8302512 8304761 8306031 8308021 8313702 8318322 - * 8327631 8332424 8334418 + * 8327631 8332424 8334418 8344589 * @summary Checks the IANA language subtag registry data update - * (LSR Revision: 2024-06-14) with Locale and Locale.LanguageRange + * (LSR Revision: 2024-11-19) with Locale and Locale.LanguageRange * class methods. * @run main LanguageSubtagRegistryTest */ From 73b8b34a8c627dd31ee97f3a301bd9d92d7031ed Mon Sep 17 00:00:00 2001 From: Harshitha Onkar Date: Tue, 3 Dec 2024 17:37:16 +0000 Subject: [PATCH 092/171] 8344368: IndependenceSwingTest.java and IndependenceAWTTest.java failed: Selected text & clipboard contents differs Reviewed-by: azvegint, dnguyen, prr, kizune --- .../Independence/IndependenceAWTTest.java | 157 +++++++++-------- .../Independence/IndependenceSwingTest.java | 164 ++++++++++-------- 2 files changed, 178 insertions(+), 143 deletions(-) diff --git a/test/jdk/java/awt/datatransfer/Independence/IndependenceAWTTest.java b/test/jdk/java/awt/datatransfer/Independence/IndependenceAWTTest.java index 2ace3510d1f..990857182e7 100644 --- a/test/jdk/java/awt/datatransfer/Independence/IndependenceAWTTest.java +++ b/test/jdk/java/awt/datatransfer/Independence/IndependenceAWTTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -21,43 +21,67 @@ * questions. */ -import java.awt.*; -import java.awt.datatransfer.*; -import java.awt.event.*; -import java.util.Properties; /* * @test * @key headful + * @requires (os.family == "linux") * @summary To make sure that System & Primary clipboards should behave independently - * @author Jitender(jitender.singh@eng.sun.com) area=AWT - * @author dmitriy.ermashov@oracle.com * @library /lib/client * @build ExtendedRobot * @run main IndependenceAWTTest */ -public class IndependenceAWTTest { +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.HeadlessException; +import java.awt.Panel; +import java.awt.Point; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; - Frame frame; - Panel panel; - TextField tf1, tf2, tf3; - Clipboard sClip, pClip; +public class IndependenceAWTTest { + private static Frame frame; + private static TextField tf1; + private static TextField tf2; + private static TextField tf3; + private static Clipboard systemClip; + private static Clipboard primaryClip; + private static ExtendedRobot robot; + private static volatile Point ttf1Center; + private static volatile Point glideStartLocation; public static void main (String[] args) throws Exception { - new IndependenceAWTTest().doTest(); + try { + robot = new ExtendedRobot(); + EventQueue.invokeAndWait(IndependenceAWTTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + test(); + } finally { + EventQueue.invokeAndWait(frame::dispose); + } } - public IndependenceAWTTest() { - - frame = new Frame(); + private static void createAndShowUI() { + frame = new Frame("IndependenceAWTTest"); frame.setSize(200, 200); // This textfield will be used to update the contents of clipboards tf1 = new TextField(); tf1.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent fe) { - tf1.setText("Clipboards_Independance_Testing"); + tf1.setText("Clipboards_Independence_Testing"); } }); @@ -65,7 +89,7 @@ public void focusGained(FocusEvent fe) { tf2 = new TextField(); tf3 = new TextField(); - panel = new Panel(); + Panel panel = new Panel(); panel.setLayout(new BorderLayout()); panel.add(tf2, BorderLayout.NORTH); @@ -73,38 +97,36 @@ public void focusGained(FocusEvent fe) { frame.add(tf1, BorderLayout.NORTH); frame.add(panel, BorderLayout.CENTER); + frame.setLocationRelativeTo(null); frame.setVisible(true); tf1.requestFocus(); } // Get System Selection i.e. Primary Clipboard - private void getPrimaryClipboard() { - Properties ps = System.getProperties(); - String operSys = ps.getProperty("os.name"); + private static void getPrimaryClipboard() { try { - pClip = Toolkit.getDefaultToolkit().getSystemSelection(); - if (pClip == null) - if ((operSys.substring(0,3)).equalsIgnoreCase("Win") || operSys.toLowerCase().contains("os x")) - System.out.println(operSys + "Operating system does not support system selection "); - else - throw new RuntimeException("Method getSystemSelection() is returning null on X11 platform"); - } catch(HeadlessException e) { + primaryClip = Toolkit.getDefaultToolkit().getSystemSelection(); + if (primaryClip == null) { + throw new RuntimeException("Method getSystemSelection() is returning null" + + " on Linux platform"); + } + } catch (HeadlessException e) { System.out.println("Headless exception thrown " + e); } } // Method to get the contents of both of the clipboards - public void getClipboardsContent() throws Exception { - sClip = Toolkit.getDefaultToolkit().getSystemClipboard(); + private static void getClipboardsContent() throws Exception { + systemClip = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable tp; Transferable ts; StringSelection content = new StringSelection(tf1.getText()); - sClip.setContents(content,content); + systemClip.setContents(content, content); - tp = pClip.getContents(this); - ts = sClip.getContents(this); + tp = primaryClip.getContents(null); + ts = systemClip.getContents(null); // Paste the contents of System clipboard on textfield tf2 while the paste the contents of // of primary clipboard on textfiled tf3 @@ -122,7 +144,7 @@ public void getClipboardsContent() throws Exception { } // Method to compare the Contents return by system & primary clipboard - public void compareText (boolean mustEqual) { + private static void compareText (boolean mustEqual) { if ((tf2.getText()).equals(tf3.getText())) { if (mustEqual) System.out.println("Selected text & clipboard contents are same\n"); @@ -136,40 +158,39 @@ public void compareText (boolean mustEqual) { } } - public void doTest() throws Exception { + private static void test() throws Exception { getPrimaryClipboard(); - ExtendedRobot robot = new ExtendedRobot(); - robot.waitForIdle(1000); - frame.setLocation(100, 100); - robot.waitForIdle(1000); - - if (pClip != null) { - Point ttf1Center = tf1.getLocationOnScreen(); - ttf1Center.translate(tf1.getWidth()/2, tf1.getHeight()/2); - - robot.glide(new Point(0, 0), ttf1Center); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(20); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(20); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(20); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(2000); - - getClipboardsContent(); - compareText(true); - - //Change the text selection to update the contents of primary clipboard - robot.mouseMove(ttf1Center); - robot.mousePress(MouseEvent.BUTTON1_MASK); - robot.delay(200); - robot.mouseMove(ttf1Center.x + 15, ttf1Center.y); - robot.mouseRelease(MouseEvent.BUTTON1_MASK); - robot.waitForIdle(2000); - - getClipboardsContent(); - compareText(false); - } + robot.waitForIdle(500); + + EventQueue.invokeAndWait(() -> { + Point center = tf1.getLocationOnScreen(); + center.translate(tf1.getWidth() / 2, tf1.getHeight() / 2); + ttf1Center = center; + + glideStartLocation = frame.getLocationOnScreen(); + glideStartLocation.x -= 10; + }); + + robot.glide(glideStartLocation, ttf1Center); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(20); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(500); + + getClipboardsContent(); + compareText(true); + + //Change the text selection to update the contents of primary clipboard + robot.mouseMove(ttf1Center); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(20); + robot.mouseMove(ttf1Center.x + 15, ttf1Center.y); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(500); + + getClipboardsContent(); + compareText(false); } } diff --git a/test/jdk/java/awt/datatransfer/Independence/IndependenceSwingTest.java b/test/jdk/java/awt/datatransfer/Independence/IndependenceSwingTest.java index 4ee7dea6a54..b2f594c13ae 100644 --- a/test/jdk/java/awt/datatransfer/Independence/IndependenceSwingTest.java +++ b/test/jdk/java/awt/datatransfer/Independence/IndependenceSwingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -21,8 +21,21 @@ * questions. */ -import javax.swing.*; -import java.awt.*; +/* + * @test + * @key headful + * @requires (os.family == "linux") + * @summary To make sure that System & Primary clipboards should behave independently + * @library /lib/client + * @build ExtendedRobot + * @run main IndependenceSwingTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.HeadlessException; +import java.awt.Point; +import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; @@ -31,40 +44,44 @@ import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; -import java.util.Properties; -/* - * @test - * @key headful - * @summary To make sure that System & Primary clipboards should behave independently - * @author Jitender(jitender.singh@eng.sun.com) area=AWT - * @author dmitriy.ermashov@oracle.com - * @library /lib/client - * @build ExtendedRobot - * @run main IndependenceSwingTest - */ +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; public class IndependenceSwingTest { - - JFrame frame; - JPanel panel; - JTextField tf1, tf2, tf3; - Clipboard sClip, pClip; + private static JFrame frame; + private static JTextField tf1; + private static JTextField tf2; + private static JTextField tf3; + private static Clipboard systemClip; + private static Clipboard primaryClip; + private static ExtendedRobot robot; + private static volatile Point ttf1Center; + private static volatile Point glideStartLocation; public static void main (String[] args) throws Exception { - new IndependenceSwingTest().doTest(); + try { + robot = new ExtendedRobot(); + SwingUtilities.invokeAndWait(IndependenceSwingTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + test(); + } finally { + SwingUtilities.invokeAndWait(frame::dispose); + } } - public IndependenceSwingTest() { - - frame = new JFrame(); + private static void createAndShowUI() { + frame = new JFrame("IndependenceSwingTest"); frame.setSize(200, 200); // This textfield will be used to update the contents of clipboards tf1 = new JTextField(); tf1.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent fe) { - tf1.setText("Clipboards_Independance_Testing"); + tf1.setText("Clipboards_Independence_Testing"); } }); @@ -72,7 +89,7 @@ public void focusGained(FocusEvent fe) { tf2 = new JTextField(); tf3 = new JTextField(); - panel = new JPanel(); + JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(tf2, BorderLayout.NORTH); @@ -80,38 +97,36 @@ public void focusGained(FocusEvent fe) { frame.add(tf1, BorderLayout.NORTH); frame.add(panel, BorderLayout.CENTER); + frame.setLocationRelativeTo(null); frame.setVisible(true); tf1.requestFocus(); } // Get System Selection i.e. Primary Clipboard - private void getPrimaryClipboard() { - Properties ps = System.getProperties(); - String operSys = ps.getProperty("os.name"); + private static void getPrimaryClipboard() { try { - pClip = Toolkit.getDefaultToolkit().getSystemSelection(); - if (pClip == null) - if ((operSys.substring(0,3)).equalsIgnoreCase("Win") || operSys.toLowerCase().contains("os x")) - System.out.println(operSys + "Operating system does not support system selection "); - else - throw new RuntimeException("Method getSystemSelection() is returning null on X11 platform"); - } catch(HeadlessException e) { + primaryClip = Toolkit.getDefaultToolkit().getSystemSelection(); + if (primaryClip == null) { + throw new RuntimeException("Method getSystemSelection() is returning null" + + " on Linux platform"); + } + } catch (HeadlessException e) { System.out.println("Headless exception thrown " + e); } } // Method to get the contents of both of the clipboards - public void getClipboardsContent() throws Exception { - sClip = Toolkit.getDefaultToolkit().getSystemClipboard(); + private static void getClipboardsContent() throws Exception { + systemClip = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable tp; Transferable ts; StringSelection content = new StringSelection(tf1.getText()); - sClip.setContents(content,content); + systemClip.setContents(content, content); - tp = pClip.getContents(this); - ts = sClip.getContents(this); + tp = primaryClip.getContents(null); + ts = systemClip.getContents(null); // Paste the contents of System clipboard on textfield tf2 while the paste the contents of // of primary clipboard on textfiled tf3 @@ -129,7 +144,7 @@ public void getClipboardsContent() throws Exception { } // Method to compare the Contents return by system & primary clipboard - public void compareText (boolean mustEqual) { + private static void compareText (boolean mustEqual) { if ((tf2.getText()).equals(tf3.getText())) { if (mustEqual) System.out.println("Selected text & clipboard contents are same\n"); @@ -143,40 +158,39 @@ public void compareText (boolean mustEqual) { } } - public void doTest() throws Exception { + private static void test() throws Exception { getPrimaryClipboard(); - ExtendedRobot robot = new ExtendedRobot(); - robot.waitForIdle(1000); - frame.setLocation(100, 100); - robot.waitForIdle(1000); - - if (pClip != null) { - Point ttf1Center = tf1.getLocationOnScreen(); - ttf1Center.translate(tf1.getWidth()/2, tf1.getHeight()/2); - - robot.glide(new Point(0, 0), ttf1Center); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(20); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(20); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(20); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - robot.waitForIdle(2000); - - getClipboardsContent(); - compareText(true); - - //Change the text selection to update the contents of primary clipboard - robot.mouseMove(ttf1Center); - robot.mousePress(MouseEvent.BUTTON1_MASK); - robot.delay(200); - robot.mouseMove(ttf1Center.x + 15, ttf1Center.y); - robot.mouseRelease(MouseEvent.BUTTON1_MASK); - robot.waitForIdle(2000); - - getClipboardsContent(); - compareText(false); - } + robot.waitForIdle(500); + + SwingUtilities.invokeAndWait(() -> { + Point center = tf1.getLocationOnScreen(); + center.translate(tf1.getWidth() / 2, tf1.getHeight() / 2); + ttf1Center = center; + + glideStartLocation = frame.getLocationOnScreen(); + glideStartLocation.x -= 10; + }); + + robot.glide(glideStartLocation, ttf1Center); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(20); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(500); + + getClipboardsContent(); + compareText(true); + + //Change the text selection to update the contents of primary clipboard + robot.mouseMove(ttf1Center); + robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); + robot.delay(20); + robot.mouseMove(ttf1Center.x + 15, ttf1Center.y); + robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(500); + + getClipboardsContent(); + compareText(false); } } From f37f64df8c44fffa25a0b337193d67016f8380f3 Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Tue, 3 Dec 2024 18:18:13 +0000 Subject: [PATCH 093/171] 8343736: Test java/awt/Focus/UnaccessibleChoice/AccessibleChoiceTest.java failed: Choice can't be controlled by keyboard Reviewed-by: honkar, abhiscxk --- .../AccessibleChoiceTest.java | 97 +++++++++++-------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/test/jdk/java/awt/Focus/UnaccessibleChoice/AccessibleChoiceTest.java b/test/jdk/java/awt/Focus/UnaccessibleChoice/AccessibleChoiceTest.java index 7f17c1978e4..c30e29edd7d 100644 --- a/test/jdk/java/awt/Focus/UnaccessibleChoice/AccessibleChoiceTest.java +++ b/test/jdk/java/awt/Focus/UnaccessibleChoice/AccessibleChoiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -23,74 +23,75 @@ import java.awt.Button; import java.awt.Choice; +import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Robot; -import java.awt.Window; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; -import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; -/** +/* * @test * @bug 4478780 * @key headful * @summary Tests that Choice can be accessed and controlled by keyboard. */ -public class AccessibleChoiceTest { - //Declare things used in the test, like buttons and labels here - Frame frame = new Frame("window owner"); - Window win = new Window(frame); - Choice choice = new Choice(); - Button def = new Button("default owner"); - CountDownLatch go = new CountDownLatch(1); - - public static void main(final String[] args) throws IOException { - AccessibleChoiceTest app = new AccessibleChoiceTest(); - app.test(); - } - private void test() throws IOException { +public class AccessibleChoiceTest { + static Frame frame; + static Choice choice; + static Button button; + static volatile CountDownLatch go; + static volatile Point loc; + static volatile int bWidth; + static volatile int bHeight; + + public static void main(final String[] args) throws Exception { try { - init(); - start(); + createAndShowUI(); + test(); } finally { - if (frame != null) frame.dispose(); - if (win != null) win.dispose(); + if (frame != null) { + EventQueue.invokeAndWait(() -> frame.dispose()); + } } } - public void init() { - win.setLayout (new FlowLayout ()); - win.add(def); - def.addFocusListener(new FocusAdapter() { + public static void createAndShowUI() throws Exception { + go = new CountDownLatch(1); + EventQueue.invokeAndWait(() -> { + frame = new Frame("Accessible Choice Test Frame"); + choice = new Choice(); + button = new Button("default owner"); + frame.setLayout(new FlowLayout()); + frame.add(button); + button.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { go.countDown(); } }); - choice.add("One"); - choice.add("Two"); - win.add(choice); + choice.add("One"); + choice.add("Two"); + frame.add(choice); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); } - public void start () throws IOException { - frame.setVisible(true); - win.pack(); - win.setLocation(100, 200); - win.setVisible(true); - - Robot robot = null; + public static void test() throws Exception { + Robot robot; try { robot = new Robot(); } catch (Exception ex) { @@ -98,12 +99,17 @@ public void start () throws IOException { } robot.waitForIdle(); robot.delay(1000); - robot.setAutoDelay(150); robot.setAutoWaitForIdle(true); // Focus default button and wait till it gets focus - Point loc = def.getLocationOnScreen(); - robot.mouseMove(loc.x+2, loc.y+2); + EventQueue.invokeAndWait(() -> { + loc = button.getLocationOnScreen(); + bWidth = button.getWidth(); + bHeight = button.getHeight(); + }); + robot.mouseMove(loc.x + bWidth / 2, + loc.y + bHeight / 2); + robot.delay(500); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); @@ -113,25 +119,30 @@ public void start () throws IOException { throw new RuntimeException("Interrupted !!!"); } - if (!def.isFocusOwner()) { + if (!button.isFocusOwner()) { throw new RuntimeException("Button doesn't have focus"); } + robot.delay(500); + // Press Tab key to move focus to Choice robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_TAB); robot.delay(500); - // Press Down key to select next item in the choice(Motif 2.1) + if (!choice.isFocusOwner()) { + throw new RuntimeException("Choice doesn't have focus"); + } + + // Press Down key to select next item in the choice // If bug exists we won't be able to do so robot.keyPress(KeyEvent.VK_DOWN); robot.keyRelease(KeyEvent.VK_DOWN); - robot.delay(500); - String osName = System.getProperty("os.name").toLowerCase(); if (osName.startsWith("mac")) { + robot.delay(500); robot.keyPress(KeyEvent.VK_DOWN); robot.keyRelease(KeyEvent.VK_DOWN); robot.delay(500); @@ -142,7 +153,7 @@ public void start () throws IOException { robot.delay(1000); // On success second item should be selected - if (choice.getSelectedItem() != choice.getItem(1)) { + if (!choice.getSelectedItem().equals(choice.getItem(1))) { // Print out os name to check if mac conditional is relevant System.err.println("Failed on os: " + osName); From 2be07b5f9d2f3f0b885feb08ff10a57824ea5748 Mon Sep 17 00:00:00 2001 From: Dmitry Markov Date: Tue, 3 Dec 2024 18:37:27 +0000 Subject: [PATCH 094/171] 8324491: Keyboard layout didn't keep its state if it was changed when dialog was active Reviewed-by: aivanov, azvegint --- .../windows/classes/sun/awt/windows/WInputMethod.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java index 1dcd4bf11af..e893a58f9ed 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WInputMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -319,6 +319,10 @@ public void activate() { isLastFocussedActiveClient = isAc; } isActive = true; + + // Sync currentLocale with the Windows keyboard layout which could be changed + // while the component was inactive. + getLocale(); if (currentLocale != null) { setLocale(currentLocale, true); } From a49f0776eb176129f558b6fab3f50e0453f8cbcb Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Tue, 3 Dec 2024 18:44:31 +0000 Subject: [PATCH 095/171] 8345221: Replace legacy with new Provider APIs in SunNativeGSS Co-authored-by: Francisco Ferrari Bihurriet Co-authored-by: Martin Balao Reviewed-by: weijun --- .../jgss/wrapper/SunNativeProvider.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java index 00ba08f1028..2da15b8b6a6 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java @@ -26,7 +26,6 @@ package sun.security.jgss.wrapper; import java.io.Serial; -import java.util.HashMap; import java.security.Provider; import jdk.internal.util.OperatingSystem; @@ -64,10 +63,10 @@ static void debug(String message) { System.err.println(NAME + ": " + message); } - private static final HashMap MECH_MAP = constructMechMap(); + private static final Oid[] MECH_OIDS = getMechOIDs(); @SuppressWarnings("restricted") - private static HashMap constructMechMap() { + private static Oid[] getMechOIDs() { try { // Ensure the InetAddress class is loaded before // loading j2gss. The library will access this class @@ -112,28 +111,29 @@ private static HashMap constructMechMap() { debug("Loaded GSS library: " + libName); } Oid[] mechs = GSSLibStub.indicateMechs(); - HashMap map = new HashMap<>(); - for (int i = 0; i < mechs.length; i++) { - if (DEBUG) { - debug("Native MF for " + mechs[i]); + if (DEBUG) { + for (Oid mech : mechs) { + debug("Native MF for " + mech); } - map.put("GssApiMechanism." + mechs[i], MF_CLASS); } - return map; + return mechs; } } return null; } - // initialize INSTANCE after MECH_MAP is constructed + // initialize INSTANCE after MECH_OIDS is constructed static final Provider INSTANCE = new SunNativeProvider(); public SunNativeProvider() { /* We are the Sun NativeGSS provider */ super(NAME, PROVIDER_VER, INFO); - if (MECH_MAP != null) { - putAll(MECH_MAP); + if (MECH_OIDS != null) { + for (Oid mech : MECH_OIDS) { + putService(new Service(this, "GssApiMechanism", + mech.toString(), MF_CLASS, null, null)); + } } } } From 157a4341f759931c178fdb5759dbb4b16df3dbf7 Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Tue, 3 Dec 2024 19:58:01 +0000 Subject: [PATCH 096/171] 8345389: Bump missed copyright years for JDK-8336768 Reviewed-by: pminborg --- .../share/classes/jdk/internal/foreign/abi/LinkerOptions.java | 2 +- .../classes/jdk/internal/foreign/abi/NativeEntryPoint.java | 2 +- src/java.base/share/native/libfallbackLinker/fallbackLinker.c | 2 +- test/jdk/java/foreign/TestIllegalLink.java | 2 +- .../jdk/java/foreign/capturecallstate/TestCaptureCallState.java | 2 +- test/jdk/java/foreign/critical/TestCritical.java | 2 +- test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 9a19b5a8511..5ca410f40e2 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java index 3926060e6bf..abcc6027919 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c index 1548fb49e26..5a85fedd774 100644 --- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 diff --git a/test/jdk/java/foreign/TestIllegalLink.java b/test/jdk/java/foreign/TestIllegalLink.java index 2a9d5ad3782..45239e3cb45 100644 --- a/test/jdk/java/foreign/TestIllegalLink.java +++ b/test/jdk/java/foreign/TestIllegalLink.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 diff --git a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java index a1bf1183fb5..7b34ef76add 100644 --- a/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java +++ b/test/jdk/java/foreign/capturecallstate/TestCaptureCallState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 diff --git a/test/jdk/java/foreign/critical/TestCritical.java b/test/jdk/java/foreign/critical/TestCritical.java index 85aa962383b..60b76623b0e 100644 --- a/test/jdk/java/foreign/critical/TestCritical.java +++ b/test/jdk/java/foreign/critical/TestCritical.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 diff --git a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java index 375c672a606..4ca0eb7bb82 100644 --- a/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java +++ b/test/jdk/java/foreign/passheapsegment/TestPassHeapSegment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 From 0664b517650c622dcf21f8bd2e3e389e7d81bbab Mon Sep 17 00:00:00 2001 From: Leonid Mesnik Date: Tue, 3 Dec 2024 20:32:36 +0000 Subject: [PATCH 097/171] 8344987: Test serviceability/sa/TestJhsdbJstackPrintVMLocks.java fails: NoClassDefFoundError: jdk/test/lib/Utils Reviewed-by: cjplummer --- test/hotspot/jtreg/TEST.groups | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index d04ed90ae07..d68c015496c 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -491,6 +491,7 @@ hotspot_cds_relocation = \ runtime/modules/PatchModule/PatchModuleClassList.java \ runtime/NMT \ serviceability/sa \ + -serviceability/sa/TestJhsdbJstackPrintVMLocks.java \ -runtime/cds/DeterministicDump.java hotspot_cds_verify_shared_spaces = \ From 05ee562a38bf7325becdd04f2e9d3238b95a4cb0 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 3 Dec 2024 20:41:48 +0000 Subject: [PATCH 098/171] 8343839: Detect patched modules and abort run-time image link early Reviewed-by: mchung --- .../jlink/internal/ImageFileCreator.java | 42 +++---------- .../jdk/tools/jlink/internal/JRTArchive.java | 19 ++---- .../jdk/tools/jlink/internal/JlinkTask.java | 12 +++- .../RuntimeImageLinkException.java | 62 ------------------- .../tools/jlink/resources/jlink.properties | 4 +- .../jdk/tools/jlink/ImageFileCreatorTest.java | 2 +- .../runtimeImage/ModifiedFilesExitTest.java | 2 +- .../ModifiedFilesWarningTest.java | 2 +- .../PatchedJDKModuleJlinkTest.java | 6 +- 9 files changed, 30 insertions(+), 121 deletions(-) delete mode 100644 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/runtimelink/RuntimeImageLinkException.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java index 466c5d0a14c..9e05fe31aa9 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java @@ -32,6 +32,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -56,7 +57,6 @@ import jdk.tools.jlink.internal.runtimelink.JimageDiffGenerator.ImageResource; import jdk.tools.jlink.internal.runtimelink.ResourceDiff; import jdk.tools.jlink.internal.runtimelink.ResourcePoolReader; -import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException; import jdk.tools.jlink.plugin.PluginException; import jdk.tools.jlink.plugin.ResourcePool; import jdk.tools.jlink.plugin.ResourcePoolBuilder; @@ -91,14 +91,11 @@ public final class ImageFileCreator { private final Map> entriesForModule = new HashMap<>(); private final ImagePluginStack plugins; private final boolean generateRuntimeImage; - private final TaskHelper helper; private ImageFileCreator(ImagePluginStack plugins, - boolean generateRuntimeImage, - TaskHelper taskHelper) { + boolean generateRuntimeImage) { this.plugins = Objects.requireNonNull(plugins); this.generateRuntimeImage = generateRuntimeImage; - this.helper = taskHelper; } /** @@ -118,24 +115,20 @@ private ImageFileCreator(ImagePluginStack plugins, public static ExecutableImage create(Set archives, ByteOrder byteOrder, ImagePluginStack plugins, - boolean generateRuntimeImage, - TaskHelper taskHelper) + boolean generateRuntimeImage) throws IOException { ImageFileCreator image = new ImageFileCreator(plugins, - generateRuntimeImage, - taskHelper); + generateRuntimeImage); try { image.readAllEntries(archives); // write to modular image image.writeImage(archives, byteOrder); - } catch (RuntimeImageLinkException e) { - // readAllEntries() might throw this exception. - // Propagate as IOException with appropriate message for - // jlink runs from the run-time image. This handles better - // error messages for the case of modified files in the run-time - // image. - throw image.newIOException(e); + } catch (UncheckedIOException e) { + // When linking from the run-time image, readAllEntries() might + // throw this exception for a modified runtime. Unpack and + // re-throw as IOException. + throw e.getCause(); } finally { // Close all archives for (Archive a : archives) { @@ -200,11 +193,6 @@ private void writeImage(Set archives, ResourcePool result = null; try (DataOutputStream out = plugins.getJImageFileOutputStream()) { result = generateJImage(allContent, writer, plugins, out, generateRuntimeImage); - } catch (RuntimeImageLinkException e) { - // Propagate as IOException with appropriate message for - // jlink runs from the run-time image. This handles better - // error messages for the case of --patch-module. - throw newIOException(e); } //Handle files. @@ -218,18 +206,6 @@ private void writeImage(Set archives, } } - private IOException newIOException(RuntimeImageLinkException e) throws IOException { - if (JlinkTask.DEBUG) { - e.printStackTrace(); - } - String message = switch (e.getReason()) { - case PATCH_MODULE -> helper.getMessage("err.runtime.link.patched.module", e.getFile()); - case MODIFIED_FILE -> helper.getMessage("err.runtime.link.modified.file", e.getFile()); - default -> throw new AssertionError("Unexpected value: " + e.getReason()); - }; - throw new IOException(message); - } - /** * Create a jimage based on content of the given ResourcePoolManager, * optionally creating a runtime that can be used for linking from the diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JRTArchive.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JRTArchive.java index 755afea8c60..df7d35ac777 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JRTArchive.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JRTArchive.java @@ -26,8 +26,6 @@ package jdk.tools.jlink.internal; import static jdk.tools.jlink.internal.LinkableRuntimeImage.RESPATH_PATTERN; -import static jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException.Reason.MODIFIED_FILE; -import static jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException.Reason.PATCH_MODULE; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -37,7 +35,6 @@ import java.lang.module.ModuleReference; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.security.MessageDigest; @@ -56,7 +53,6 @@ import jdk.internal.util.OperatingSystem; import jdk.tools.jlink.internal.Archive.Entry.EntryType; import jdk.tools.jlink.internal.runtimelink.ResourceDiff; -import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException; import jdk.tools.jlink.plugin.ResourcePoolEntry; import jdk.tools.jlink.plugin.ResourcePoolEntry.Type; @@ -223,7 +219,9 @@ private void addNonClassResources() { Path path = BASE.resolve(m.resPath); if (shaSumMismatch(path, m.hashOrTarget, m.symlink)) { if (errorOnModifiedFile) { - throw new RuntimeImageLinkException(path.toString(), MODIFIED_FILE); + String msg = taskHelper.getMessage("err.runtime.link.modified.file", path.toString()); + IOException cause = new IOException(msg); + throw new UncheckedIOException(cause); } else { taskHelper.warning("err.runtime.link.modified.file", path.toString()); } @@ -460,16 +458,7 @@ public long size() { // the underlying base path is a JrtPath with the // JrtFileSystem underneath which is able to handle // this size query. - try { - return Files.size(archive.getPath().resolve(resPath)); - } catch (NoSuchFileException file) { - // This indicates that we don't find the class in the - // modules image using the JRT FS provider. Yet, we find - // the class using the system module finder. Therefore, - // we have a patched module. Mention that module patching - // is not supported. - throw new RuntimeImageLinkException(file.getFile(), PATCH_MODULE); - } + return Files.size(archive.getPath().resolve(resPath)); } } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 15998d6b929..0eda0b5d455 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -63,6 +63,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.ModulePath; import jdk.internal.module.ModuleReferenceImpl; import jdk.internal.module.ModuleResolution; @@ -73,7 +74,6 @@ import jdk.tools.jlink.internal.TaskHelper.BadArgs; import jdk.tools.jlink.internal.TaskHelper.Option; import jdk.tools.jlink.internal.TaskHelper.OptionsHelper; -import jdk.tools.jlink.internal.runtimelink.RuntimeImageLinkException; import jdk.tools.jlink.plugin.PluginException; /** @@ -309,7 +309,7 @@ int run(String[] args) { } cleanupOutput(outputPath); return EXIT_ERROR; - } catch (IllegalArgumentException | ResolutionException | RuntimeImageLinkException e) { + } catch (IllegalArgumentException | ResolutionException e) { log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage()); if (DEBUG) { e.printStackTrace(log); @@ -620,6 +620,12 @@ private static ImageHelper createImageProvider(JlinkConfiguration config, String msg = taskHelper.getMessage("err.runtime.link.jdk.jlink.prohibited"); throw new IllegalArgumentException(msg); } + // Do not permit linking from run-time image when the current image + // is being patched. + if (ModuleBootstrap.patcher().hasPatches()) { + String msg = taskHelper.getMessage("err.runtime.link.patched.module"); + throw new IllegalArgumentException(msg); + } // Print info message indicating linking from the run-time image if (verbose && log != null) { @@ -1039,7 +1045,7 @@ private static record ImageHelper(Set archives, @Override public ExecutableImage retrieve(ImagePluginStack stack) throws IOException { ExecutableImage image = ImageFileCreator.create(archives, - targetPlatform.arch().byteOrder(), stack, generateRuntimeImage, taskHelper); + targetPlatform.arch().byteOrder(), stack, generateRuntimeImage); if (packagedModulesPath != null) { // copy the packaged modules to the given path Files.createDirectories(packagedModulesPath); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/runtimelink/RuntimeImageLinkException.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/runtimelink/RuntimeImageLinkException.java deleted file mode 100644 index 9f54fd63476..00000000000 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/runtimelink/RuntimeImageLinkException.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024, Red Hat, Inc. - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package jdk.tools.jlink.internal.runtimelink; - -import java.util.Objects; - -/** - * Exception thrown when linking from the run-time image - */ -public class RuntimeImageLinkException extends RuntimeException { - - private static final long serialVersionUID = -1848914673073119403L; - - public static enum Reason { - PATCH_MODULE, /* link exception due to patched module */ - MODIFIED_FILE, /* link exception due to modified file */ - } - - private final String file; - private final Reason reason; - - public RuntimeImageLinkException(String file, Reason reason) { - this.file = Objects.requireNonNull(file); - this.reason = Objects.requireNonNull(reason); - } - - public String getFile() { - return file; - } - - public Reason getReason() { - return reason; - } - - @Override - public String getMessage() { - return reason + ", file: " + file; - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties index 9e18177d9c8..b5880a35561 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties @@ -125,8 +125,8 @@ err.runtime.link.jdk.jlink.prohibited=This JDK does not contain packaged modules err.runtime.link.packaged.mods=This JDK has no packaged modules.\ \ --keep-packaged-modules is not supported err.runtime.link.modified.file={0} has been modified -err.runtime.link.patched.module=File {0} not found in the modules image.\ -\ --patch-module is not supported when linking from the run-time image +err.runtime.link.patched.module=jlink does not support linking from the run-time image\ +\ when running on a patched runtime with --patch-module err.empty.module.path=empty module path err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3} err.automatic.module:automatic module cannot be used with jlink: {0} from {1} diff --git a/test/jdk/tools/jlink/ImageFileCreatorTest.java b/test/jdk/tools/jlink/ImageFileCreatorTest.java index b6466c6a4d9..52a878e566a 100644 --- a/test/jdk/tools/jlink/ImageFileCreatorTest.java +++ b/test/jdk/tools/jlink/ImageFileCreatorTest.java @@ -224,6 +224,6 @@ public void storeFiles(ResourcePool content) { ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(), null, false); - ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack, false, null); + ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack, false); } } diff --git a/test/jdk/tools/jlink/runtimeImage/ModifiedFilesExitTest.java b/test/jdk/tools/jlink/runtimeImage/ModifiedFilesExitTest.java index 90abe14c214..777ce302ce7 100644 --- a/test/jdk/tools/jlink/runtimeImage/ModifiedFilesExitTest.java +++ b/test/jdk/tools/jlink/runtimeImage/ModifiedFilesExitTest.java @@ -78,7 +78,7 @@ public boolean test(OutputAnalyzer t) { } analyzer.stdoutShouldContain(modifiedFile.toString() + " has been modified"); // Verify the error message is reasonable - analyzer.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException"); + analyzer.stdoutShouldNotContain("IOException"); analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException"); } diff --git a/test/jdk/tools/jlink/runtimeImage/ModifiedFilesWarningTest.java b/test/jdk/tools/jlink/runtimeImage/ModifiedFilesWarningTest.java index 935d80dee4f..c871024f37c 100644 --- a/test/jdk/tools/jlink/runtimeImage/ModifiedFilesWarningTest.java +++ b/test/jdk/tools/jlink/runtimeImage/ModifiedFilesWarningTest.java @@ -70,6 +70,6 @@ void testAndAssert(Path modifiedFile, Helper helper, Path initialImage) throws E // verify we get the warning message out.stdoutShouldMatch("Warning: .* has been modified"); out.stdoutShouldNotContain("java.lang.IllegalArgumentException"); - out.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException"); + out.stdoutShouldNotContain("IOException"); } } diff --git a/test/jdk/tools/jlink/runtimeImage/PatchedJDKModuleJlinkTest.java b/test/jdk/tools/jlink/runtimeImage/PatchedJDKModuleJlinkTest.java index f83c4c698f1..d4654ec98bd 100644 --- a/test/jdk/tools/jlink/runtimeImage/PatchedJDKModuleJlinkTest.java +++ b/test/jdk/tools/jlink/runtimeImage/PatchedJDKModuleJlinkTest.java @@ -97,10 +97,10 @@ public boolean test(OutputAnalyzer t) { if (analyzer.getExitValue() == 0) { throw new AssertionError("Expected jlink to fail due to patched module!"); } - analyzer.stdoutShouldContain("MyJlinkPatchInteger.class not found in the modules image."); - analyzer.stdoutShouldContain("--patch-module is not supported"); + analyzer.stdoutShouldContain("jlink does not support linking from the run-time image"); + analyzer.stdoutShouldContain(" when running on a patched runtime with --patch-module"); // Verify the error message is reasonable - analyzer.stdoutShouldNotContain("jdk.tools.jlink.internal.RunImageLinkException"); + analyzer.stdoutShouldNotContain("IOException"); analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException"); } From 82e8aa62de5d6854978efd66190654f05299e523 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Tue, 3 Dec 2024 22:41:47 +0000 Subject: [PATCH 099/171] 8345415: Rollback JDK-8301991 change on xmlsecurity_de.properties Reviewed-by: mullan --- .../resource/xmlsecurity_de.properties | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_de.properties b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_de.properties index 1373f3bfa5f..3d4306e988f 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_de.properties +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_de.properties @@ -20,36 +20,36 @@ # algorithm.alreadyRegistered = URI {0} wurde bereits an die Klasse {1} gebunden -algorithm.classDoesNotExist = Kann URI {0} nicht für Klasse {1} registrieren weil sie nicht existiert +algorithm.classDoesNotExist = Kann URI {0} nicht f\u00fcr Klasse {1} registrieren weil sie nicht existiert algorithm.ClassDoesNotExist = Klasse {0} existiert nicht -algorithm.extendsWrongClass = Kann URI {0} nicht für Klasse {1} registrieren weil sie nicht von {2} abgeleitet ist -algorithms.CannotUseAlgorithmParameterSpecOnDSA = AlgorithmParameterSpec kann nicht für DSA Signaturen benutzt werden. -algorithms.CannotUseAlgorithmParameterSpecOnRSA = AlgorithmParameterSpec kann nicht für RSA Signaturen benutzt werden. -algorithms.CannotUseSecureRandomOnMAC = SecureRandom kann nicht für MAC's angewandt werden. +algorithm.extendsWrongClass = Kann URI {0} nicht f\u00fcr Klasse {1} registrieren weil sie nicht von {2} abgeleitet ist +algorithms.CannotUseAlgorithmParameterSpecOnDSA = AlgorithmParameterSpec kann nicht f\u00fcr DSA Signaturen benutzt werden. +algorithms.CannotUseAlgorithmParameterSpecOnRSA = AlgorithmParameterSpec kann nicht f\u00fcr RSA Signaturen benutzt werden. +algorithms.CannotUseSecureRandomOnMAC = SecureRandom kann nicht f\u00fcr MAC's angewandt werden. algorithms.HMACOutputLengthMax = HMACOutputLength darf nicht grosser als {0} sein algorithms.HMACOutputLengthMin = HMACOutputLength darf nicht kleiner als {0} sein -algorithms.HMACOutputLengthOnlyForHMAC = Die HMACOutputLength kann nur bei HMAC integritäts Algorithmen angegeben werden +algorithms.HMACOutputLengthOnlyForHMAC = Die HMACOutputLength kann nur bei HMAC integrit\u00e4ts Algorithmen angegeben werden algorithms.MissingRSAPSSParams = RSAPSSParams is a required Element for http://www.w3.org/2007/05/xmldsig-more#rsa-pss -algorithms.NoSuchAlgorithm = Der Algorithmus {0} ist nicht verfügbar. -algorithms.NoSuchAlgorithm = Der Algorithmus {0} ist nicht verfügbar. Original Nachricht war\: {1} +algorithms.NoSuchAlgorithm = Der Algorithmus {0} ist nicht verf\u00fcgbar. +algorithms.NoSuchAlgorithm = Der Algorithmus {0} ist nicht verf\u00fcgbar. Original Nachricht war\: {1} algorithms.NoSuchMap = Algorithmus URI "{0}" konnte auf keinen JCE Algorithmus gemappt werden algorithms.NoSuchProvider = Der angegebene Provider {0} existiert nicht. Original Nachricht war\: {1} -algorithms.operationOnlyVerification = Ein öffentlicher Schlüssel (public key) kann nur zur Verifizierung einer Signatur verwendet werden. -algorithms.WrongKeyForThisOperation = Der angegebene Schlüssel-Typ kann nicht für diese Operation verwendet werden. Angegeben wurde {0} aber ein {1} wird benötigt. +algorithms.operationOnlyVerification = Ein \u00f6ffentlicher Schl\u00fcssel (public key) kann nur zur Verifizierung einer Signatur verwendet werden. +algorithms.WrongKeyForThisOperation = Der angegebene Schl\u00fcssel-Typ kann nicht f\u00fcr diese Operation verwendet werden. Angegeben wurde {0} aber ein {1} wird ben\u00f6tigt. attributeValueIllegal = Das Attribut {0} hat den Wert {1} muss aber {2} sein. -c14n.Canonicalizer.Exception = Fehler während der Kanonisierung\: Original Nachricht war {0} -c14n.Canonicalizer.IllegalNode = Unzulässiger NodeType {0}, NodeName lautete {1} +c14n.Canonicalizer.Exception = Fehler w\u00e4hrend der Kanonisierung\: Original Nachricht war {0} +c14n.Canonicalizer.IllegalNode = Unzul\u00e4ssiger NodeType {0}, NodeName lautete {1} c14n.Canonicalizer.NoSuchCanonicalizer = Kein Kanonisierer mit dem URI {0} gefunden -c14n.Canonicalizer.ParserConfigurationException = ParserConfigurationException während der Kanonisierung\: Original Nachricht war {0} +c14n.Canonicalizer.ParserConfigurationException = ParserConfigurationException w\u00e4hrend der Kanonisierung\: Original Nachricht war {0} c14n.Canonicalizer.RelativeNamespace = Das Element {0} hat einen relativen Namespace: {1}="{2}" -c14n.Canonicalizer.SAXException = SAXException während der Kanonisierung\: Original Nachricht war {0} -c14n.Canonicalizer.TraversalNotSupported = Das DOM Dokument unterstützt keine Traversal {0} -c14n.Canonicalizer.UnsupportedEncoding = Nicht unterstützte Kodierung {0} -c14n.Canonicalizer.UnsupportedOperation = Der Kanonisierer unterstützt diese Operation nicht -c14n.XMLUtils.circumventBug2650forgotten = Die Baumstruktur wurde nicht vorbereitet für die Kanonisierung mit XMLUtils\#circumventBug2650(Document) +c14n.Canonicalizer.SAXException = SAXException w\u00e4hrend der Kanonisierung\: Original Nachricht war {0} +c14n.Canonicalizer.TraversalNotSupported = Das DOM Dokument unterst\u00fctzt keine Traversal {0} +c14n.Canonicalizer.UnsupportedEncoding = Nicht unterst\u00fctzte Kodierung {0} +c14n.Canonicalizer.UnsupportedOperation = Der Kanonisierer unterst\u00fctzt diese Operation nicht +c14n.XMLUtils.circumventBug2650forgotten = Die Baumstruktur wurde nicht vorbereitet f\u00fcr die Kanonisierung mit XMLUtils\#circumventBug2650(Document) certificate.noSki.lowVersion = Das Zertifikat dard kein SubjectKeyIdentifier enthalten da es nur ein X509v{0} ist certificate.noSki.notOctetString = Der SubjectKeyIdentifier des Zertifikates ist kein "OctetString" -certificate.noSki.null = Das Zertifikat enthält kein SubjectKeyIdentifier +certificate.noSki.null = Das Zertifikat enth\u00e4lt kein SubjectKeyIdentifier defaultNamespaceCannotBeSetHere = Standard Namespace kann hier nicht gesetzt werden ElementProxy.nullElement = Kann keinen ElementProxy aus einem null Argument erzeugen empty = {0} @@ -57,23 +57,23 @@ encryption.algorithmCannotBeUsedForEncryptedData = encryption.algorithmCannotBeU encryption.algorithmCannotEatInitParams = encryption.algorithmCannotEatInitParams encryption.algorithmCannotEncryptDecrypt = encryption.algorithmCannotEncryptDecrypt encryption.algorithmCannotWrapUnWrap = encryption.algorithmCannotWrapUnWrap -encryption.ExplicitKeySizeMismatch = Das xenc\:KeySize Element fordert eine Schlüssel-Länge von {0} bits aber der Algorithmus besitzt {1} bits -encryption.nonceLongerThanDecryptedPlaintext = Das angegebene "Nonce" ist länger als der verfügbare Plaintext. +encryption.ExplicitKeySizeMismatch = Das xenc\:KeySize Element fordert eine Schl\u00fcssel-L\u00e4nge von {0} bits aber der Algorithmus besitzt {1} bits +encryption.nonceLongerThanDecryptedPlaintext = Das angegebene "Nonce" ist l\u00e4nger als der verf\u00fcgbare Plaintext. encryption.RSAOAEP.dataHashWrong = Falscher Hash-Wert encryption.RSAOAEP.dataStartWrong = Falscher Start Input {0} encryption.RSAOAEP.dataTooShort = Zu wenig Input encryption.RSAPKCS15.blockTruncated = Block abgeschnitten encryption.RSAPKCS15.noDataInBlock = Im Block sind keine Daten enthalten encryption.RSAPKCS15.unknownBlockType = Unbekannter Block Typ -encryption.nokey = Es ist kein verschlüsselungs Schlüssel geladen und es konnte kein Schlüssel mit Hilfe der "key resolvers" gefunden werden. -endorsed.jdk1.4.0 = Leider scheint niemand unsere Installations-Anleitung zu lesen, deshalb müssen wir es über die Exception machen\: Du hast den "endorsing" Mechanismus vom JDK 1.4 nicht richtig angewandt. Schaue unter nach wie man das Problem löst. -errorMessages.InvalidDigestValueException = Ungültige Signatur\: Referen-Validierung fehlgeschlagen. -errorMessages.InvalidSignatureValueException = Ungültige Signatur\: Core Validierung fehlgeschlagen. +encryption.nokey = Es ist kein verschl\u00fcsselungs Schl\u00fcssel geladen und es konnte kein Schl\u00fcssel mit Hilfe der "key resolvers" gefunden werden. +endorsed.jdk1.4.0 = Leider scheint niemand unsere Installations-Anleitung zu lesen, deshalb m\u00fcssen wir es \u00fcber die Exception machen\: Du hast den "endorsing" Mechanismus vom JDK 1.4 nicht richtig angewandt. Schaue unter nach wie man das Problem l\u00f6st. +errorMessages.InvalidDigestValueException = Ung\u00fcltige Signatur\: Referen-Validierung fehlgeschlagen. +errorMessages.InvalidSignatureValueException = Ung\u00fcltige Signatur\: Core Validierung fehlgeschlagen. errorMessages.IOException = Datei oder Resource kann nicht gelesen werden. -errorMessages.MissingKeyFailureException = Verifizierung fehlgeschlagen, weil der öffentliche Schlüssel (public key) nicht verfügbar ist. Resourcen via addResource() hinzufügen und erneut versuchen. -errorMessages.MissingResourceFailureException = Verifizierung fehlgeschlagen, weil Resourcen nicht verfügbar sind. Resourcen via addResource() hinzufügen und erneut versuchen. +errorMessages.MissingKeyFailureException = Verifizierung fehlgeschlagen, weil der \u00f6ffentliche Schl\u00fcssel (public key) nicht verf\u00fcgbar ist. Resourcen via addResource() hinzuf\u00fcgen und erneut versuchen. +errorMessages.MissingResourceFailureException = Verifizierung fehlgeschlagen, weil Resourcen nicht verf\u00fcgbar sind. Resourcen via addResource() hinzuf\u00fcgen und erneut versuchen. errorMessages.NoSuchAlgorithmException = Unbekannter Algorithmus {0} -errorMessages.NotYetImplementedException = Funktionalität noch nicht implementiert. +errorMessages.NotYetImplementedException = Funktionalit\u00e4t noch nicht implementiert. errorMessages.XMLSignatureException = Verifizierung aus unbekanntem Grund fehlgeschlagen. decoding.divisible.four = It should be divisible by four decoding.general = Fehler beim Decodieren @@ -84,70 +84,70 @@ FileKeyStorageImpl.NoCert.SubjName = Kein X509-Zertifikat mit SubjectName {0} ge generic.dontHaveConstructionElement = Konstruktions-Element fehlt generic.EmptyMessage = {0} generic.NotYetImplemented = {0} Leider noch nicht implementiert ;-(( -java.security.InvalidKeyException = Ungültiger Schlüssel -java.security.NoSuchProviderException = Unbekannter oder nicht unterstützter Provider -java.security.UnknownKeyType = Unbekannter oder nicht unterstützter Schlüssel-Typ {0} +java.security.InvalidKeyException = Ung\u00fcltiger Schl\u00fcssel +java.security.NoSuchProviderException = Unbekannter oder nicht unterst\u00fctzter Provider +java.security.UnknownKeyType = Unbekannter oder nicht unterst\u00fctzter Schl\u00fcssel-Typ {0} KeyInfo.error = Error loading Key Info -KeyInfo.needKeyResolver = Es müssen mehrere KeyResolver registriert sein -KeyInfo.nokey = Kann keinen Schlüssel aus {0} gewinnen -KeyInfo.noKey = Kann keinen öffentlichen Schlüssel finden -KeyInfo.wrongNumberOfObject = Benötige {0} keyObjects +KeyInfo.needKeyResolver = Es m\u00fcssen mehrere KeyResolver registriert sein +KeyInfo.nokey = Kann keinen Schl\u00fcssel aus {0} gewinnen +KeyInfo.noKey = Kann keinen \u00f6ffentlichen Schl\u00fcssel finden +KeyInfo.wrongNumberOfObject = Ben\u00f6tige {0} keyObjects KeyInfo.wrongUse = Dieses Objekt wird verwendet, um {0} zu gewinnen -keyResolver.alreadyRegistered = Die Klasse {1} wurde bereits registriert für {0} -KeyResolver.needStorageResolver = Es wird ein StorageResolver benötigt um ein Zertifikat aus {0} zu holen +keyResolver.alreadyRegistered = Die Klasse {1} wurde bereits registriert f\u00fcr {0} +KeyResolver.needStorageResolver = Es wird ein StorageResolver ben\u00f6tigt um ein Zertifikat aus {0} zu holen KeyResoverSpiImpl.cannotGetCert = Cannot get the Certificate that include or in {1} in implement class {0} KeyResoverSpiImpl.elementGeneration = Cannot make {1} element in implement class {0} KeyResoverSpiImpl.getPoublicKey = Cannot get the public key from implement class {0} KeyResoverSpiImpl.InvalidElement = Cannot set (2) Element in implement class {0} KeyResoverSpiImpl.keyStore = KeyStorage Fehler in der implementierenden Klasse {0} -KeyResoverSpiImpl.need.Element = Es wird der Typ {1} benötigt in der implementierenden Klasse {0} +KeyResoverSpiImpl.need.Element = Es wird der Typ {1} ben\u00f6tigt in der implementierenden Klasse {0} KeyResoverSpiImpl.wrongCRLElement = Cannot make CRL from {1} in implement class {0} KeyResoverSpiImpl.wrongKeyObject = Need {1} type of KeyObject for generation Element in implement class{0} KeyResoverSpiImpl.wrongNumberOfObject = Need {1} keyObject in implement class {0} -KeyStore.alreadyRegistered = Klasse {0} bereits registriert für {1} +KeyStore.alreadyRegistered = Klasse {0} bereits registriert f\u00fcr {1} KeyStore.register = {1} type class register error in class {0} -KeyStore.registerStore.register = Registrierungsfehler für Typ {0} +KeyStore.registerStore.register = Registrierungsfehler f\u00fcr Typ {0} KeyValue.IllegalArgument = Kann kein {0} aus {1} erzeugen namespacePrefixAlreadyUsedByOtherURI = Namespace {0} wird bereits von einer anderen URI {1} gebraucht notYetInitialized = Das Modul {0} ist noch nicht initialisiert prefix.AlreadyAssigned = Sie binden den Prefix {0} an den Namespace {1} aber er ist bereits an {2} zugewiesen -signature.Canonicalizer.UnknownCanonicalizer = Unbekannter Kanonisierer. Kein Handler installiert für URI {0} -signature.DSA.invalidFormat = Ungültige ASN.1 Kodierung der DSA Signatur +signature.Canonicalizer.UnknownCanonicalizer = Unbekannter Kanonisierer. Kein Handler installiert f\u00fcr URI {0} +signature.DSA.invalidFormat = Ung\u00fcltige ASN.1 Kodierung der DSA Signatur signature.Generation.signBeforeGetValue = Es muss zuerst XMLSignature.sign(java.security.PrivateKey) aufgerufen werden signature.Reference.ForbiddenResolver = Der "Resolver" {0} ist bei aktivierter "secure validation" nicht erlaubt signature.Reference.NoDigestMethod = A Signature Reference Element must contain a DigestMethod child signature.Reference.NoDigestValue = A Signature Reference Element must contain a DigestValue child signature.signatureAlgorithm = Der Algorithmus {0} ist bei aktivierter "secure validation" nicht erlaubt signature.signaturePropertyHasNoTarget = Das Target Attribut der SignatureProperty muss gesetzt sein -signature.tooManyReferences = Das Manifest enthält {0} Referenzen, bei aktivierter "secure validation" sind aber maximal {1} erlaubt -signature.tooManyTransforms = Die Referenz enthält {0} Transformationen, bei aktivierter "secure validation" sind aber maximal {1} erlaubt -signature.Transform.ErrorDuringTransform = Während der Transformation {0} trat eine {1} auf. +signature.tooManyReferences = Das Manifest enth\u00e4lt {0} Referenzen, bei aktivierter "secure validation" sind aber maximal {1} erlaubt +signature.tooManyTransforms = Die Referenz enth\u00e4lt {0} Transformationen, bei aktivierter "secure validation" sind aber maximal {1} erlaubt +signature.Transform.ErrorDuringTransform = W\u00e4hrend der Transformation {0} trat eine {1} auf. signature.Transform.ForbiddenTransform = Die Transformation {0} ist bei aktivierter "secure validation" nicht erlaubt signature.Transform.NotYetImplemented = Transform {0} noch nicht implementiert -signature.Transform.NullPointerTransform = Null pointer als URI übergeben. Programmierfehler? -signature.Transform.UnknownTransform = Unbekannte Transformation. Kein Handler installiert für URI {0} +signature.Transform.NullPointerTransform = Null pointer als URI \u00fcbergeben. Programmierfehler? +signature.Transform.UnknownTransform = Unbekannte Transformation. Kein Handler installiert f\u00fcr URI {0} signature.Util.BignumNonPositive = bigInteger.signum() muss positiv sein signature.Util.NonTextNode = Keine Text Node signature.Util.TooManyChilds = Zu viele Kind-Elemente vom Typ {0} in {1} signature.Verification.certificateError = Zertifikatsfehler signature.Verification.IndexOutOfBounds = Index {0} illegal. Es sind nur {1} Referenzen vorhanden signature.Verification.internalError = Interner Fehler -signature.Verification.InvalidDigestOrReference = Ungültiger Digest Wert der Referenz {0} +signature.Verification.InvalidDigestOrReference = Ung\u00fcltiger Digest Wert der Referenz {0} signature.Verification.InvalidElement = Current Node {0} is not permitted in this location in the Signature -signature.Verification.keyStore = Öffnen des KeyStore fehlgeschlagen +signature.Verification.keyStore = \u00d6ffnen des KeyStore fehlgeschlagen signature.Verification.MissingID = Element mit der ID {0} nicht gefunden -signature.Verification.MissingResources = Kann die externe Resource {0} nicht auflösen +signature.Verification.MissingResources = Kann die externe Resource {0} nicht aufl\u00f6sen signature.Verification.MultipleIDs = Mehrere Elemente mit der ID {0} gefunden -signature.Verification.NoSignatureElement = Input Dokument enthält kein {0} Element mit dem Namespace {1} -signature.Verification.Reference.NoInput = Die Referenz für den URI {0} hat keinen XMLSignatureInput erhalten. +signature.Verification.NoSignatureElement = Input Dokument enth\u00e4lt kein {0} Element mit dem Namespace {1} +signature.Verification.Reference.NoInput = Die Referenz f\u00fcr den URI {0} hat keinen XMLSignatureInput erhalten. signature.Verification.SignatureError = Signatur Fehler signature.XMLSignatureInput.MissingConstuctor = Kann aus der Klasse {0} keinen XMLSignatureInput erzeugen signature.XMLSignatureInput.SerializeDOM = Input mit einem DOM Dokument initialisiert. Muss mit C14N serialisiert werden -transform.Init.IllegalContextArgument = Unzulässiges Kontext Argument der Klasse {0}. Muss String, org.w3c.dom.NodeList oder java.io.InputStream sein. +transform.Init.IllegalContextArgument = Unzul\u00e4ssiges Kontext Argument der Klasse {0}. Muss String, org.w3c.dom.NodeList oder java.io.InputStream sein. transform.init.NotInitialized = transform.init.wrongURI = Initialisiert mit dem falschen URI. Das sollte nie passieren. Die Transformation implementiert {0} aber {1} wurde bei der Instantiierung verwendet. -utils.Base64.IllegalBitlength = Ungültige Byte-Länge; Muss ein vielfaches von 4 sein -utils.resolver.noClass = Keinen Resolver für URI {0} und Base {1} gefunden +utils.Base64.IllegalBitlength = Ung\u00fcltige Byte-L\u00e4nge; Muss ein vielfaches von 4 sein +utils.resolver.noClass = Keinen Resolver f\u00fcr URI {0} und Base {1} gefunden xml.WrongContent = Kann {0} nicht finden in {1} xml.WrongElement = Kann kein {0} aus einem {1} Element erzeugen xpath.funcHere.documentsDiffer = Der XPath ist nicht im selben Dokument wie der Kontext Node @@ -157,43 +157,43 @@ signature.Transform.nodeAndType = Aktuelle Node\: {0}, Typ\: {1} signature.XMLSignatureInput.nodesetReference = Das Node-Set der Referenz konnte nicht konvertieren werden transform.envelopedSignatureTransformNotInSignatureElement = Enveloped Transform konnte kein Signatur Element finden Base64Decoding = Fehler bei der Decodierung -secureProcessing.MaximumAllowedTransformsPerReference = Die Referenz enthält {0} Transformationen. Es sind aber maximal {1} erlaubt. Die Limite kann über das Konfigurations-Property "MaximumAllowedTransformsPerReference" erhöht werden. -secureProcessing.MaximumAllowedReferencesPerManifest = Das Manifest enhält {0} Referenzen. Es sind aber maximal {1} erlaubt. Die Limite kann über das Konfigurations-Property "MaximumAllowedReferencesPerManifest" erhöht werden. -secureProcessing.DoNotThrowExceptionForManifests = Signatur-Manifests werden nicht unterstützt. Das werfen dieser Exception kann durch das Konfigurations-Property "DoNotThrowExceptionForManifests" verhindert werden. -secureProcessing.AllowMD5Algorithm = Vom Einsatz des MD5 Algorithmus wird strengstens abgeraten. Trotzdem kann er über das Konfigurations-Property "AllowMD5Algorithm" erlaubt werden. -secureProcessing.AllowNotSameDocumentReferences = Externe Referenzen gefunden. Die Verarbeitung von externen Referenzen ist standardmässig ausgeschaltet. Es kann über das Konfigurations-Property "AllowNotSameDocumentReferences" aktiviert werden. -secureProcessing.MaximumAllowedXMLStructureDepth = Die Maximum erlaubte Dokumenten-Tiefe von ({0}) wurde erreicht. Die Limite kann über das Konfigurations-Property "MaximumAllowedXMLStructureDepth" erhöht werden. +secureProcessing.MaximumAllowedTransformsPerReference = Die Referenz enth\u00e4lt {0} Transformationen. Es sind aber maximal {1} erlaubt. Die Limite kann \u00fcber das Konfigurations-Property "MaximumAllowedTransformsPerReference" erh\u00f6ht werden. +secureProcessing.MaximumAllowedReferencesPerManifest = Das Manifest enh\u00e4lt {0} Referenzen. Es sind aber maximal {1} erlaubt. Die Limite kann \u00fcber das Konfigurations-Property "MaximumAllowedReferencesPerManifest" erh\u00f6ht werden. +secureProcessing.DoNotThrowExceptionForManifests = Signatur-Manifests werden nicht unterst\u00fctzt. Das werfen dieser Exception kann durch das Konfigurations-Property "DoNotThrowExceptionForManifests" verhindert werden. +secureProcessing.AllowMD5Algorithm = Vom Einsatz des MD5 Algorithmus wird strengstens abgeraten. Trotzdem kann er \u00fcber das Konfigurations-Property "AllowMD5Algorithm" erlaubt werden. +secureProcessing.AllowNotSameDocumentReferences = Externe Referenzen gefunden. Die Verarbeitung von externen Referenzen ist standardm\u00e4ssig ausgeschaltet. Es kann \u00fcber das Konfigurations-Property "AllowNotSameDocumentReferences" aktiviert werden. +secureProcessing.MaximumAllowedXMLStructureDepth = Die Maximum erlaubte Dokumenten-Tiefe von ({0}) wurde erreicht. Die Limite kann \u00fcber das Konfigurations-Property "MaximumAllowedXMLStructureDepth" erh\u00f6ht werden. secureProcessing.inputStreamLimitReached = Maximal erlaubte Anzahl bytes ({0}) erreicht. stax.duplicateActions=Doppelte Actions sind nicht erlaubt. stax.missingSecurityProperties = SecurityProperties darf nicht null sein\! stax.noOutputAction = Keine ausgehenden "Actions" definiert. -stax.noKey = Kein Schlüssel geladen und es konnte kein Schlüssel gefunden werden für {0} -stax.keyNotFound = Schlüssel nicht gefunden. -stax.unsupportedKeyValue = Kein oder ungültiger KeyValue. -stax.emptyReferenceURI = Referenz enthält kein URI Attribut. -stax.encryption.unprocessedReferences = Es wurden nicht alle Verschlüsselungs-Referenzen verarbeitet... +stax.noKey = Kein Schl\u00fcssel geladen und es konnte kein Schl\u00fcssel gefunden werden f\u00fcr {0} +stax.keyNotFound = Schl\u00fcssel nicht gefunden. +stax.unsupportedKeyValue = Kein oder ung\u00fcltiger KeyValue. +stax.emptyReferenceURI = Referenz enth\u00e4lt kein URI Attribut. +stax.encryption.unprocessedReferences = Es wurden nicht alle Verschl\u00fcsselungs-Referenzen verarbeitet... stax.signature.unprocessedReferences = Es wurden nicht alle Signatur-Referenzen verarbeitet... -stax.unsupportedToken = {0} nicht unterstützt. +stax.unsupportedToken = {0} nicht unterst\u00fctzt. stax.xmlStructureSizeExceeded = Maximal erlaubte ({0}) XML-Struktur Tiefe erreicht. stax.unexpectedXMLEvent = Unerwarteter StAX-Event\: {0} -stax.encryption.noEncAlgo = xenc\:EncryptedKey enthält kein xenc\:EncryptionMethod/@Algorithm. -stax.encryption.noCipherValue = EncryptedKey enthält kein xenc\:CipherData/xenc\:CipherValue. +stax.encryption.noEncAlgo = xenc\:EncryptedKey enth\u00e4lt kein xenc\:EncryptionMethod/@Algorithm. +stax.encryption.noCipherValue = EncryptedKey enth\u00e4lt kein xenc\:CipherData/xenc\:CipherValue. stax.unsecuredMessage = Ungesicherte Nachricht. Weder ein Signatur- noch ein EncryptedData- Element wurde gefunden. stax.signature.signedInfoMissing = SignedInfo Element fehlt. stax.signature.signatureMethodMissing = Signature method fehlt. stax.signature.canonicalizationMethodMissing = Signature canonicalization method fehlt. stax.signature.signatureValueMissing = Signature value fehlt. stax.signature.publicKeyOrCertificateMissing = Weder ein Zertifikat noch ein public-key wurde konfiguriert. -stax.encryption.encryptionKeyMissing = Kein Schlüssel für die Verschlüsselung wurde konfiguriert. -stax.unsupportedKeyTransp = Der public-key Algorithmus ist zu kurz um den symmetrischen Schlüssel zu verschlüsseln. -stax.recursiveKeyReference = Rekursive Schlüssel referenzierung detektiert. -stax.ecParametersNotSupported = ECParameters werden nicht unterstützt. +stax.encryption.encryptionKeyMissing = Kein Schl\u00fcssel f\u00fcr die Verschl\u00fcsselung wurde konfiguriert. +stax.unsupportedKeyTransp = Der public-key Algorithmus ist zu kurz um den symmetrischen Schl\u00fcssel zu verschl\u00fcsseln. +stax.recursiveKeyReference = Rekursive Schl\u00fcssel referenzierung detektiert. +stax.ecParametersNotSupported = ECParameters werden nicht unterst\u00fctzt. stax.namedCurveMissing = NamedCurve fehlt. -stax.encryption.securePartNotFound = Part zum Verschlüsseln nicht gefunden: {0} +stax.encryption.securePartNotFound = Part zum Verschl\u00fcsseln nicht gefunden: {0} stax.signature.securePartNotFound = Part zum Signieren nicht gefunden: {0} stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterstützt. stax.signature.keyNameMissing = KeyName nicht konfiguriert. -stax.keyNotFoundForName = Kein Schlüssel für Schlüsselname konfiguriert: {0} -stax.keyTypeNotSupported = Key vom Typ {0} nicht für einen Key-Namenssuche unterstützt +stax.keyNotFoundForName = Kein Schl\u00fcssel für Schl\u00fcsselname konfiguriert: {0} +stax.keyTypeNotSupported = Key vom Typ {0} nicht f\u00fcr einen Key-Namenssuche unterst\u00fctzt stax.idsetbutnotgenerated = An Id attribute is specified, but Id generation is disabled stax.idgenerationdisablewithmultipleparts = Id generation must not be disabled when multiple parts need signing From 42378970e1accc67c2a152c6af2b0becacf5b4b8 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Wed, 4 Dec 2024 00:15:30 +0000 Subject: [PATCH 100/171] 8345341: Fix incorrect log message in JDI stop002t test Reviewed-by: amenkov, lmesnik --- .../vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java index 694485e608e..e82b749c905 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ThreadReference/stop/stop002t.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -180,7 +180,7 @@ private int runIt(String args[]) { log.display("TEST #5: interrupted = " + Thread.interrupted()); // We don't expect the exception to be thrown when in vthread mode. if (!vthreadMode && t instanceof MyThrowable) { - log.display("TEST #5: Caught expected exception while in loop: " + t); + log.display("TEST #5: Caught expected exception while in sleep: " + t); } else { log.complain("TEST #5: Unexpected exception caught: " + t); t.printStackTrace(); From c143138a35689605ebe44d847904e226ffcaeb74 Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Wed, 4 Dec 2024 01:45:16 +0000 Subject: [PATCH 101/171] 8345351: RISC-V: Rename macro-assembler routine cmpxchg_weak to weak_cmpxchg Reviewed-by: rehn, mli --- .../riscv/gc/z/zBarrierSetAssembler_riscv.cpp | 2 +- .../cpu/riscv/macroAssembler_riscv.cpp | 2 +- .../cpu/riscv/macroAssembler_riscv.hpp | 2 +- src/hotspot/cpu/riscv/riscv.ad | 40 +++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index cd83eafcaeb..dde2f1f131f 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -286,7 +286,7 @@ void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm, __ relocate(barrier_Relocation::spec(), [&] { __ li16u(rtmp1, barrier_Relocation::unpatched); }, ZBarrierRelocationFormatStoreGoodBits); - __ cmpxchg_weak(rtmp2, zr, rtmp1, + __ weak_cmpxchg(rtmp2, zr, rtmp1, Assembler::int64, Assembler::relaxed /* acquire */, Assembler::relaxed /* release */, rtmp3); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 3ecfd5de725..44b806834f9 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -3576,7 +3576,7 @@ void MacroAssembler::cmpxchg(Register addr, Register expected, bind(done); } -void MacroAssembler::cmpxchg_weak(Register addr, Register expected, +void MacroAssembler::weak_cmpxchg(Register addr, Register expected, Register new_val, enum operand_size size, Assembler::Aqrl acquire, Assembler::Aqrl release, diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 26a0ed3b0d1..0d28eaaf1f0 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1146,7 +1146,7 @@ class MacroAssembler: public Assembler { enum operand_size size, Assembler::Aqrl acquire, Assembler::Aqrl release, Register result, bool result_as_bool = false); - void cmpxchg_weak(Register addr, Register expected, + void weak_cmpxchg(Register addr, Register expected, Register new_val, enum operand_size size, Assembler::Aqrl acquire, Assembler::Aqrl release, diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index d0085125c76..32019251bbe 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -5744,7 +5744,7 @@ instruct weakCompareAndSwapB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iReg effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); format %{ - "cmpxchg_weak $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapB" %} @@ -5767,7 +5767,7 @@ instruct weakCompareAndSwapS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iReg effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); format %{ - "cmpxchg_weak $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapS" %} @@ -5787,12 +5787,12 @@ instruct weakCompareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); format %{ - "cmpxchg_weak $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapI" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); %} @@ -5806,12 +5806,12 @@ instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL ne ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); format %{ - "cmpxchg_weak $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapL" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); %} @@ -5826,12 +5826,12 @@ instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN ne ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4); format %{ - "cmpxchg_weak $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapN" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); %} @@ -5846,12 +5846,12 @@ instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); format %{ - "cmpxchg_weak $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapP" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); %} @@ -5870,7 +5870,7 @@ instruct weakCompareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, i effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); format %{ - "cmpxchg_weak_acq $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg_acq $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapBAcq" %} @@ -5895,7 +5895,7 @@ instruct weakCompareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, i effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); format %{ - "cmpxchg_weak_acq $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg_acq $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapSAcq" %} @@ -5917,12 +5917,12 @@ instruct weakCompareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); format %{ - "cmpxchg_weak_acq $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg_acq $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapIAcq" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); %} @@ -5938,12 +5938,12 @@ instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); format %{ - "cmpxchg_weak_acq $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg_acq $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapLAcq" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); %} @@ -5959,12 +5959,12 @@ instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4); format %{ - "cmpxchg_weak_acq $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg_acq $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t" "# $res == 1 when success, #@weakCompareAndSwapNAcq" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); %} @@ -5980,12 +5980,12 @@ instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); format %{ - "cmpxchg_weak_acq $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t" + "weak_cmpxchg_acq $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t" "\t# $res == 1 when success, #@weakCompareAndSwapPAcq" %} ins_encode %{ - __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, + __ weak_cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); %} From 7ec36bb7837932959beb6ce5eb669a0553f978b6 Mon Sep 17 00:00:00 2001 From: Joe Wang Date: Wed, 4 Dec 2024 02:28:06 +0000 Subject: [PATCH 102/171] 8343001: Adjust XSLT and XPath Extension Function Property Reviewed-by: rriggs, lancea --- .../internal/res/XSLTErrorResources.java | 9 +- .../internal/xsltc/compiler/FunctionCall.java | 83 +++++---- .../xalan/internal/xsltc/compiler/XSLTC.java | 8 +- .../xsltc/compiler/util/ErrorMessages.java | 11 +- .../xsltc/compiler/util/ErrorMsg.java | 3 +- .../internal/xsltc/runtime/ErrorMessages.java | 14 +- .../xsltc/trax/TransformerFactoryImpl.java | 5 +- .../internal/xsltc/trax/TransformerImpl.java | 7 +- .../jdk/xml/internal/JdkXmlFeatures.java | 15 +- src/java.xml/share/conf/jaxp.properties | 7 +- .../libs/jaxp/library/JAXPTestUtilities.java | 32 ++++ .../common/config/ImplProperties.java | 2 +- .../jaxp/unittest/transform/Bug6513892.java | 10 +- .../unittest/transform/ErrorListenerTest.java | 6 +- .../transform/SecureProcessingTest.java | 158 ++++++++---------- .../unittest/transform/XSLTFunctionsTest.java | 111 ++++++------ .../javax/xml/jaxp/common/8032908/XSLT.java | 6 +- .../javax/xml/jaxp/parsers/8024707/XSLT.java | 6 +- .../transform/8004476/XSLTExFuncTest.java | 8 +- 19 files changed, 268 insertions(+), 233 deletions(-) diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources.java index e5b8aeabfd2..a1f1a8a0705 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/res/XSLTErrorResources.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -31,7 +31,7 @@ * Array. You also need to update MAX_CODE for error strings * and MAX_WARNING for warnings ( Needed for only information * purpose ) - * @LastModified: May 2022 + * @LastModified: Dec 2024 */ public class XSLTErrorResources extends ListResourceBundle { @@ -1197,7 +1197,10 @@ public Object[][] getContents() "Cannot set the feature ''{0}'' on this TransformerFactory."}, { ER_EXTENSION_ELEMENT_NOT_ALLOWED_IN_SECURE_PROCESSING, - "Use of the extension element ''{0}'' is not allowed when the secure processing feature is set to true."}, + "Use of the extension function ''{0}'' is not allowed when extension " + + "functions are disabled by the secure processing feature or " + + "the property ''jdk.xml.enableExtensionFunctions''. " + + "To enable extension functions, set ''jdk.xml.enableExtensionFunctions'' to ''true''."}, { ER_NAMESPACE_CONTEXT_NULL_NAMESPACE, "Cannot get the prefix for a null namespace uri."}, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java index cd7c9e49834..2cf491d96f5 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -62,7 +62,7 @@ * @author Morten Jorgensen * @author Erwin Bolwidt * @author Todd Miller - * @LastModified: Nov 2017 + * @LastModified: Dec 2024 */ class FunctionCall extends Expression { @@ -958,12 +958,12 @@ public boolean isExtension() { * after stripping its namespace or null * if no such methods exist. */ - private List findMethods() { + private List findMethods() throws TypeCheckError { - List result = null; - final String namespace = _fname.getNamespace(); + List result = null; + final String namespace = _fname.getNamespace(); - if (_className != null && _className.length() > 0) { + if (_className != null && _className.length() > 0) { final int nArgs = _arguments.size(); try { if (_clazz == null) { @@ -971,48 +971,45 @@ private List findMethods() { final boolean isExtensionFunctionEnabled = getXSLTC() .getFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION); - //Check if FSP and SM - only then process with loading - if (namespace != null && isSecureProcessing - && isExtensionFunctionEnabled - && (namespace.startsWith(JAVA_EXT_XALAN) - || namespace.startsWith(JAVA_EXT_XSLTC) - || namespace.startsWith(JAVA_EXT_XALAN_OLD) - || namespace.startsWith(XALAN_CLASSPACKAGE_NAMESPACE))) { - _clazz = getXSLTC().loadExternalFunction(_className); + // the property has the precedence + if (isExtensionFunctionEnabled) { + if (getXSLTC().hasExtensionClassLoader()) { + _clazz = getXSLTC().loadExternalFunction(_className); + } else { + _clazz = ObjectFactory.findProviderClass(_className, true); + } + if (_clazz == null) { + final ErrorMsg msg + = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); + getParser().reportError(Constants.ERROR, msg); + return null; + } } else { - _clazz = ObjectFactory.findProviderClass(_className, true); + throw new TypeCheckError(ErrorMsg.UNSUPPORTED_EXT_FUNC_ERR, _className); } - - if (_clazz == null) { - final ErrorMsg msg = - new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); - getParser().reportError(Constants.ERROR, msg); } - } - - final String methodName = _fname.getLocalPart(); - final Method[] methods = _clazz.getMethods(); - - for (int i = 0; i < methods.length; i++) { - final int mods = methods[i].getModifiers(); - // Is it public and same number of args ? - if (Modifier.isPublic(mods) - && methods[i].getName().equals(methodName) - && methods[i].getParameterTypes().length == nArgs) - { - if (result == null) { - result = new ArrayList<>(); - } - result.add(methods[i]); + + final String methodName = _fname.getLocalPart(); + final Method[] methods = _clazz.getMethods(); + + for (int i = 0; i < methods.length; i++) { + final int mods = methods[i].getModifiers(); + // Is it public and same number of args ? + if (Modifier.isPublic(mods) + && methods[i].getName().equals(methodName) + && methods[i].getParameterTypes().length == nArgs) { + if (result == null) { + result = new ArrayList<>(); + } + result.add(methods[i]); + } } - } - } - catch (ClassNotFoundException e) { - final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); - getParser().reportError(Constants.ERROR, msg); + } catch (ClassNotFoundException e) { + final ErrorMsg msg = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, _className); + getParser().reportError(Constants.ERROR, msg); } - } - return result; + } + return result; } /** diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java index 9a256efc5a1..52edad7c22a 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -57,7 +57,7 @@ * @author G. Todd Miller * @author Morten Jorgensen * @author John Howard (johnh@schemasoft.com) - * @LastModified: Jan 2022 + * @LastModified: Dec 2024 */ public final class XSLTC { @@ -291,6 +291,10 @@ private void setExternalExtensionFunctions(String name, Class clazz) { } } + boolean hasExtensionClassLoader() { + return _extensionClassLoader != null; + } + /* * Function loads an external extension function. * The filtering of function types (external,internal) takes place in FunctionCall class diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java index c34f4fa59eb..660d1194b30 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java @@ -24,7 +24,7 @@ /** * @author Morten Jorgensen - * @LastModified: Nov 2024 + * @LastModified: Dec 2024 */ public class ErrorMessages extends ListResourceBundle { @@ -552,6 +552,15 @@ public Object[][] getContents() {ErrorMsg.DATA_CONVERSION_ERR, "Cannot convert data-type ''{0}'' to ''{1}''."}, + /* + * Note to translators: property name "jdk.xml.enableExtensionFunctions" + * and value "true" should not be translated. + */ + {ErrorMsg.UNSUPPORTED_EXT_FUNC_ERR, + "Use of the extension function ''{0}'' is not allowed when extension " + + "functions are disabled by the secure processing feature or " + + "the property ''jdk.xml.enableExtensionFunctions''. " + + "To enable extension functions, set ''jdk.xml.enableExtensionFunctions'' to ''true''."}, /* * Note to translators: "Templates" is a Java class name that should * not be translated. diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java index c586ad2674c..8ef4c9c86b6 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java @@ -33,7 +33,7 @@ * @author G. Todd Miller * @author Erwin Bolwidt * @author Morten Jorgensen - * @LastModified: Nov 2024 + * @LastModified: Dec 2024 */ public final class ErrorMsg { @@ -105,6 +105,7 @@ public final class ErrorMsg { public static final String ATTR_VAL_TEMPLATE_ERR = "ATTR_VAL_TEMPLATE_ERR"; public static final String UNKNOWN_SIG_TYPE_ERR = "UNKNOWN_SIG_TYPE_ERR"; public static final String DATA_CONVERSION_ERR = "DATA_CONVERSION_ERR"; + public static final String UNSUPPORTED_EXT_FUNC_ERR = "UNSUPPORTED_EXT_FUNC_ERR"; // JAXP/TrAX error messages public static final String NO_TRANSLET_CLASS_ERR = "NO_TRANSLET_CLASS_ERR"; diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/ErrorMessages.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/ErrorMessages.java index 54ecd80e55e..453c008c0a9 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/ErrorMessages.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/ErrorMessages.java @@ -1,6 +1,5 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,6 +24,7 @@ /** * @author Morten Jorgensen + * @LastModified: Dec 2024 */ public class ErrorMessages extends ListResourceBundle { @@ -275,10 +275,16 @@ public Object[][] getContents() "An attribute whose value must be an NCName had the value ''{0}''"}, {BasisLibrary.UNALLOWED_EXTENSION_FUNCTION_ERR, - "Use of the extension function ''{0}'' is not allowed when the secure processing feature is set to true."}, + "Use of the extension function ''{0}'' is not allowed when extension " + + "functions are disabled by the secure processing feature or " + + "the property ''jdk.xml.enableExtensionFunctions''. " + + "To enable extension functions, set ''jdk.xml.enableExtensionFunctions'' to ''true''."}, {BasisLibrary.UNALLOWED_EXTENSION_ELEMENT_ERR, - "Use of the extension element ''{0}'' is not allowed when the secure processing feature is set to true."}, + "Use of the extension element ''{0}'' is not allowed when extension " + + "functions are disabled by the secure processing feature or " + + "the property ''jdk.xml.enableExtensionFunctions''. " + + "To enable extension functions, set ''jdk.xml.enableExtensionFunctions'' to ''true''."}, }; } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java index 440df5593ea..d9af6f47440 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java @@ -75,7 +75,6 @@ import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.JdkProperty.ImplPropMap; import jdk.xml.internal.JdkProperty.State; -import jdk.xml.internal.SecuritySupport; import jdk.xml.internal.TransformErrorListener; import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.InputSource; @@ -88,7 +87,7 @@ * @author G. Todd Miller * @author Morten Jorgensen * @author Santiago Pericas-Geertsen - * @LastModified: Nov 2024 + * @LastModified: Dec 2024 */ public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader @@ -216,7 +215,7 @@ public PIParamWrapper(String media, String title, String charset) { /** *

      State of secure processing feature.

      */ - private boolean _isNotSecureProcessing = true; + private boolean _isNotSecureProcessing = false; /** *

      State of secure mode.

      */ diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java index f551024408b..32eaec026bf 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -100,7 +100,7 @@ * @author Morten Jorgensen * @author G. Todd Miller * @author Santiago Pericas-Geertsen - * @LastModified: July 2023 + * @LastModified: Dec 2024 */ public final class TransformerImpl extends Transformer implements DOMCache @@ -206,7 +206,7 @@ public final class TransformerImpl extends Transformer /** * State of the secure processing feature. */ - private boolean _isSecureProcessing = false; + private boolean _isSecureProcessing = true; /** * Indicates whether 3rd party parser may be used to override the system-default @@ -292,6 +292,7 @@ protected TransformerImpl(Translet translet, Properties outputProperties, _propertiesClone = (Properties) _properties.clone(); _indentNumber = indentNumber; _tfactory = tfactory; + _isSecureProcessing = _tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING); _overrideDefaultParser = _tfactory.overrideDefaultParser(); _accessExternalDTD = (String)_tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_DTD); _securityManager = (XMLSecurityManager)_tfactory.getAttribute(JdkConstants.SECURITY_MANAGER); diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlFeatures.java b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlFeatures.java index b3b42588911..e8e5c2c83de 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlFeatures.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlFeatures.java @@ -52,7 +52,7 @@ public static enum XmlFeature { * function is disabled. */ ENABLE_EXTENSION_FUNCTION(ImplPropMap.ENABLEEXTFUNC, null, null, true, - null, null, true, false, true, true), + null, null, false, false, true, true), /** * The {@link javax.xml.XMLConstants.USE_CATALOG} feature. * FSP: USE_CATALOG is not enforced by FSP. @@ -382,13 +382,7 @@ public int getIndex(String propertyName) { */ private void readSystemProperties() { for (XmlFeature feature : XmlFeature.values()) { - if (!getSystemProperty(feature, feature.systemProperty())) { - //if system property is not found, try the older form if any - String oldName = feature.systemPropertyOld(); - if (oldName != null) { - getSystemProperty(feature, oldName); - } - } + getSystemProperty(feature, feature.systemProperty()); } } @@ -402,6 +396,11 @@ private void readSystemProperties() { private boolean getSystemProperty(XmlFeature feature, String sysPropertyName) { try { String value = System.getProperty(sysPropertyName); + if (value == null && feature.systemPropertyOld() != null) { + // legacy system property + value = System.getProperty(feature.systemPropertyOld()); + } + if (value != null && !value.isEmpty()) { setFeature(feature, State.SYSTEMPROPERTY, Boolean.parseBoolean(value)); return true; diff --git a/src/java.xml/share/conf/jaxp.properties b/src/java.xml/share/conf/jaxp.properties index b011586c13c..2b7136319ce 100644 --- a/src/java.xml/share/conf/jaxp.properties +++ b/src/java.xml/share/conf/jaxp.properties @@ -57,11 +57,10 @@ # Extension Functions: # # This property determines whether XSLT and XPath extension functions are allowed. -# The value type is boolean and the default value is true (allowing -# extension functions). The following entry overrides the default value and -# disallows extension functions: +# The value type is boolean and the default value is false (disallowing +# extension functions). # -# jdk.xml.enableExtensionFunctions=false +jdk.xml.enableExtensionFunctions=false # # # Overriding the default parser: diff --git a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPTestUtilities.java b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPTestUtilities.java index 8f72543db8e..bc12f14dd09 100644 --- a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPTestUtilities.java +++ b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPTestUtilities.java @@ -51,6 +51,7 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import org.testng.Assert; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -60,6 +61,9 @@ * This is an interface provide basic support for JAXP functional test. */ public class JAXPTestUtilities { + public static final String CLS_DIR = System.getProperty("test.classes"); + public static final String SRC_DIR = System.getProperty("test.src"); + public static final boolean isWindows = System.getProperty("os.name").contains("Windows"); /** * Prefix for error message. */ @@ -373,6 +377,34 @@ public interface RunnableWithException { void run() throws Exception; } + /** + * Asserts the run does not cause a Throwable. May be replaced with JUnit 5. + * @param runnable the runnable + * @param message the message if the test fails + */ + public static void assertDoesNotThrow(Assert.ThrowingRunnable runnable, String message) { + try { + runnable.run(); + } catch (Throwable t) { + Assert.fail(message + "\n Exception thrown: " + t.getMessage()); + } + } + + /** + * Returns the System identifier (URI) of the source. + * @param path the path to the source + * @return the System identifier + */ + public static String getSystemId(String path) { + if (path == null) return null; + String xmlSysId = "file://" + path; + if (isWindows) { + path = path.replace('\\', '/'); + xmlSysId = "file:///" + path; + } + return xmlSysId; + } + /** * Current test directory. */ diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/config/ImplProperties.java b/test/jaxp/javax/xml/jaxp/unittest/common/config/ImplProperties.java index e72252e0f67..4f38f9152b4 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/config/ImplProperties.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/config/ImplProperties.java @@ -102,7 +102,7 @@ public static enum PropertyType { "0", "1000000", "3000000", "10000", "5000", "0", "1000", "10", "100", "10000"}, // default values in JDK 24 - {"true", "false", "continue", "allow", "2500", "100000", + {"false", "false", "continue", "allow", "2500", "100000", "100000", "15000", "100000", "200", "5000", "100", "1000", "10", "100", "10000"}, // default values in jaxp-strict.properties.template, since JDK 23 diff --git a/test/jaxp/javax/xml/jaxp/unittest/transform/Bug6513892.java b/test/jaxp/javax/xml/jaxp/unittest/transform/Bug6513892.java index 349e137d664..0d39d427e95 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/transform/Bug6513892.java +++ b/test/jaxp/javax/xml/jaxp/unittest/transform/Bug6513892.java @@ -33,28 +33,22 @@ import javax.xml.transform.stream.StreamSource; import org.testng.Assert; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.w3c.dom.Document; /* * @test - * @bug 6513892 + * @bug 6513892 8343001 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm transform.Bug6513892 * @summary Test the output encoding of the transform is the same as that of the redirect extension. */ public class Bug6513892 { - @BeforeClass - public void setup(){ - if (System.getSecurityManager() != null) - System.setSecurityManager(null); - } - @Test public void test0() { try { TransformerFactory tf = TransformerFactory.newInstance(); + tf.setFeature("jdk.xml.enableExtensionFunctions", true); Transformer t = tf.newTransformer(new StreamSource(getClass().getResourceAsStream("redirect.xsl"), getClass().getResource("redirect.xsl") .toString())); diff --git a/test/jaxp/javax/xml/jaxp/unittest/transform/ErrorListenerTest.java b/test/jaxp/javax/xml/jaxp/unittest/transform/ErrorListenerTest.java index dbe1dcfe00f..139cf2911e6 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/transform/ErrorListenerTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/transform/ErrorListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -50,9 +50,9 @@ /* * @test - * @bug 8157830 8228854 + * @bug 8157830 8228854 8343001 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest - * @run testng/othervm transform.ErrorListenerTest + * @run testng/othervm -Djdk.xml.enableExtensionFunctions=true transform.ErrorListenerTest * @summary Verifies that ErrorListeners are handled properly */ public class ErrorListenerTest { diff --git a/test/jaxp/javax/xml/jaxp/unittest/transform/SecureProcessingTest.java b/test/jaxp/javax/xml/jaxp/unittest/transform/SecureProcessingTest.java index 1d5fa236279..673c3c73326 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/transform/SecureProcessingTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/transform/SecureProcessingTest.java @@ -23,114 +23,98 @@ package transform; -import java.io.InputStream; import java.io.StringWriter; - import javax.xml.XMLConstants; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; - +import static jaxp.library.JAXPTestUtilities.SRC_DIR; +import static jaxp.library.JAXPTestUtilities.assertDoesNotThrow; +import static jaxp.library.JAXPTestUtilities.getSystemId; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import static transform.XSLTFunctionsTest.SP_ENABLE_EXTENSION_FUNCTION_SPEC; /* * @test + * @bug 8343001 8343001 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm transform.SecureProcessingTest - * @summary Test XSLT shall report TransformerException for unsafe xsl when FEATURE_SECURE_PROCESSING is true. + * @summary Verifies that XSLT reports TransformerException as it processes xsl + * using extension functions while FEATURE_SECURE_PROCESSING is set to true. */ public class SecureProcessingTest { - @Test - public void testSecureProcessing() { - boolean _isSecureMode = System.getSecurityManager() != null; - // SECURE_PROCESSING == false + /** + * Test state + */ + public static enum TestState { + DEFAULT, // the default state + SETFSP, // set FEATURE_SECURE_PROCESSING + SETPROPERTY; // set the enalbeExtensionFunctions property + } - // the style sheet - InputStream xslStream = this.getClass().getResourceAsStream("SecureProcessingTest.xsl"); - StreamSource xslSource = new StreamSource(xslStream); + @DataProvider(name = "extFunc") + public Object[][] getExtFuncSettings() throws Exception { + return new Object[][] { + // by default, Extension Functions are disallowed + { TestState.DEFAULT, true, null, false, TransformerException.class}, + // set FSP=true, Extension Functions are disallowed + { TestState.SETFSP, true, null, false, TransformerException.class}, + // turning off FSP does not enable Extension Functions + { TestState.SETFSP, false, null, false, TransformerException.class}, + // between FSP and the Extension Functions property (jdk.xml.enableExtensionFunctions), + // the later takes precedence + { TestState.SETPROPERTY, true, SP_ENABLE_EXTENSION_FUNCTION_SPEC, false, TransformerException.class}, + { TestState.SETPROPERTY, true, SP_ENABLE_EXTENSION_FUNCTION_SPEC, true, null}, + }; + } + /** + * Verifies the effect of FEATURE_SECURE_PROCESSING (FSP) and the precedence + * between FSP and the Extension Functions property. + * + * @param testState the state of the test + * @param fspValue the FSP value to be set + * @param property the Extension Functions property + * @param propertyValue the property value + * @param expectedThrow the expected throw if the specified DTD can not be + * resolved. + * @throws Exception if the test fails + */ + @Test(dataProvider = "extFunc") + public void testFSP(TestState testState, boolean fspValue, String property, + boolean propertyValue, Class expectedThrow) + throws Exception { + final TransformerFactory tf = TransformerFactory.newInstance(); + switch (testState) { + case DEFAULT: + break; + case SETFSP: + tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, fspValue); + break; + case SETPROPERTY: + tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, fspValue); + tf.setFeature(property, propertyValue); + break; + } + if (expectedThrow == null) { + assertDoesNotThrow(() -> runTransform(tf), "Unexpected exception."); + } else { + Assert.assertThrows(expectedThrow, () -> runTransform(tf)); + } + } - // the xml source - InputStream xmlStream = this.getClass().getResourceAsStream("SecureProcessingTest.xml"); - StreamSource xmlSource = new StreamSource(xmlStream); + private void runTransform(TransformerFactory tf) + throws Exception { + StreamSource xslSource = new StreamSource(getSystemId(SRC_DIR + "/SecureProcessingTest.xsl")); + StreamSource xmlSource = new StreamSource(getSystemId(SRC_DIR + "/SecureProcessingTest.xml")); // the xml result StringWriter xmlResultString = new StringWriter(); StreamResult xmlResultStream = new StreamResult(xmlResultString); - - // the transformer - TransformerFactory transformerFactory = null; - Transformer transformer = null; - - // transform with a non-secure Transformer - // expect success - String xmlResult; - if (!_isSecureMode) { // jaxp secure feature can not be turned off when - // security manager is present - try { - transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); - transformer = transformerFactory.newTransformer(xslSource); - transformer.transform(xmlSource, xmlResultStream); - } catch (TransformerConfigurationException ex) { - ex.printStackTrace(); - Assert.fail(ex.toString()); - } catch (TransformerException ex) { - ex.printStackTrace(); - Assert.fail(ex.toString()); - } - - // expected success - // and the result is ... - xmlResult = xmlResultString.toString(); - System.out.println("Transformation result (SECURE_PROCESSING == false) = \"" + xmlResult + "\""); - } - - // now do same transformation but with SECURE_PROCESSING == true - // expect Exception - boolean exceptionCaught = false; - - // the style sheet - xslStream = this.getClass().getResourceAsStream("SecureProcessingTest.xsl"); - xslSource = new StreamSource(xslStream); - - // the xml source - xmlStream = this.getClass().getResourceAsStream("SecureProcessingTest.xml"); - xmlSource = new StreamSource(xmlStream); - - // the xml result - xmlResultString = new StringWriter(); - xmlResultStream = new StreamResult(xmlResultString); - - // the transformer - transformerFactory = null; - transformer = null; - - // transform with a secure Transformer - try { - transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - transformer = transformerFactory.newTransformer(xslSource); - transformer.transform(xmlSource, xmlResultStream); - } catch (TransformerConfigurationException ex) { - ex.printStackTrace(); - Assert.fail(ex.toString()); - } catch (TransformerException ex) { - // expected failure - System.out.println("expected failure: " + ex.toString()); - ex.printStackTrace(System.out); - exceptionCaught = true; - } - - // unexpected success? - if (!exceptionCaught) { - // and the result is ... - xmlResult = xmlResultString.toString(); - System.err.println("Transformation result (SECURE_PROCESSING == true) = \"" + xmlResult + "\""); - Assert.fail("SECURITY_PROCESSING == true, expected failure but got result: \"" + xmlResult + "\""); - } + Transformer transformer = tf.newTransformer(xslSource); + transformer.transform(xmlSource, xmlResultStream); } } diff --git a/test/jaxp/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java b/test/jaxp/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java index 24570c20bad..4e5d03151a9 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java @@ -23,7 +23,6 @@ package transform; -import java.io.FilePermission; import java.io.StringReader; import java.io.StringWriter; import java.nio.file.Files; @@ -36,16 +35,18 @@ import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import static jaxp.library.JAXPTestUtilities.SRC_DIR; +import static jaxp.library.JAXPTestUtilities.assertDoesNotThrow; +import static jaxp.library.JAXPTestUtilities.getSystemId; +import static jaxp.library.JAXPTestUtilities.getSystemProperty; import org.testng.Assert; +import static org.testng.Assert.assertEquals; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static jaxp.library.JAXPTestUtilities.clearSystemProperty; -import static jaxp.library.JAXPTestUtilities.setSystemProperty; -import static jaxp.library.JAXPTestUtilities.getSystemProperty; /* * @test + * @bug 8062518 8153082 8165116 8343001 * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @compile DocumentExtFunc.java * @run testng/othervm transform.XSLTFunctionsTest @@ -53,6 +54,21 @@ */ public class XSLTFunctionsTest { + @DataProvider(name = "propertyName") + public static Object[][] getSettings() { + return new Object[][] { + // legacy property name + {ORACLE_ENABLE_EXTENSION_FUNCTION, true, true, null}, + {ORACLE_ENABLE_EXTENSION_FUNCTION, true, false, TransformerException.class}, + // legacy system property name + {SP_ENABLE_EXTENSION_FUNCTION, false, true, null}, + {SP_ENABLE_EXTENSION_FUNCTION, false, false, TransformerException.class}, + // current property and system property name + {SP_ENABLE_EXTENSION_FUNCTION_SPEC, true, true, null}, + {SP_ENABLE_EXTENSION_FUNCTION_SPEC, true, false, TransformerException.class}, + }; + } + /** * @bug 8165116 * Verifies that redirect works properly when extension function is enabled @@ -86,59 +102,50 @@ public void testRedirect(String xml, String xsl, String output, String redirect) } /** - * @bug 8161454 - * Verifies that the new / correct name is supported, as is the old / incorrect - * one for compatibility + * @bug 8161454 8343001 + * Verifies that legacy property names are continually supported for compatibility. + * + * @param property the property name + * @param isAPIProperty indicates whether the property can be set via the factory + * @param value the property value + * @param expectedThrow the expected throw if the specified DTD can not be + * resolved. + * @throws Exception if the test fails */ - @Test - public void testNameChange() { - - boolean feature; - TransformerFactory tf = TransformerFactory.newInstance(); - feature = tf.getFeature(ORACLE_ENABLE_EXTENSION_FUNCTION); - System.out.println("Default setting: " + feature); - // The default: true if no SecurityManager, false otherwise - Assert.assertTrue(feature == getDefault()); - - setSystemProperty(SP_ENABLE_EXTENSION_FUNCTION, getDefaultOpposite()); - tf = TransformerFactory.newInstance(); - feature = tf.getFeature(ORACLE_ENABLE_EXTENSION_FUNCTION); - System.out.println("After setting " + SP_ENABLE_EXTENSION_FUNCTION + ": " + feature); - clearSystemProperty(SP_ENABLE_EXTENSION_FUNCTION); - // old/incorrect name is still supported - Assert.assertTrue(feature != getDefault()); - - setSystemProperty(SP_ENABLE_EXTENSION_FUNCTION_SPEC, getDefaultOpposite()); - tf = TransformerFactory.newInstance(); - feature = tf.getFeature(ORACLE_ENABLE_EXTENSION_FUNCTION); - System.out.println("After setting " + SP_ENABLE_EXTENSION_FUNCTION_SPEC + ": " + feature); - clearSystemProperty(SP_ENABLE_EXTENSION_FUNCTION_SPEC); - // new/correct name is effective - Assert.assertTrue(feature != getDefault()); - } - - final boolean isSecure; - { - String runSecMngr = getSystemProperty("runSecMngr"); - isSecure = runSecMngr != null && runSecMngr.equals("true"); - } - - // The default: true if no SecurityManager, false otherwise - private boolean getDefault() { - if (isSecure) { - return false; + @Test(dataProvider = "propertyName") + public void testNameChange(String property, boolean isAPIProperty, + boolean value, Class expectedThrow) + throws Exception { + if (expectedThrow == null) { + assertDoesNotThrow(() -> runTransform(property, isAPIProperty, value), + "Extension Functions property is set to " + value + " but exception is thrown."); } else { - return true; + Assert.assertThrows(expectedThrow, + () -> runTransform(property, isAPIProperty, value)); } } - // Gets a String value that is opposite to the default value - private String getDefaultOpposite() { - if (isSecure) { - return "true"; - } else { - return "false"; + private void runTransform(String property, boolean isAPIProperty, boolean value) + throws Exception { + StreamSource xslSource = new StreamSource(getSystemId(SRC_DIR + "/SecureProcessingTest.xsl")); + StreamSource xmlSource = new StreamSource(getSystemId(SRC_DIR + "/SecureProcessingTest.xml")); + + // the xml result + StringWriter xmlResultString = new StringWriter(); + StreamResult xmlResultStream = new StreamResult(xmlResultString); + + if (!isAPIProperty) { + System.setProperty(property, Boolean.toString(value)); + } + TransformerFactory tf = TransformerFactory.newInstance(); + if (isAPIProperty) { + tf.setFeature(property, value); + } + Transformer transformer = tf.newTransformer(xslSource); + if (!isAPIProperty) { + System.clearProperty(property); } + transformer.transform(xmlSource, xmlResultStream); } /** diff --git a/test/jdk/javax/xml/jaxp/common/8032908/XSLT.java b/test/jdk/javax/xml/jaxp/common/8032908/XSLT.java index 22a4346edae..d4d84bf2b9e 100644 --- a/test/jdk/javax/xml/jaxp/common/8032908/XSLT.java +++ b/test/jdk/javax/xml/jaxp/common/8032908/XSLT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -23,12 +23,12 @@ /** * @test - * @bug 8032908 8081392 + * @bug 8032908 8081392 8343001 * @summary Test if Node.getTextContent() function correctly returns children * content and also check that Node.getNodeValue() returns null value for * Element nodes * @compile TestFunc.java XSLT.java - * @run main/othervm XSLT + * @run main/othervm -Djdk.xml.enableExtensionFunctions=true XSLT */ import java.io.ByteArrayOutputStream; import javax.xml.transform.Transformer; diff --git a/test/jdk/javax/xml/jaxp/parsers/8024707/XSLT.java b/test/jdk/javax/xml/jaxp/parsers/8024707/XSLT.java index e6a03f5af68..89cb0741cfa 100644 --- a/test/jdk/javax/xml/jaxp/parsers/8024707/XSLT.java +++ b/test/jdk/javax/xml/jaxp/parsers/8024707/XSLT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -23,10 +23,10 @@ /** * @test - * @bug 8024707 + * @bug 8024707 8343001 * @summary Test for XSLT extension function with 1 element sized nodelist * @compile TestFunc.java XSLT.java - * @run main/othervm XSLT + * @run main/othervm -Djdk.xml.enableExtensionFunctions=true XSLT * @author aleksej.efimov@oracle.com */ diff --git a/test/jdk/javax/xml/jaxp/transform/8004476/XSLTExFuncTest.java b/test/jdk/javax/xml/jaxp/transform/8004476/XSLTExFuncTest.java index e23a0329bba..dc75a7980ae 100644 --- a/test/jdk/javax/xml/jaxp/transform/8004476/XSLTExFuncTest.java +++ b/test/jdk/javax/xml/jaxp/transform/8004476/XSLTExFuncTest.java @@ -30,7 +30,7 @@ /** * @test - * @bug 8004476 + * @bug 8004476 8343001 * @summary test XSLT extension functions * @run main/othervm XSLTExFuncTest */ @@ -77,18 +77,18 @@ public static void main(String[] args) { } /** - * by default, extension function is enabled + * As of JDK-8343001, extension function is disabled by default. */ public void testExtFunc() { TransformerFactory factory = TransformerFactory.newInstance(); try { transform(factory); - System.out.println("testExtFunc: OK"); } catch (TransformerConfigurationException e) { fail(e.getMessage()); } catch (TransformerException ex) { - fail(ex.getMessage()); + //expected since extension function is disallowed + System.out.println("testExtFunc: OK"); } } From 43b337eb438f230dbca903b56e0809fc36fcd71d Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Wed, 4 Dec 2024 03:44:41 +0000 Subject: [PATCH 103/171] 8344304: [s390x] ubsan: negation of -2147483648 cannot be represented in type 'int' Reviewed-by: lucy, dlong --- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 8 +++-- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 33 ++++++++++++++++++- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 4 ++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index dee64d3db26..bb0494dc478 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -1532,8 +1532,12 @@ void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr // cpu register - constant jint c = right->as_constant_ptr()->as_jint(); switch (code) { - case lir_add: __ z_agfi(lreg, c); break; - case lir_sub: __ z_agfi(lreg, -c); break; // note: -min_jint == min_jint + case lir_add: + __ add2reg_32(lreg, c); + break; + case lir_sub: + __ add2reg_32(lreg, java_negate(c)); + break; case lir_mul: __ z_msfi(lreg, c); break; default: ShouldNotReachHere(); } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index aacfb894c72..c297c66b02b 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -657,7 +657,7 @@ void MacroAssembler::add2reg(Register r1, int64_t imm, Register r2) { z_aghik(r1, r2, imm); return; } - z_lgr(r1, r2); + lgr_if_needed(r1, r2); z_aghi(r1, imm); return; } @@ -681,6 +681,37 @@ void MacroAssembler::add2reg(Register r1, int64_t imm, Register r2) { z_agfi(r1, imm); } +void MacroAssembler::add2reg_32(Register r1, int64_t imm, Register r2) { + assert(Immediate::is_simm32(imm), "probably an implicit conversion went wrong"); + + if (r2 == noreg) { r2 = r1; } + + // Handle special case imm == 0. + if (imm == 0) { + lr_if_needed(r1, r2); + // Nothing else to do. + return; + } + + if (Immediate::is_simm16(imm)) { + if (r1 == r2){ + z_ahi(r1, imm); + return; + } + if (VM_Version::has_DistinctOpnds()) { + z_ahik(r1, r2, imm); + return; + } + lr_if_needed(r1, r2); + z_ahi(r1, imm); + return; + } + + // imm is simm32 + lr_if_needed(r1, r2); + z_afi(r1, imm); +} + // Generic operation r := b + x + d // // Addition of several operands with address generation semantics - sort of: diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 91703fac994..15968812818 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -156,7 +156,9 @@ class MacroAssembler: public Assembler { unsigned int mul_reg64_const16(Register rval, Register work, int cval); // Generic operation r1 := r2 + imm. - void add2reg(Register r1, int64_t imm, Register r2 = noreg); + void add2reg (Register r1, int64_t imm, Register r2 = noreg); + void add2reg_32(Register r1, int64_t imm, Register r2 = noreg); + // Generic operation r := b + x + d. void add2reg_with_index(Register r, int64_t d, Register x, Register b = noreg); From 447f8d49963fef59d36c464c4b31bff79ef02de3 Mon Sep 17 00:00:00 2001 From: Joe Wang Date: Wed, 4 Dec 2024 03:48:24 +0000 Subject: [PATCH 104/171] 8345353: Test for JDK-8344800 W3C DTDs and XSDs in the built-in Catalog Reviewed-by: rriggs, lancea --- .../common/jdkcatalog/JDKCatalogTest.java | 271 ++++++++++++++++++ .../common/jdkcatalog/TestCatalog.xml | 8 + .../unittest/common/jdkcatalog/dtdtest.xml | 20 ++ .../common/jdkcatalog/testDatatypes.xml | 11 + .../common/jdkcatalog/testDatatypes.xsd | 29 ++ .../unittest/common/jdkcatalog/testXML.xml | 10 + .../unittest/common/jdkcatalog/testXML.xsd | 27 ++ .../common/jdkcatalog/testXMLSchema.xml | 6 + .../common/jdkcatalog/xhtml-frameset.xml | 14 + .../jaxp/unittest/common/jdkcatalog/xhtml.xml | 12 + .../unittest/common/jdkcatalog/xsdtest.xml | 7 + 11 files changed, 415 insertions(+) create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/JDKCatalogTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/TestCatalog.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/dtdtest.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xsd create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xsd create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXMLSchema.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml-frameset.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml.xml create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xsdtest.xml diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/JDKCatalogTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/JDKCatalogTest.java new file mode 100644 index 00000000000..0f4c0aec2de --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/JDKCatalogTest.java @@ -0,0 +1,271 @@ +/* + * 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. + */ +package common.jdkcatalog; + +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Collectors; +import javax.xml.XMLConstants; +import javax.xml.catalog.CatalogFeatures; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.sax.SAXSource; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; +import org.testng.Assert; +import org.testng.Assert.ThrowingRunnable; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/* + * @test + * @bug 8344800 8345353 + * @run testng/othervm common.jdkcatalog.JDKCatalogTest + * @summary Verifies the W3C DTDs and XSDs in the JDK built-in catalog. + */ +public class JDKCatalogTest { + static String CLS_DIR = System.getProperty("test.classes"); + static String SRC_DIR = System.getProperty("test.src"); + public static boolean isWindows = false; + static { + if (System.getProperty("os.name").contains("Windows")) { + isWindows = true; + } + }; + public static final String JDKCATALOG_RESOLVE = "jdk.xml.jdkcatalog.resolve"; + static final String PUBLIC_ID = "{{publicId}}"; + static final String SYSTEM_ID = "{{systemId}}"; + static final String XSD_LOCATION = "{{SCHEMA_LOCATION}}"; + static final String TARGET_NAMESPACE = "{{targetNamespace}}"; + static final String ROOT_ELEMENT = "{{rootElement}}"; + + /* + * DataProvider: for verifying DTDs in the JDKCatalog + * Data provided: see test testExternalDTD + */ + @DataProvider(name = "externalDTD") + public Object[][] getDTD() throws Exception { + return new Object[][]{ + // verifies the test method correctly throws an exception if the specified + // DTD can not be resolved + {"-//ORG//DTD FOO 200102//EN", "http://foo.org/2001/bar.dtd", SAXException.class}, + // this test also verifies datatypes.dtd as it's referenced in XMLSchema.dtd + {"-//W3C//DTD XMLSCHEMA 200102//EN", "http://www.w3.org/2001/XMLSchema.dtd", null}, + {"-//W3C//DTD XHTML 1.0 Frameset//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd", null}, + {"-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null}, + {"-//W3C//DTD XHTML 1.0 Transitional//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd", null}, + {"-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", null}, + {"-//W3C//DTD Specification V2.10//EN", "http://www.w3.org/2002/xmlspec/dtd/2.10/xmlspec.dtd", null}, + }; + } + + /* + * DataProvider: for verifying XSDs in the JDKCatalog + * Data provided: see test testXSD + */ + @DataProvider(name = "getXSD") + public Object[][] getXSD() throws Exception { + return new Object[][]{ + // verifies the test method correctly throws an exception if the specified + // XSD can not be resolved + {"xsdtest.xml", "http://foo.org/2001/bar.xsd", "http://foo.org/2001/bar", "root", null, SAXException.class}, + // application XSD is resolved by a custom catalog, the W3C XSD then by the JDKCatalog + {"testXML.xml", "http://www.w3.org/2001/xml.xsd", "http://www.w3.org/XML/1998/namespace", "testXMLXSD", "TestCatalog.xml", null}, + // this test also verifies XMLSchema.dtd and xml.xsd as they are referenced + {"testXMLSchema.xml", "http://www.w3.org/2001/XMLSchema.xsd", "http://www.w3.org/2001/XMLSchema", "xs:schema", null, null}, + {"testDatatypes.xml", "http://www.w3.org/2009/XMLSchema/XMLSchema-datatypes.xsd", "http://www.w3.org/2001/XMLSchema-datatypes", "testDatatypes", "TestCatalog.xml", null}, + {"xhtml-frameset.xml", "https://www.w3.org/2002/08/xhtml/xhtml1-frameset.xsd", "http://www.w3.org/1999/xhtml", "html", null, null}, + {"xhtml.xml", "https://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd", "http://www.w3.org/1999/xhtml", "html", null, null}, + {"xhtml.xml", "https://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd", "http://www.w3.org/1999/xhtml", "html", null, null}, + {"xhtml.xml", "http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd", "http://www.w3.org/1999/xhtml", "html", null, null}, + }; + } + + /** + * Verifies that references to the W3C DTDs are resolved by the JDK built-in + * catalog. + * @param publicId the PUBLIC identifier + * @param systemId the SYSTEM identifier + * @param expectedThrow the expected throw if the specified DTD can not be + * resolved. + * @throws Exception if the test fails + */ + @Test(dataProvider = "externalDTD") + public void testExternalDTD(String publicId, String systemId, Class expectedThrow) + throws Exception { + final String xmlString = generateXMLWithDTDRef(publicId, systemId); + + if (expectedThrow == null) { + assertDoesNotThrow(() -> parseWithResolveStrict(xmlString), + "JDKCatalog shall resolve " + systemId + " but exception is thrown."); + } else { + Assert.assertThrows(expectedThrow, + () -> parseWithResolveStrict(xmlString)); + } + } + + /** + * Verifies that references to the W3C DTDs are resolved by the JDK built-in + * catalog. + * @param xmlTemplate a template used to generate an XML instance + * @param xsdLocation the XSD to be resolved + * @param targetNS the target namespace + * @param rootElement the root element + * @param catalog the custom catalog to be used to resolve XSDs used by the + * test. + * @param expectedThrow the expected throw if the specified DTD can not be + * resolved. + * @throws Exception if the test fails + */ + @Test(dataProvider = "getXSD") + public void testXSD(String xmlTemplate, String xsdLocation, String targetNS, String rootElement, String catalog, + Class expectedThrow) + throws Exception { + String xmlSrcPath = SRC_DIR + "/" + xmlTemplate; + final String xmlSrcId = getSysId(xmlSrcPath); + + final String customCatalog = getSysId((catalog != null) ? SRC_DIR + "/" + catalog : null); + + final String xmlString = generateXMLWithXSDRef(xmlSrcPath, xsdLocation, + targetNS, rootElement); + if (expectedThrow == null) { + assertDoesNotThrow(() -> validateWithResolveStrict(xmlString, xmlSrcId, customCatalog), + "JDKCatalog shall resolve " + xsdLocation + " but exception is thrown."); + } else { + Assert.assertThrows(expectedThrow, + () -> validateWithResolveStrict(xmlString, xmlSrcId, customCatalog)); + } + } + + /** + * Validate the specified XML document with jdk.xml.jdkCatalog.resolve set to strict. + * @param xml the XML document to be validated + * @param xmlSrcPathId the URI to the XML source (template in this case) + * @param customCatalog the custom catalog used to resolve local XSDs + * @throws Exception if validation fails + */ + public void validateWithResolveStrict(String xml, String xmlSrcPathId, String customCatalog) + throws Exception { + SAXSource ss = new SAXSource(new InputSource(new StringReader(xml))); + ss.setSystemId(xmlSrcPathId); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + schemaFactory.setProperty(JDKCATALOG_RESOLVE, "strict"); + if (customCatalog != null) { + schemaFactory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), customCatalog); + schemaFactory.setProperty(CatalogFeatures.Feature.RESOLVE.getPropertyName(), "continue"); + } + Validator validator = schemaFactory.newSchema().newValidator(); + validator.validate(ss); + } + + /** + * Parses the XML with jdk.xml.jdkCatalog.resolve set to strict. + * @param xml the XML document to be parsed + * @throws Exception if external access is denied + */ + public void parseWithResolveStrict(String xml) + throws Exception { + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + saxParserFactory.setNamespaceAware(true); + XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader(); + xmlReader.setProperty(JDKCATALOG_RESOLVE, "strict"); + xmlReader.setContentHandler(new DefaultHandler()); + xmlReader.parse(new InputSource(new StringReader(xml))); + } + + /** + * Generates an XML with the specified PUBLIC and SYSTEM identifiers. + * @param publicId the public identifier + * @param systemId the system identifier + * @return an XML + * @throws Exception if error happens + */ + private String generateXMLWithDTDRef(String publicId, String systemId) + throws Exception { + Path path = Paths.get(SRC_DIR + "/dtdtest.xml"); + String xmlString = Files.lines(path).map(line -> { + line = line.replace(PUBLIC_ID, publicId); + line = line.replace(SYSTEM_ID, systemId); + return line; + }).collect(Collectors.joining(System.lineSeparator())); + return xmlString; + } + + /** + * Generates an XML with the specified XSD location. + * @param xmlSrcPath the path to the XML source + * @param xsd the XSD location + * @return an XML + * @throws Exception if error happens + */ + private String generateXMLWithXSDRef(String xmlSrcPath, String xsd, + String targetNS, String rootElement) + throws Exception { + String xmlString = Files.lines(Paths.get(xmlSrcPath)).map(line -> { + if (line.contains(XSD_LOCATION)) { + line = line.replace(XSD_LOCATION, xsd); + } + if (line.contains(TARGET_NAMESPACE)) { + line = line.replace(TARGET_NAMESPACE, targetNS); + } + if (line.contains(ROOT_ELEMENT)) { + line = line.replace(ROOT_ELEMENT, rootElement); + } + return line; + }).collect(Collectors.joining(System.lineSeparator())); + return xmlString; + } + + /** + * Returns the System identifier (URI) of the source. + * @param path the path to the source + * @return the System identifier + */ + private String getSysId(String path) { + if (path == null) return null; + String xmlSysId = "file://" + path; + if (isWindows) { + path = path.replace('\\', '/'); + xmlSysId = "file:///" + path; + } + return xmlSysId; + } + + /** + * Asserts the run does not cause a Throwable. + * @param runnable the runnable + * @param message the message if the test fails + */ + private void assertDoesNotThrow(ThrowingRunnable runnable, String message) { + try { + runnable.run(); + } catch (Throwable t) { + Assert.fail(message + "\n Exception thrown: " + t.getMessage()); + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/TestCatalog.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/TestCatalog.xml new file mode 100644 index 00000000000..d54a4bd1c85 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/TestCatalog.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/dtdtest.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/dtdtest.xml new file mode 100644 index 00000000000..fd5fed4e266 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/dtdtest.xml @@ -0,0 +1,20 @@ + + + + + + XHTML 1.0 + + + +

      + XHTML 1.0 +

      +
        +
      • + xhtml1-transition.dtd +
      • +
      + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xml new file mode 100644 index 00000000000..11660802190 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xml @@ -0,0 +1,11 @@ + + + + Hello, World! + 12345 + 67.78 + false + 2024-11-24 + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xsd new file mode 100644 index 00000000000..4af92ad25c1 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testDatatypes.xsd @@ -0,0 +1,29 @@ + + + + + + + + Get access to the type defined in datatypes + + + + + + + + + + + + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xml new file mode 100644 index 00000000000..359076f37f6 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xml @@ -0,0 +1,10 @@ + + + + Hello, World! + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xsd new file mode 100644 index 00000000000..ba761f18926 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXML.xsd @@ -0,0 +1,27 @@ + + + + + + + Get access to the xml: attribute groups for xml:lang + as declared on 'schema' and 'documentation' below + + + + + + + + + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXMLSchema.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXMLSchema.xml new file mode 100644 index 00000000000..d7b13fa762b --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/testXMLSchema.xml @@ -0,0 +1,6 @@ + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml-frameset.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml-frameset.xml new file mode 100644 index 00000000000..f046df8ccf9 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml-frameset.xml @@ -0,0 +1,14 @@ + + + + Test XHTML + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml.xml new file mode 100644 index 00000000000..9ceb61516cd --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xhtml.xml @@ -0,0 +1,12 @@ + + + + Test XHTML + + +

      Test XHTML

      + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xsdtest.xml b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xsdtest.xml new file mode 100644 index 00000000000..36533f88065 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/jdkcatalog/xsdtest.xml @@ -0,0 +1,7 @@ + +<{{rootElement}} xmlns="{{targetNamespace}}" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="{{targetNamespace}} {{SCHEMA_LOCATION}}" + xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + + From 521ed72b87d0fb1def6d94485e08be22632deef0 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 4 Dec 2024 04:28:06 +0000 Subject: [PATCH 105/171] 8345357: test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java fails in ubuntu22.04 Reviewed-by: abhiscxk --- .../JRadioButton/8033699/bug8033699.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java b/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java index 9365e6ba45b..699338c4b30 100644 --- a/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java +++ b/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -28,6 +28,7 @@ * @summary Incorrect radio button behavior when pressing tab key * @run main bug8033699 */ + import java.awt.KeyboardFocusManager; import java.awt.Robot; import java.awt.event.ActionListener; @@ -57,42 +58,50 @@ public class bug8033699 { public static void main(String[] args) throws Throwable { SwingUtilities.invokeAndWait(() -> { - changeLAF(); - createAndShowGUI(); + changeLAF(); + createAndShowGUI(); }); robot = new Robot(); - Thread.sleep(100); - robot.waitForIdle(); - robot.setAutoDelay(100); + robot.waitForIdle(); + robot.delay(1000); // tab key test grouped radio button runTest1(); + robot.delay(100); // tab key test non-grouped radio button runTest2(); + robot.delay(100); // shift tab key test grouped and non-grouped radio button runTest3(); + robot.delay(100); // left/up key test in grouped radio button runTest4(); + robot.delay(100); // down/right key test in grouped radio button runTest5(); + robot.delay(100); // tab from radio button in group to next component in the middle of button group layout runTest6(); + robot.delay(100); // tab to radio button in group from component in the middle of button group layout runTest7(); + robot.delay(100); // down key circle back to first button in grouped radio button runTest8(); + robot.delay(100); // Verify that ActionListener is called when a RadioButton is selected using arrow key. runTest9(); + robot.delay(100); SwingUtilities.invokeAndWait(() -> mainFrame.dispose()); } From e15912b804ca42446f5fc309aa44043c9209b977 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 4 Dec 2024 06:02:08 +0000 Subject: [PATCH 106/171] 8345248: Module name 'transitive' not accepted for `requires transitive` Reviewed-by: vromero --- .../sun/tools/javac/parser/JavacParser.java | 8 +- .../javac/modules/RequiresTransitiveTest.java | 103 ++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index a02945dee3c..d975a6c927a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -4208,16 +4208,16 @@ List moduleDirectiveList() { while (true) { switch (token.kind) { case IDENTIFIER: - if (token.name() == names.transitive && !isTransitive) { + if (token.name() == names.transitive) { Token t1 = S.token(1); if (t1.kind == SEMI || t1.kind == DOT) { break loop; } + if (isTransitive) { + log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.RepeatedModifier); + } isTransitive = true; break; - } else if (token.name() == names.transitive && isTransitive) { - log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.RepeatedModifier); - break; } else { break loop; } diff --git a/test/langtools/tools/javac/modules/RequiresTransitiveTest.java b/test/langtools/tools/javac/modules/RequiresTransitiveTest.java index 6ff0dabec1f..03f34e96113 100644 --- a/test/langtools/tools/javac/modules/RequiresTransitiveTest.java +++ b/test/langtools/tools/javac/modules/RequiresTransitiveTest.java @@ -23,6 +23,7 @@ /* * @test + * @bug 8345248 * @summary tests for "requires transitive" * @library /tools/lib * @modules @@ -34,6 +35,8 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; +import java.util.Objects; import toolbox.JavacTask; import toolbox.Task; @@ -256,4 +259,104 @@ public void testRepeatedModifiers(Path base) throws Exception { throw new Exception("expected output not found: " + e); } } + + @Test //JDK-8345248: + public void testTransitiveModuleName(Path base) throws Exception { + Path lib = base.resolve("lib"); + Path libSrc = lib.resolve("src"); + Path transitive = libSrc.resolve("transitive"); + tb.writeJavaFiles(transitive, + """ + module transitive { + } + """ + ); + Path transitiveA = libSrc.resolve("transitive.a"); + tb.writeJavaFiles(transitiveA, + """ + module transitive.a { + } + """ + ); + + Path libClasses = lib.resolve("classes"); + Files.createDirectories(libClasses); + + new JavacTask(tb, Task.Mode.CMDLINE) + .options("--module-source-path", libSrc.toString()) + .files(findJavaFiles(libSrc)) + .outdir(libClasses) + .run() + .writeAll(); + + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + + Files.createDirectories(classes); + + tb.writeJavaFiles(src, + """ + module m { + requires transitive; + requires transitive.a; + } + """ + ); + + new JavacTask(tb, Task.Mode.CMDLINE) + .options("--module-path", libClasses.toString()) + .sourcepath(src) + .files(findJavaFiles(src)) + .outdir(classes) + .run() + .writeAll(); + + tb.writeJavaFiles(src, + """ + module m { + requires transitive transitive; + requires transitive transitive.a; + } + """ + ); + + new JavacTask(tb, Task.Mode.CMDLINE) + .options("--module-path", libClasses.toString()) + .sourcepath(src) + .files(findJavaFiles(src)) + .outdir(classes) + .run() + .writeAll(); + + tb.writeJavaFiles(src, + """ + module m { + requires transitive transitive transitive; + requires transitive transitive transitive.a; + } + """ + ); + + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .options("--module-path", libClasses.toString(), + "-XDrawDiagnostics") + .sourcepath(src) + .files(findJavaFiles(src)) + .outdir(classes) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + + List expected = List.of( + "module-info.java:2:25: compiler.err.repeated.modifier", + "module-info.java:3:25: compiler.err.repeated.modifier", + "2 errors" + ); + + if (!Objects.equals(expected, log)) { + throw new Exception("expected: " + expected + + ", but got: " + log); + } + } } From 4b928167435bbf41dd00425c927da761751ca704 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 4 Dec 2024 06:36:54 +0000 Subject: [PATCH 107/171] 8345375: Improve debuggability of test/jdk/java/net/Socket/CloseAvailable.java Reviewed-by: cstein, dfuchs --- test/jdk/java/net/Socket/CloseAvailable.java | 207 +++++++++++-------- 1 file changed, 116 insertions(+), 91 deletions(-) diff --git a/test/jdk/java/net/Socket/CloseAvailable.java b/test/jdk/java/net/Socket/CloseAvailable.java index bbeb37eb8f5..5ff7e0c03a3 100644 --- a/test/jdk/java/net/Socket/CloseAvailable.java +++ b/test/jdk/java/net/Socket/CloseAvailable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -25,7 +25,7 @@ * @test * @bug 4091859 8189366 * @library /test/lib - * @summary Test Socket.available() + * @summary Test Socket.getInputStream().available() * @run main CloseAvailable * @run main/othervm -Djava.net.preferIPv4Stack=true CloseAvailable */ @@ -48,110 +48,135 @@ public static void main(String[] args) throws Exception { testIOEOnClosed(false); } + /* + * Verifies that the Socket.getInputStream().available() throws an IOException + * if invoked after the socket has been closed. + */ static void testClose() throws IOException { - boolean error = true; - InetAddress addr = InetAddress.getLocalHost(); - ServerSocket ss = new ServerSocket(0, 0, addr); - int port = ss.getLocalPort(); - - Thread t = new Thread(new Thread("Close-Available-1") { - public void run() { - try { - Socket s = new Socket(addr, port); - s.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - - t.start(); - - Socket soc = ss.accept(); - ss.close(); - - DataInputStream is = new DataInputStream(soc.getInputStream()); - is.close(); - + System.out.println("testClose"); + final InetAddress addr = InetAddress.getLoopbackAddress(); + final Socket acceptedSocket; + try (final ServerSocket ss = new ServerSocket(0, 0, addr)) { + System.out.println("created server socket: " + ss); + final int port = ss.getLocalPort(); + // start a thread which initiates a socket connection to the server + Thread.ofPlatform().name("Close-Available-1") + .start(() -> { + try { + final Socket s = new Socket(addr, port); + System.out.println("created socket: " + s); + s.close(); + System.out.println("closed socket: " + s); + } catch (Exception e) { + System.err.println("exception in " + Thread.currentThread().getName() + + ": " + e); + e.printStackTrace(); + } + }); + // accept the client connect + acceptedSocket = ss.accept(); + System.out.println(ss + " accepted connection " + acceptedSocket); + } // (intentionally) close the ServerSocket + + final DataInputStream is = new DataInputStream(acceptedSocket.getInputStream()); + is.close(); // close the inputstream and thus the underlying socket + System.out.println("closed inputstream of socket: " + acceptedSocket); try { - is.available(); - } - catch (IOException ex) { - error = false; + final int av = is.available(); + // available() was expected to fail but didn't + throw new AssertionError("Socket.getInputStream().available() was expected to fail on " + + acceptedSocket + " but returned " + av); + } catch (IOException ex) { + // expected IOException + System.out.println("received the expected IOException: " + ex); } - if (error) - throw new RuntimeException("Available() can be called after stream closed."); } - // Verifies consistency of `available` behaviour when EOF reached, both - // explicitly and implicitly. + /* + * Verifies consistency of Socket.getInputStream().available() behaviour when EOF reached, both + * explicitly and implicitly. + */ static void testEOF(boolean readUntilEOF) throws IOException { System.out.println("testEOF, readUntilEOF: " + readUntilEOF); - InetAddress addr = InetAddress.getLoopbackAddress(); - ServerSocket ss = new ServerSocket(); - ss.bind(new InetSocketAddress(addr, 0), 0); - int port = ss.getLocalPort(); - - try (Socket s = new Socket(addr, port)) { - s.getOutputStream().write(0x42); - s.shutdownOutput(); - - try (Socket soc = ss.accept()) { - ss.close(); - - InputStream is = soc.getInputStream(); - int b = is.read(); - assert b == 0x42; - assert !s.isClosed(); - if (readUntilEOF) { - b = is.read(); - assert b == -1; + final InetAddress addr = InetAddress.getLoopbackAddress(); + try (final ServerSocket ss = new ServerSocket()) { + ss.bind(new InetSocketAddress(addr, 0), 0); + System.out.println("server socket bound: " + ss); + final int port = ss.getLocalPort(); + try (final Socket s = new Socket(addr, port)) { + System.out.println("created socket: " + s); + s.getOutputStream().write(0x42); + s.shutdownOutput(); + + try (final Socket soc = ss.accept()) { + System.out.println("accepted socket: " + soc); + ss.close(); + System.out.println("closed server socket: " + ss); + + final InputStream is = soc.getInputStream(); + int b = is.read(); + assert b == 0x42 : "unexpected byte read: " + b; + assert !s.isClosed() : "socket " + s + " is unexpectedly closed"; + if (readUntilEOF) { + b = is.read(); + assert b == -1 : "unexpected number of bytes read: " + b; + } + + int a; + for (int i = 0; i < 100; i++) { + a = is.available(); + System.out.print(a + ", "); + if (a != 0) { + throw new RuntimeException("Unexpected non-zero available: " + a); + } + } + assert !s.isClosed() : "socket " + s + " is unexpectedly closed"; + final int more = is.read(); + assert more == -1 : "unexpected byte read: " + more; } - - int a; - for (int i = 0; i < 100; i++) { - a = is.available(); - System.out.print(a + ", "); - if (a != 0) - throw new RuntimeException("Unexpected non-zero available: " + a); - } - assert !s.isClosed(); - assert is.read() == -1; } } System.out.println("\ncomplete"); } - // Verifies IOException thrown by `available`, on a closed input stream - // that may, or may not, have reached EOF prior to closure. + /* + * Verifies IOException thrown by Socket.getInputStream().available(), on a closed input stream + * that may, or may not, have reached EOF prior to closure. + */ static void testIOEOnClosed(boolean readUntilEOF) throws IOException { System.out.println("testIOEOnClosed, readUntilEOF: " + readUntilEOF); - InetAddress addr = InetAddress.getLoopbackAddress(); - ServerSocket ss = new ServerSocket(); - ss.bind(new InetSocketAddress(addr, 0), 0); - int port = ss.getLocalPort(); - - try (Socket s = new Socket(addr, port)) { - s.getOutputStream().write(0x43); - s.shutdownOutput(); - - try (Socket soc = ss.accept()) { - ss.close(); - - InputStream is = soc.getInputStream(); - int b = is.read(); - assert b == 0x43; - assert !s.isClosed(); - if (readUntilEOF) { - b = is.read(); - assert b == -1; - } - is.close(); - try { - b = is.available(); - throw new RuntimeException("UNEXPECTED successful read: " + b); - } catch (IOException expected) { - System.out.println("caught expected IOException:" + expected); + final InetAddress addr = InetAddress.getLoopbackAddress(); + try (final ServerSocket ss = new ServerSocket()) { + ss.bind(new InetSocketAddress(addr, 0), 0); + System.out.println("server socket bound: " + ss); + final int port = ss.getLocalPort(); + + try (final Socket s = new Socket(addr, port)) { + System.out.println("created socket: " + s); + s.getOutputStream().write(0x43); + s.shutdownOutput(); + + try (final Socket soc = ss.accept()) { + System.out.println("accepted socket: " + soc); + ss.close(); + System.out.println("closed server socket: " + ss); + + final InputStream is = soc.getInputStream(); + int b = is.read(); + assert b == 0x43 : "unexpected byte read: " + b; + assert !s.isClosed() : "socket " + s + " is unexpectedly closed"; + if (readUntilEOF) { + b = is.read(); + assert b == -1 : "unexpected byte read: " + b; + } + is.close(); + System.out.println("closed inputstream of socket: " + soc); + try { + b = is.available(); + throw new RuntimeException("UNEXPECTED successful read: " + b); + } catch (IOException expected) { + System.out.println("caught expected IOException:" + expected); + } } } } From 4c33caa185ccc2f406cf2e9c4c58c3cc0a1856f8 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Wed, 4 Dec 2024 07:26:34 +0000 Subject: [PATCH 108/171] 8344609: Check ResourceMark nesting when allocating a GrowableArray on an alternative ResourceArea Reviewed-by: dholmes, mdoerr --- src/hotspot/share/utilities/growableArray.cpp | 13 +++++++++++++ src/hotspot/share/utilities/growableArray.hpp | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/utilities/growableArray.cpp b/src/hotspot/share/utilities/growableArray.cpp index 60ed0a477e8..2fd8166ba4d 100644 --- a/src/hotspot/share/utilities/growableArray.cpp +++ b/src/hotspot/share/utilities/growableArray.cpp @@ -61,6 +61,10 @@ GrowableArrayNestingCheck::GrowableArrayNestingCheck(bool on_resource_area) : _nesting(on_resource_area ? Thread::current()->resource_area()->nesting() : 0) { } +GrowableArrayNestingCheck::GrowableArrayNestingCheck(Arena* arena) : + _nesting((arena->get_tag() == Arena::Tag::tag_ra) ? static_cast(arena)->nesting() : 0) { +} + void GrowableArrayNestingCheck::on_resource_area_alloc() const { // Check for insidious allocation bug: if a GrowableArray overflows, the // grown array must be allocated under the same ResourceMark as the original. @@ -70,6 +74,11 @@ void GrowableArrayNestingCheck::on_resource_area_alloc() const { } } +void GrowableArrayNestingCheck::on_arena_alloc(Arena* arena) const { + if ((arena->get_tag() == Arena::Tag::tag_ra) && (_nesting != static_cast(arena)->nesting())) { + fatal("allocation bug: GrowableArray is growing within nested ResourceMark"); + } +} void GrowableArrayMetadata::init_checks(const GrowableArrayBase* array) const { // Stack allocated arrays support all three element allocation locations if (array->allocated_on_stack_or_embedded()) { @@ -89,4 +98,8 @@ void GrowableArrayMetadata::on_resource_area_alloc_check() const { _nesting_check.on_resource_area_alloc(); } +void GrowableArrayMetadata::on_arena_alloc_check() const { + _nesting_check.on_arena_alloc(arena()); +} + #endif // ASSERT diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 2eb8e6fd09e..5dec089a4fb 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -608,8 +608,10 @@ class GrowableArrayNestingCheck { public: GrowableArrayNestingCheck(bool on_resource_area); + GrowableArrayNestingCheck(Arena* arena); void on_resource_area_alloc() const; + void on_arena_alloc(Arena* arena) const; }; #endif // ASSERT @@ -649,7 +651,7 @@ class GrowableArrayMetadata { // Arena allocation GrowableArrayMetadata(Arena* arena) : _bits(bits(arena)) - debug_only(COMMA _nesting_check(false)) { + debug_only(COMMA _nesting_check(arena)) { } // CHeap allocation @@ -676,6 +678,7 @@ class GrowableArrayMetadata { void init_checks(const GrowableArrayBase* array) const; void on_resource_area_alloc_check() const; + void on_arena_alloc_check() const; #endif // ASSERT bool on_C_heap() const { return (_bits & 1) == 1; } @@ -740,6 +743,7 @@ class GrowableArray : public GrowableArrayWithAllocator> { } assert(on_arena(), "Sanity"); + debug_only(_metadata.on_arena_alloc_check()); return allocate(this->_capacity, _metadata.arena()); } From 9e2b66fb0f2b86d2c70b8ec5cce2eab123c7a9c1 Mon Sep 17 00:00:00 2001 From: Robbin Ehn Date: Wed, 4 Dec 2024 07:53:30 +0000 Subject: [PATCH 109/171] 8345178: RISC-V: Add gtests for narrow cmpxchg Reviewed-by: fyang, mli --- .../gtest/riscv/test_assembler_riscv.cpp | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/test/hotspot/gtest/riscv/test_assembler_riscv.cpp b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp index 415f72f9a0f..7df6c6b933e 100644 --- a/test/hotspot/gtest/riscv/test_assembler_riscv.cpp +++ b/test/hotspot/gtest/riscv/test_assembler_riscv.cpp @@ -260,4 +260,85 @@ TEST_VM(RiscV, cmpxchg_int32_plain_maybe_zacas) { } } +template +class NarrowCmpxchgTester { + public: + typedef TESTSIZE (*cmpxchg_func)(intptr_t addr, TESTSIZE expected, TESTSIZE new_value, TESTSIZE result, + int64_t scratch0, int64_t scratch1, int64_t scratch2); + + static TESTSIZE narrow_cmpxchg(intptr_t addr, TESTSIZE expected, TESTSIZE new_value, TESTSIZE result, bool boolean_result = false) { + BufferBlob* bb = BufferBlob::create("riscvTest", 128); + CodeBuffer code(bb); + MacroAssembler _masm(&code); + address entry = _masm.pc(); + { + _masm.cmpxchg_narrow_value(/*addr*/ c_rarg0, /*expected*/ c_rarg1, /*new_value*/c_rarg2, + ASMSIZE, Assembler::relaxed, Assembler::relaxed, + /*result*/ c_rarg3, boolean_result, c_rarg4, c_rarg5, c_rarg6); /* Uses also t0-t1, caller saved */ + _masm.mv(c_rarg0, c_rarg3); + _masm.ret(); + } + _masm.flush(); // icache invalidate + TESTSIZE ret = ((cmpxchg_func)entry)(addr, expected, new_value, result, -1, -1, -1); + BufferBlob::free(bb); + return ret; + } +}; + +template +void run_narrow_cmpxchg_tests() { + // Assume natural aligned + TESTSIZE data[8]; + TESTSIZE ret; + for (int i = 0; i < 7; i++) { + memset(data, -1, sizeof(data)); + + data[i] = 121; + ret = NarrowCmpxchgTester::narrow_cmpxchg((intptr_t)&data[i], 121, 42, /* result */ 67, false); + ASSERT_EQ(ret, 121); + ASSERT_EQ(data[i], 42); + + data[i] = 121; + ret = NarrowCmpxchgTester::narrow_cmpxchg((intptr_t)&data[i], 120, 42, /* result */ 67, false); + ASSERT_EQ(ret, 121); + ASSERT_EQ(data[i], 121); + + data[i] = 121; + ret = NarrowCmpxchgTester::narrow_cmpxchg((intptr_t)&data[i], 121, 42, /* result */ 67, true); + ASSERT_EQ(ret, 1); + ASSERT_EQ(data[i], 42); + + data[i] = 121; + ret = NarrowCmpxchgTester::narrow_cmpxchg((intptr_t)&data[i], 120, 42, /* result */ 67, true); + ASSERT_EQ(ret, 0); + ASSERT_EQ(data[i], 121); + } +} + +TEST_VM(RiscV, cmpxchg_int16_lr_sc) { + bool zacas = UseZacas; + UseZacas = false; + run_narrow_cmpxchg_tests(); + UseZacas = zacas; +} + +TEST_VM(RiscV, cmpxchg_int8_lr_sc) { + bool zacas = UseZacas; + UseZacas = false; + run_narrow_cmpxchg_tests(); + UseZacas = zacas; +} + +TEST_VM(RiscV, cmpxchg_int16_maybe_zacas) { + if (UseZacas) { + run_narrow_cmpxchg_tests(); + } +} + +TEST_VM(RiscV, cmpxchg_int8_maybe_zacas) { + if (UseZacas) { + run_narrow_cmpxchg_tests(); + } +} + #endif // RISCV From 943aa033ae3b40a65cdf157797f0a9685019dc48 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 4 Dec 2024 08:06:58 +0000 Subject: [PATCH 110/171] 8345404: [ppc64le] ProblemList TestAllocOutOfMemory.java#large Reviewed-by: mdoerr --- test/hotspot/jtreg/ProblemList.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index e09004aa58b..08678af706d 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -95,7 +95,7 @@ gc/TestAlwaysPreTouchBehavior.java#G1 8334513 generic-all gc/TestAlwaysPreTouchBehavior.java#Z 8334513 generic-all gc/TestAlwaysPreTouchBehavior.java#Epsilon 8334513 generic-all gc/stress/gclocker/TestExcessGCLockerCollections.java 8229120 generic-all -gc/shenandoah/oom/TestAllocOutOfMemory.java 8344312 linux-ppc64le +gc/shenandoah/oom/TestAllocOutOfMemory.java#large 8344312 linux-ppc64le ############################################################################# From cf1eb58d6723ca3856687bf52167455eece2a260 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 4 Dec 2024 08:11:33 +0000 Subject: [PATCH 111/171] 8344935: [ubsan]: javaThread.hpp:1241:52: runtime error: load of value 9831830, which is not a valid value for type 'freeze_result' Co-authored-by: Richard Reingruber Reviewed-by: rrich, pchilanomate --- src/hotspot/share/runtime/continuationFreezeThaw.cpp | 7 ++++++- src/hotspot/share/runtime/javaThread.cpp | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 18beb10f0b4..a6c8e64e55e 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -256,7 +256,9 @@ class Config { using OopT = std::conditional_t; static freeze_result freeze(JavaThread* thread, intptr_t* const sp) { - return freeze_internal(thread, sp); + freeze_result res = freeze_internal(thread, sp); + JFR_ONLY(assert((res == freeze_ok) || (res == thread->last_freeze_fail_result()), "freeze failure not set")); + return res; } static freeze_result freeze_preempt(JavaThread* thread, intptr_t* const sp) { @@ -1722,6 +1724,9 @@ static inline freeze_result freeze_internal(JavaThread* current, intptr_t* const log_develop_debug(continuations)("PINNED due to critical section/hold monitor"); verify_continuation(cont.continuation()); freeze_result res = entry->is_pinned() ? freeze_pinned_cs : freeze_pinned_monitor; + if (!preempt) { + JFR_ONLY(current->set_last_freeze_fail_result(res);) + } log_develop_trace(continuations)("=== end of freeze (fail %d)", res); // Avoid Thread.yield() loops without safepoint polls. if (SafepointMechanism::should_process(current) && !preempt) { diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index fd9f75c41b4..5123ab69ff3 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -518,6 +518,10 @@ JavaThread::JavaThread(MemTag mem_tag) : _SleepEvent(ParkEvent::Allocate(this)), +#if INCLUDE_JFR + _last_freeze_fail_result(freeze_ok), +#endif + _lock_stack(this), _om_cache(this) { set_jni_functions(jni_functions()); @@ -2361,4 +2365,4 @@ void JavaThread::post_vthread_pinned_event(EventVirtualThreadPinned* event, cons event->commit(); } } -#endif \ No newline at end of file +#endif From 38927fc5900184d6231f3da08dca9fc30711816c Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Wed, 4 Dec 2024 08:31:55 +0000 Subject: [PATCH 112/171] 8343213: TEST_BUG: [Graal] java/lang/ref/Basic.java fails Reviewed-by: mchung --- test/jdk/java/lang/ref/Basic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/lang/ref/Basic.java b/test/jdk/java/lang/ref/Basic.java index deb9ec34826..abed7fdcd64 100644 --- a/test/jdk/java/lang/ref/Basic.java +++ b/test/jdk/java/lang/ref/Basic.java @@ -46,8 +46,8 @@ protected void finalize() { }; protected void finalize() { - Basic.finalized = true; System.err.println("Finalized " + this); + Basic.finalized = true; } public static class Sub { }; From 3d49665b85619038c082566b0bc38c0ebe5f752e Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 4 Dec 2024 09:23:57 +0000 Subject: [PATCH 113/171] 8345286: Remove use of SecurityManager API from misc areas Reviewed-by: alanb, kevinw, sgehwolf --- .../platform/CgroupSubsystemController.java | 19 ++-- .../platform/CgroupSubsystemFactory.java | 12 +-- .../jdk/internal/platform/CgroupUtil.java | 92 ------------------- .../platform/cgroupv2/CgroupV2Subsystem.java | 13 +-- .../share/classes/java/io/FilePermission.java | 1 - .../share/classes/java/lang/String.java | 2 - .../share/classes/java/lang/System.java | 2 - .../java/lang/invoke/ClassSpecializer.java | 1 - .../java/lang/reflect/ProxyGenerator.java | 1 - .../share/classes/java/net/URLConnection.java | 1 - .../javax/crypto/JceSecurity.java.template | 1 - .../jdk/internal/loader/NativeLibraries.java | 11 +-- .../jdk/internal/logger/LazyLoggers.java | 2 - .../jdk/internal/misc/InnocuousThread.java | 3 +- .../share/classes/jdk/internal/perf/Perf.java | 64 +------------ .../jdk/internal/perf/PerfCounter.java | 7 +- .../classes/jdk/internal/ref/Cleaner.java | 18 ++-- .../internal/reflect/ReflectionFactory.java | 17 ---- .../sun/net/www/http/KeepAliveStream.java | 1 - .../sun/management/VMManagementImpl.java | 16 +--- .../incubator/vector/VectorIntrinsics.java | 9 +- .../protocol/local/PerfDataBuffer.java | 11 +-- 22 files changed, 43 insertions(+), 261 deletions(-) delete mode 100644 src/java.base/linux/classes/jdk/internal/platform/CgroupUtil.java diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java index 38be8628fb4..17bc60ef863 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2022, Red Hat Inc. + * 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 @@ -28,8 +29,8 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.math.BigInteger; +import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -61,13 +62,13 @@ public interface CgroupSubsystemController { public static String getStringValue(CgroupSubsystemController controller, String param) { if (controller == null) return null; - try { - return CgroupUtil.readStringValue(controller, param); - } - catch (IOException e) { + Path filePath = Path.of(controller.path(), param); + try (Stream lines = Files.lines(filePath)) { + Optional firstLine = lines.findFirst(); + return firstLine.orElse(null); + } catch (UncheckedIOException | IOException e) { return null; } - } /** @@ -92,8 +93,8 @@ public static long getLongValueMatchingLine(CgroupSubsystemController controller return retval; } try { - Path filePath = Paths.get(controller.path(), param); - List lines = CgroupUtil.readAllLinesPrivileged(filePath); + Path filePath = Path.of(controller.path(), param); + List lines = Files.readAllLines(filePath); for (String line : lines) { if (line.startsWith(match)) { retval = conversion.apply(line); @@ -161,7 +162,7 @@ public static double getDoubleValue(CgroupSubsystemController controller, String public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname, long defaultRetval) { if (controller == null) return defaultRetval; - try (Stream lines = CgroupUtil.readFilePrivileged(Paths.get(controller.path(), param))) { + try (Stream lines = Files.lines(Path.of(controller.path(), param))) { Optional result = lines.map(line -> line.split(" ")) .filter(line -> (line.length == 2 && diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java index 0a6d9958d11..d963c46f079 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2022, Red Hat Inc. + * 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 @@ -29,8 +30,8 @@ import java.io.UncheckedIOException; import java.lang.System.Logger; import java.lang.System.Logger.Level; +import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -140,7 +141,7 @@ public static Optional determineType(String mountInfo, String cgroups, String selfCgroup) throws IOException { final Map infos = new HashMap<>(); - List lines = CgroupUtil.readAllLinesPrivileged(Paths.get(cgroups)); + List lines = Files.readAllLines(Path.of(cgroups)); for (String line : lines) { if (line.startsWith("#")) { continue; @@ -180,7 +181,7 @@ public static Optional determineType(String mountInfo, // However, continuing in that case does not make sense as we'd need // information from mountinfo for the mounted controller paths which we wouldn't // find anyway in that case. - lines = CgroupUtil.readAllLinesPrivileged(Paths.get(mountInfo)); + lines = Files.readAllLines(Path.of(mountInfo)); boolean anyCgroupMounted = false; for (String line: lines) { boolean cgroupsControllerFound = amendCgroupInfos(line, infos, isCgroupsV2); @@ -196,8 +197,7 @@ public static Optional determineType(String mountInfo, // See: // setCgroupV1Path() for the action run for cgroups v1 systems // setCgroupV2Path() for the action run for cgroups v2 systems - try (Stream selfCgroupLines = - CgroupUtil.readFilePrivileged(Paths.get(selfCgroup))) { + try (Stream selfCgroupLines = Files.lines(Path.of(selfCgroup))) { Consumer action = (tokens -> setCgroupV1Path(infos, tokens)); if (isCgroupsV2) { action = (tokens -> setCgroupV2Path(infos, tokens)); @@ -311,7 +311,7 @@ private static boolean amendCgroupInfos(String mntInfoLine, String mountPath = lineMatcher.group(2); String fsType = lineMatcher.group(3); if (fsType.equals("cgroup")) { - Path p = Paths.get(mountPath); + Path p = Path.of(mountPath); String[] controllerNames = p.getFileName().toString().split(","); for (String controllerName: controllerNames) { switch (controllerName) { diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupUtil.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupUtil.java deleted file mode 100644 index dbe8a85b2b2..00000000000 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupUtil.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2020, Red Hat Inc. - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package jdk.internal.platform; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.List; -import java.util.stream.Stream; - -public final class CgroupUtil { - - @SuppressWarnings("removal") - public static Stream readFilePrivileged(Path path) throws IOException { - try { - PrivilegedExceptionAction> pea = () -> Files.lines(path); - return AccessController.doPrivileged(pea); - } catch (PrivilegedActionException e) { - unwrapIOExceptionAndRethrow(e); - throw new InternalError(e.getCause()); - } catch (UncheckedIOException e) { - throw e.getCause(); - } - } - - static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IOException { - Throwable x = pae.getCause(); - if (x instanceof IOException) - throw (IOException) x; - if (x instanceof RuntimeException) - throw (RuntimeException) x; - if (x instanceof Error) - throw (Error) x; - } - - static String readStringValue(CgroupSubsystemController controller, String param) throws IOException { - PrivilegedExceptionAction pea = () -> - Files.newBufferedReader(Paths.get(controller.path(), param)); - try (@SuppressWarnings("removal") BufferedReader bufferedReader = - AccessController.doPrivileged(pea)) { - String line = bufferedReader.readLine(); - return line; - } catch (PrivilegedActionException e) { - unwrapIOExceptionAndRethrow(e); - throw new InternalError(e.getCause()); - } catch (UncheckedIOException e) { - throw e.getCause(); - } - } - - @SuppressWarnings("removal") - public static List readAllLinesPrivileged(Path path) throws IOException { - try { - PrivilegedExceptionAction> pea = () -> Files.readAllLines(path); - return AccessController.doPrivileged(pea); - } catch (PrivilegedActionException e) { - unwrapIOExceptionAndRethrow(e); - throw new InternalError(e.getCause()); - } catch (UncheckedIOException e) { - throw e.getCause(); - } - } -} diff --git a/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java b/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java index ddb4d8e2718..aa618766b38 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2022, Red Hat Inc. + * 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 @@ -27,15 +28,16 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.file.Paths; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.internal.platform.CgroupInfo; import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; -import jdk.internal.platform.CgroupUtil; public class CgroupV2Subsystem implements CgroupSubsystem { @@ -328,10 +330,9 @@ public long getBlkIOServiced() { } private long sumTokensIOStat(Function mapFunc) { - try { - return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat")) - .map(mapFunc) - .collect(Collectors.summingLong(e -> e)); + try (Stream lines = Files.lines(Path.of(unified.path(), "io.stat"))) { + return lines.map(mapFunc) + .collect(Collectors.summingLong(e -> e)); } catch (UncheckedIOException | IOException e) { return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } diff --git a/src/java.base/share/classes/java/io/FilePermission.java b/src/java.base/share/classes/java/io/FilePermission.java index 30fa3978638..1330766b078 100644 --- a/src/java.base/share/classes/java/io/FilePermission.java +++ b/src/java.base/share/classes/java/io/FilePermission.java @@ -283,7 +283,6 @@ public FilePermission newPermUsingAltPath(FilePermission input) { * @param mask the actions mask to use. * */ - @SuppressWarnings("removal") private void init(int mask) { if ((mask & ALL) != mask) throw new IllegalArgumentException("invalid actions mask"); diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index e497e8faa6c..033d32611b0 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -549,7 +549,6 @@ public String(byte[] bytes, int offset, int length, Charset charset) { * Important: parameter order of this method is deliberately changed in order to * disambiguate it against other similar methods of this class. */ - @SuppressWarnings("removal") private String(Charset charset, byte[] bytes, int offset, int length) { if (length == 0) { this.value = "".value; @@ -787,7 +786,6 @@ static String newStringNoRepl(byte[] src, Charset cs) throws CharacterCodingExce } } - @SuppressWarnings("removal") private static String newStringNoRepl1(byte[] src, Charset cs) { int len = src.length; if (len == 0) { diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 11c77d48bf0..87aca3e1ffd 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -1379,7 +1379,6 @@ public static LoggerFinder getLoggerFinder() { private static volatile LoggerFinder service; - @SuppressWarnings("removal") static LoggerFinder accessProvider() { // We do not need to synchronize: LoggerFinderLoader will // always return the same instance, so if we don't have it, @@ -1483,7 +1482,6 @@ public static Logger getLogger(String name) { * * @since 9 */ - @SuppressWarnings("removal") @CallerSensitive public static Logger getLogger(String name, ResourceBundle bundle) { final ResourceBundle rb = Objects.requireNonNull(bundle); diff --git a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java index 8f934d6d67c..e03504a749b 100644 --- a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java +++ b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java @@ -578,7 +578,6 @@ S loadSpecies(S speciesData) { * @param speciesData what species we are generating * @return the generated concrete TopClass class */ - @SuppressWarnings("removal") Class generateConcreteSpeciesCode(String className, ClassSpecializer.SpeciesData speciesData) { byte[] classFile = generateConcreteSpeciesCodeFile(className, speciesData); var lookup = new MethodHandles.Lookup(topClass); diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index 96e0a7e729f..3b34453bc3f 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -103,7 +103,6 @@ final class ProxyGenerator { /** * debugging flag for saving generated class files */ - @SuppressWarnings("removal") private static final boolean SAVE_GENERATED_FILES = Boolean.getBoolean("jdk.proxy.ProxyGenerator.saveGeneratedFiles"); diff --git a/src/java.base/share/classes/java/net/URLConnection.java b/src/java.base/share/classes/java/net/URLConnection.java index 209b84de200..69f71942561 100644 --- a/src/java.base/share/classes/java/net/URLConnection.java +++ b/src/java.base/share/classes/java/net/URLConnection.java @@ -1388,7 +1388,6 @@ private ContentHandler lookupContentHandlerClassFor(String contentType) { return UnknownContentHandler.INSTANCE; } - @SuppressWarnings("removal") private ContentHandler lookupContentHandlerViaProvider(String contentType) { ClassLoader cl = ClassLoader.getSystemClassLoader(); diff --git a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template index 8b64b452b11..01282394b57 100644 --- a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template +++ b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template @@ -290,7 +290,6 @@ final class JceSecurity { } } - // This is called from within an doPrivileged block. private static void setupJurisdictionPolicies() throws Exception { // Sanity check the crypto.policy Security property. Single diff --git a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java index 02dc79f0288..44eaab0e83a 100644 --- a/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java +++ b/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java @@ -30,8 +30,6 @@ import java.io.File; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayDeque; import java.util.Deque; import java.util.function.BiFunction; @@ -323,18 +321,13 @@ boolean open() { return load(this, name, isBuiltin, throwExceptionIfFail()); } - @SuppressWarnings("removal") private boolean throwExceptionIfFail() { if (loadLibraryOnlyIfPresent) return true; // If the file exists but fails to load, UnsatisfiedLinkException thrown by the VM // will include the error message from dlopen to provide diagnostic information - return AccessController.doPrivileged(new PrivilegedAction<>() { - public Boolean run() { - File file = new File(name); - return file.exists(); - } - }); + File file = new File(name); + return file.exists(); } /* diff --git a/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java b/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java index 885e2e8c4d7..628d768d9ce 100644 --- a/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java +++ b/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java @@ -342,8 +342,6 @@ private static LoggerFinder accessLoggerFinder() { // no need to lock: it doesn't matter if we call // getLoggerFinder() twice - since LoggerFinder already caches // the result. - // This is just an optimization to avoid the cost of calling - // doPrivileged every time. prov = LoggerFinder.getLoggerFinder(); if (prov instanceof TemporaryLoggerFinder) return prov; provider = prov; diff --git a/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java b/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java index ca2fcf11e99..4f7811faf34 100644 --- a/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java +++ b/src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java @@ -28,10 +28,9 @@ import java.util.concurrent.atomic.AtomicInteger; /** - * A thread that has no permissions, is not a member of any user-defined + * A thread that is not a member of any user-defined * ThreadGroup and supports the ability to erase ThreadLocals. */ -@SuppressWarnings("removal") public final class InnocuousThread extends Thread { private static final jdk.internal.misc.Unsafe UNSAFE; private static final long THREAD_LOCALS; diff --git a/src/java.base/share/classes/jdk/internal/perf/Perf.java b/src/java.base/share/classes/jdk/internal/perf/Perf.java index e764fe79273..ada89dcf2e0 100644 --- a/src/java.base/share/classes/jdk/internal/perf/Perf.java +++ b/src/java.base/share/classes/jdk/internal/perf/Perf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,8 +25,6 @@ package jdk.internal.perf; import java.nio.ByteBuffer; -import java.security.Permission; -import java.security.PrivilegedAction; import java.io.IOException; import sun.nio.cs.UTF_8; @@ -49,7 +47,6 @@ * @author Brian Doherty * @since 1.4.2 * @see #getPerf - * @see jdk.internal.perf.Perf.GetPerfAction * @see java.nio.ByteBuffer */ public final class Perf { @@ -58,46 +55,6 @@ public final class Perf { private Perf() { } // prevent instantiation - /** - * The GetPerfAction class is a convenience class for acquiring access - * to the singleton Perf instance using the - * AccessController.doPrivileged() method. - *

      - * An instance of this class can be used as the argument to - * AccessController.doPrivileged(PrivilegedAction). - *

      Here is a suggested idiom for use of this class: - * - *

      {@code
      -     * class MyTrustedClass {
      -     *   private static final Perf perf =
      -     *       AccessController.doPrivileged(new Perf.GetPerfAction());
      -     *   ...
      -     * }
      -     * }
      - *

      - * In the presence of a security manager, the MyTrustedClass - * class in the above example will need to be granted the - * "sun.misc.Perf.getPerf" RuntimePermission - * permission in order to successfully acquire the singleton Perf instance. - *

      - * Please note that the "sun.misc.Perf.getPerf" permission - * is not a JDK specified permission. - * - * @see java.security.AccessController#doPrivileged(PrivilegedAction) - * @see java.lang.RuntimePermission - */ - public static class GetPerfAction implements PrivilegedAction - { - /** - * Run the Perf.getPerf() method in a privileged context. - * - * @see #getPerf - */ - public Perf run() { - return getPerf(); - } - } - /** * Return a reference to the singleton Perf instance. *

      @@ -106,11 +63,6 @@ public Perf run() { * for accessing the instrumentation buffer for this or another local * Java virtual machine. *

      - * If a security manager is installed, its checkPermission - * method is called with a RuntimePermission with a target - * of "sun.misc.Perf.getPerf". A security exception will result - * if the caller has not been granted this permission. - *

      * Access to the returned Perf object should be protected * by its caller and not passed on to untrusted code. This object can * be used to attach to the instrumentation buffer provided by this Java @@ -119,26 +71,12 @@ public Perf run() { * information. API's built on top of this interface may want to provide * finer grained access control to the contents of individual * instrumentation objects contained within the buffer. - *

      - * Please note that the "sun.misc.Perf.getPerf" permission - * is not a JDK specified permission. * * @return A reference to the singleton Perf instance. - * @throws SecurityException if a security manager exists and its - * checkPermission method doesn't allow access - * to the "jdk.internal.perf.Perf.getPerf"" target. - * @see java.lang.RuntimePermission * @see #attach */ public static Perf getPerf() { - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - Permission perm = new RuntimePermission("jdk.internal.perf.Perf.getPerf"); - security.checkPermission(perm); - } - return instance; } diff --git a/src/java.base/share/classes/jdk/internal/perf/PerfCounter.java b/src/java.base/share/classes/jdk/internal/perf/PerfCounter.java index b0a87c207b1..dfe67ce849c 100644 --- a/src/java.base/share/classes/jdk/internal/perf/PerfCounter.java +++ b/src/java.base/share/classes/jdk/internal/perf/PerfCounter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -28,7 +28,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.LongBuffer; -import java.security.AccessController; /** * Performance counter support for internal JRE classes. @@ -48,9 +47,7 @@ * */ public class PerfCounter { - @SuppressWarnings("removal") - private static final Perf perf = - AccessController.doPrivileged(new Perf.GetPerfAction()); + private static final Perf perf = Perf.getPerf(); // Must match values defined in hotspot/src/share/vm/runtime/perfdata.hpp private static final int V_Constant = 1; diff --git a/src/java.base/share/classes/jdk/internal/ref/Cleaner.java b/src/java.base/share/classes/jdk/internal/ref/Cleaner.java index 3a02605577b..ecd1c32167f 100644 --- a/src/java.base/share/classes/jdk/internal/ref/Cleaner.java +++ b/src/java.base/share/classes/jdk/internal/ref/Cleaner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -26,8 +26,6 @@ package jdk.internal.ref; import java.lang.ref.*; -import java.security.AccessController; -import java.security.PrivilegedAction; /** @@ -136,21 +134,17 @@ public static Cleaner create(Object ob, Runnable thunk) { /** * Runs this cleaner, if it has not been run before. */ - @SuppressWarnings("removal") public void clean() { if (!remove(this)) return; try { thunk.run(); } catch (final Throwable x) { - AccessController.doPrivileged(new PrivilegedAction<>() { - public Void run() { - if (System.err != null) - new Error("Cleaner terminated abnormally", x) - .printStackTrace(); - System.exit(1); - return null; - }}); + if (System.err != null) { + new Error("Cleaner terminated abnormally", x) + .printStackTrace(); + } + System.exit(1); } } } diff --git a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java index 687e32cdf61..20b390855c9 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java +++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java @@ -41,7 +41,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.security.PrivilegedAction; import java.util.Set; import jdk.internal.access.JavaLangReflectAccess; @@ -75,22 +74,6 @@ private ReflectionFactory() { this.langReflectAccess = SharedSecrets.getJavaLangReflectAccess(); } - /** - * A convenience class for acquiring the capability to instantiate - * reflective objects. Use this instead of a raw call to {@link - * #getReflectionFactory} in order to avoid being limited by the - * permissions of your callers. - * - *

      An instance of this class can be used as the argument of - * AccessController.doPrivileged. - */ - public static final class GetReflectionFactoryAction - implements PrivilegedAction { - public ReflectionFactory run() { - return getReflectionFactory(); - } - } - /** * Provides the caller with the capability to instantiate reflective * objects. diff --git a/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java b/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java index 4ec81e42c37..de7aa016ea3 100644 --- a/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java +++ b/src/java.base/share/classes/sun/net/www/http/KeepAliveStream.java @@ -154,7 +154,6 @@ public boolean hurry() { } } - @SuppressWarnings("removal") private static void queueForCleanup(KeepAliveCleanerEntry kace) { queue.lock(); try { diff --git a/src/java.management/share/classes/sun/management/VMManagementImpl.java b/src/java.management/share/classes/sun/management/VMManagementImpl.java index 4e2fc26c850..7e1e870acb4 100644 --- a/src/java.management/share/classes/sun/management/VMManagementImpl.java +++ b/src/java.management/share/classes/sun/management/VMManagementImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -35,8 +35,6 @@ import java.util.List; import java.util.Arrays; import java.util.Collections; -import java.security.AccessController; -import java.security.PrivilegedAction; /** * Implementation of VMManagement interface that accesses the management @@ -203,14 +201,7 @@ public synchronized List getVmArguments() { // Compilation Subsystem public String getCompilerName() { - @SuppressWarnings("removal") - String name = AccessController.doPrivileged( - new PrivilegedAction<>() { - public String run() { - return System.getProperty("sun.management.compiler"); - } - }); - return name; + return System.getProperty("sun.management.compiler"); } public native long getTotalCompileTime(); @@ -255,8 +246,7 @@ private synchronized PerfInstrumentation getPerfInstrumentation() { } // construct PerfInstrumentation object - @SuppressWarnings("removal") - Perf perf = AccessController.doPrivileged(new Perf.GetPerfAction()); + Perf perf = Perf.getPerf(); try { ByteBuffer bb = perf.attach(0); if (bb.capacity() == 0) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java index c0e70ca58e6..266a843083a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -26,14 +26,11 @@ import jdk.internal.vm.annotation.ForceInline; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Objects; /*non-public*/ class VectorIntrinsics { - @SuppressWarnings("removal") - static final int VECTOR_ACCESS_OOB_CHECK = AccessController.doPrivileged((PrivilegedAction) () -> - Integer.getInteger("jdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK", 2)); + static final int VECTOR_ACCESS_OOB_CHECK = + Integer.getInteger("jdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK", 2); @ForceInline static void requireLength(int haveLength, int length) { diff --git a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataBuffer.java b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataBuffer.java index 056671d7841..3c128c6b6b3 100644 --- a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataBuffer.java +++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/monitor/protocol/local/PerfDataBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -28,12 +28,8 @@ import jdk.internal.perf.Perf; import sun.jvmstat.monitor.*; import sun.jvmstat.perfdata.monitor.*; -import java.util.*; import java.io.*; import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.lang.reflect.Constructor; -import java.security.AccessController; /** * The concrete PerfDataBuffer implementation for the local: @@ -45,11 +41,8 @@ * @author Brian Doherty * @since 1.5 */ -// Suppreess unchecked conversion warning at line 34. -//@SuppressWarnings("unchecked") public class PerfDataBuffer extends AbstractPerfDataBuffer { - @SuppressWarnings("removal") - private static final Perf perf = AccessController.doPrivileged(new Perf.GetPerfAction()); + private static final Perf perf = Perf.getPerf(); /** * Create a PerfDataBuffer instance for accessing the specified From 994504c3e1440401a22ad3bdb30413f9db8a7780 Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Wed, 4 Dec 2024 09:32:33 +0000 Subject: [PATCH 114/171] 8329351: Add runtime/Monitor/TestRecursiveLocking.java for recursive Java monitor stress testing Co-authored-by: Daniel D. Daugherty Reviewed-by: dcubed, coleenp, aboldtch --- test/hotspot/jtreg/TEST.groups | 3 + ...tressWrapper_TestRecursiveLocking_36M.java | 207 +++++++ .../runtime/Monitor/TestRecursiveLocking.java | 568 ++++++++++++++++++ 3 files changed, 778 insertions(+) create mode 100644 test/hotspot/jtreg/runtime/Monitor/StressWrapper_TestRecursiveLocking_36M.java create mode 100644 test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index d68c015496c..4cb84e9f599 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -397,6 +397,8 @@ tier1_runtime = \ -runtime/Metaspace/FragmentMetaspace.java \ -runtime/Metaspace/FragmentMetaspaceSimple.java \ -runtime/MirrorFrame/Test8003720.java \ + -runtime/Monitor/StressWrapper_TestRecursiveLocking_36M.java \ + -runtime/Monitor/TestRecursiveLocking.java \ -runtime/modules/LoadUnloadModuleStress.java \ -runtime/modules/ModuleStress/ExportModuleStressTest.java \ -runtime/modules/ModuleStress/ModuleStressGC.java \ @@ -664,6 +666,7 @@ hotspot_tier2_runtime = \ -runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java \ -runtime/CompressedOops/UseCompressedOops.java \ -runtime/InvocationTests \ + -runtime/Monitor/StressWrapper_TestRecursiveLocking_36M.java \ -runtime/Thread/TestThreadDumpMonitorContention.java \ -:tier1_runtime \ -:hotspot_tier2_runtime_platform_agnostic \ diff --git a/test/hotspot/jtreg/runtime/Monitor/StressWrapper_TestRecursiveLocking_36M.java b/test/hotspot/jtreg/runtime/Monitor/StressWrapper_TestRecursiveLocking_36M.java new file mode 100644 index 00000000000..34bf5da64d9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Monitor/StressWrapper_TestRecursiveLocking_36M.java @@ -0,0 +1,207 @@ +/* + * 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 id=Xint_outer_inner + * @requires vm.flagless + * @summary Tests recursive locking in -Xint in outer then inner mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + */ + +/* + * @test id=Xint_alternate_AB + * @requires vm.flagless + * @summary Tests recursive locking in -Xint in alternate A and B mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + */ + +/* + * @test id=C1_outer_inner + * @requires vm.flagless + * @requires vm.compiler1.enabled + * @summary Tests recursive locking in C1 in outer then inner mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + */ + +/* + * @test id=C1_alternate_AB + * @requires vm.flagless + * @requires vm.compiler1.enabled + * @summary Tests recursive locking in C1 in alternate A and B mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + */ + +/* + * @test id=C2_outer_inner + * @requires vm.flagless + * @requires vm.compiler2.enabled + * @summary Tests recursive locking in C2 in outer then inner mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-EliminateNestedLocks + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-EliminateNestedLocks + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-EliminateNestedLocks + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 120 1 + */ + +/* + * @test id=C2_alternate_AB + * @requires vm.flagless + * @requires vm.compiler2.enabled + * @summary Tests recursive locking in C2 in alternate A and B mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:LockingMode=0 + * -XX:-EliminateNestedLocks + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:LockingMode=1 + * -XX:-EliminateNestedLocks + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + * + * @run main/othervm/timeout=240 -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:LockingMode=2 + * -XX:-EliminateNestedLocks + * -ms256m -mx256m + * TestRecursiveLocking 120 2 + */ diff --git a/test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java b/test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java new file mode 100644 index 00000000000..f9193b6eeb5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java @@ -0,0 +1,568 @@ +/* + * 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 id=Xint_outer_inner + * @requires vm.flagless + * @summary Tests recursive locking in -Xint in outer then inner mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + */ + +/* + * @test id=Xint_alternate_AB + * @requires vm.flagless + * @summary Tests recursive locking in -Xint in alternate A and B mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xint + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + */ + +/* + * @test id=C1_outer_inner + * @requires vm.flagless + * @requires vm.compiler1.enabled + * @summary Tests recursive locking in C1 in outer then inner mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + */ + +/* + * @test id=C1_alternate_AB + * @requires vm.flagless + * @requires vm.compiler1.enabled + * @summary Tests recursive locking in C1 in alternate A and B mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TieredStopAtLevel=1 + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + */ + +/* + * @test id=C2_outer_inner + * @requires vm.flagless + * @requires vm.compiler2.enabled + * @summary Tests recursive locking in C2 in outer then inner mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-EliminateNestedLocks + * -XX:LockingMode=0 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-EliminateNestedLocks + * -XX:LockingMode=1 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-EliminateNestedLocks + * -XX:LockingMode=2 + * -ms256m -mx256m + * TestRecursiveLocking 5 1 + */ + +/* + * @test id=C2_alternate_AB + * @requires vm.flagless + * @requires vm.compiler2.enabled + * @summary Tests recursive locking in C2 in alternate A and B mode. + * @library /testlibrary /test/lib + * @build jdk.test.whitebox.WhiteBox + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:LockingMode=0 + * -XX:-EliminateNestedLocks + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:LockingMode=1 + * -XX:-EliminateNestedLocks + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:LockingMode=2 + * -XX:-EliminateNestedLocks + * -ms256m -mx256m + * TestRecursiveLocking 5 2 + */ + +import jdk.test.lib.Asserts; +import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; + +public class TestRecursiveLocking { + static final WhiteBox WB = WhiteBox.getWhiteBox(); + static final int flagLockingMode = WB.getIntVMFlag("LockingMode").intValue(); + static final int constLockStackCapacity = WB.getLockStackCapacity(); + static final int LM_MONITOR = 0; + static final int LM_LEGACY = 1; + static final int LM_LIGHTWEIGHT = 2; + static final int def_mode = 2; + static final int def_n_secs = 30; + static final SyncThread syncThread = new SyncThread(); + + // This SynchronizedObject class and the OUTER followed by INNER testing + // model is adapted from runtime/lockStack/TestLockStackCapacity.java. + static class SynchronizedObject { + private int counter; + + synchronized void runInner(int depth, SynchronizedObject outer) { + counter++; + + // Legacy mode has no lock stack, i.e., there is no limit + // on recursion, so for legacy mode we can't say that + // "outer" must be inflated here, which we can say for all + // the other locking modes. + if (flagLockingMode != LM_LEGACY) { + outer.assertInflated(); + } + + // We haven't reached the stack lock capacity (recursion + // level), so we shouldn't be inflated here. Except for + // monitor mode, which is always inflated. + if (flagLockingMode != LM_MONITOR) { + assertNotInflated(); + } + if (depth == 1) { + return; + } else { + runInner(depth - 1, outer); + } + if (flagLockingMode != LM_MONITOR) { + assertNotInflated(); + } + } + + synchronized void runOuter(int depth, SynchronizedObject inner) { + counter++; + + if (flagLockingMode != LM_MONITOR) { + assertNotInflated(); + } + if (depth == 1) { + inner.runInner(constLockStackCapacity, this); + } else { + runOuter(depth - 1, inner); + } + if (flagLockingMode != LM_LEGACY) { + assertInflated(); + } + } + + // This test nests x recursive locks of INNER, in x recursive + // locks of OUTER. The number x is taken from the max number + // of elements in the lock stack. + public void runOuterInnerTest() { + final SynchronizedObject OUTER = new SynchronizedObject(); + final SynchronizedObject INNER = new SynchronizedObject(); + + // Just checking since they are new objects: + OUTER.assertNotInflated(); + INNER.assertNotInflated(); + + synchronized (OUTER) { + OUTER.counter++; + + if (flagLockingMode != LM_MONITOR) { + OUTER.assertNotInflated(); + } + INNER.assertNotInflated(); + OUTER.runOuter(constLockStackCapacity - 1, INNER); + + if (flagLockingMode != LM_LEGACY) { + OUTER.assertInflated(); + } + if (flagLockingMode != LM_MONITOR) { + INNER.assertNotInflated(); + } + } + + // Verify that the nested monitors have been properly released: + syncThread.verifyCanBeSynced(OUTER); + syncThread.verifyCanBeSynced(INNER); + + Asserts.assertEquals(OUTER.counter, constLockStackCapacity); + Asserts.assertEquals(INNER.counter, constLockStackCapacity); + } + + synchronized void runA(int depth, SynchronizedObject B) { + counter++; + + if (flagLockingMode == LM_LIGHTWEIGHT) { + // First time we lock A, A is the only one on the lock + // stack. + if (counter == 1) { + assertNotInflated(); + } else { + // Second time we want to lock A, the lock stack + // looks like this [A, B]. Lightweight locking + // doesn't allow interleaving ([A, B, A]), instead + // it inflates A and removes it from the lock + // stack. Which leaves us with only [B] on the + // lock stack. After more recursions it will grow + // to [B, B ... B]. + assertInflated(); + } + } else if (flagLockingMode == LM_MONITOR) { + assertInflated(); + } + + // Call runB() at the same depth as runA's depth: + B.runB(depth, this); + } + + synchronized void runB(int depth, SynchronizedObject A) { + counter++; + + if (flagLockingMode != LM_MONITOR) { + // Legacy tolerates endless recursions. While testing + // lightweight we don't go deeper than the size of the + // lock stack, which in this test case will be filled + // with a number of B-elements. See comment in runA() + // above for more info. + assertNotInflated(); + } else { + assertInflated(); + } + + if (depth == 1) { + // Reached LockStackCapacity in depth so we're done. + return; + } else { + A.runA(depth - 1, this); + } + } + + // This test alternates by locking A and B. + public void runAlternateABTest() { + final SynchronizedObject A = new SynchronizedObject(); + final SynchronizedObject B = new SynchronizedObject(); + + // Just checking since they are new objects: + A.assertNotInflated(); + B.assertNotInflated(); + + A.runA(constLockStackCapacity, B); + + // Verify that the nested monitors have been properly released: + syncThread.verifyCanBeSynced(A); + syncThread.verifyCanBeSynced(B); + + Asserts.assertEquals(A.counter, constLockStackCapacity); + Asserts.assertEquals(B.counter, constLockStackCapacity); + if (flagLockingMode == LM_LEGACY) { + A.assertNotInflated(); + } + // Implied else: for LM_MONITOR or LM_LIGHTWEIGHT it can be + // either inflated or not because A is not locked anymore + // and subject to deflation. + + if (flagLockingMode != LM_MONITOR) { + B.assertNotInflated(); + } + } + + void assertNotInflated() { + Asserts.assertFalse(WB.isMonitorInflated(this)); + } + + void assertInflated() { + Asserts.assertTrue(WB.isMonitorInflated(this)); + } + } + + static void usage() { + System.err.println(); + System.err.println("Usage: java TestRecursiveLocking [n_secs]"); + System.err.println(" java TestRecursiveLocking n_secs [mode]"); + System.err.println(); + System.err.println("where:"); + System.err.println(" n_secs ::= > 0"); + System.err.println(" Default n_secs is " + def_n_secs + "."); + System.err.println(" mode ::= 1 - outer and inner"); + System.err.println(" ::= 2 - alternate A and B"); + System.err.println(" Default mode is " + def_mode + "."); + System.exit(1); + } + + public static void main(String... argv) throws Exception { + int mode = def_mode; + int n_secs = def_n_secs; + + if (argv.length != 0 && argv.length != 1 && argv.length != 2) { + usage(); + } else if (argv.length > 0) { + try { + n_secs = Integer.parseInt(argv[0]); + if (n_secs <= 0) { + throw new NumberFormatException("Not > 0: '" + argv[0] + + "'"); + } + } catch (NumberFormatException nfe) { + System.err.println(); + System.err.println(nfe); + System.err.println("ERROR: '" + argv[0] + + "': invalid n_secs value."); + usage(); + } + + if (argv.length > 1) { + try { + mode = Integer.parseInt(argv[1]); + if (mode != 1 && mode != 2) { + throw new NumberFormatException("Not 1 -> 2: '" + + argv[1] + "'"); + } + } catch (NumberFormatException nfe) { + System.err.println(); + System.err.println(nfe); + System.err.println("ERROR: '" + argv[1] + + "': invalid mode value."); + usage(); + } + } + } + + System.out.println("INFO: LockingMode=" + flagLockingMode); + System.out.println("INFO: LockStackCapacity=" + constLockStackCapacity); + System.out.println("INFO: n_secs=" + n_secs); + System.out.println("INFO: mode=" + mode); + + long loopCount = 0; + long endTime = System.currentTimeMillis() + n_secs * 1000; + + syncThread.waitForStart(); + + while (System.currentTimeMillis() < endTime) { + loopCount++; + SynchronizedObject syncObj = new SynchronizedObject(); + switch (mode) { + case 1: + syncObj.runOuterInnerTest(); + break; + + case 2: + syncObj.runAlternateABTest(); + break; + + default: + throw new RuntimeException("bad mode parameter: " + mode); + } + } + + syncThread.setDone(); + try { + syncThread.join(); + } catch (InterruptedException ie) { + // This should not happen. + ie.printStackTrace(); + } + + System.out.println("INFO: main executed " + loopCount + " loops in " + + n_secs + " seconds."); + } +} + +class SyncThread extends Thread { + static final boolean verbose = false; // set to true for debugging + private boolean done = false; + private boolean haveWork = false; + private Object obj; + private Object waiter = new Object(); + + public void run() { + if (verbose) System.out.println("SyncThread: running."); + synchronized (waiter) { + // Let main know that we are running: + if (verbose) System.out.println("SyncThread: notify main running."); + waiter.notify(); + + while (!done) { + if (verbose) System.out.println("SyncThread: waiting."); + try { + waiter.wait(); + } catch (InterruptedException ie) { + // This should not happen. + ie.printStackTrace(); + } + if (haveWork) { + if (verbose) System.out.println("SyncThread: working."); + synchronized (obj) { + } + if (verbose) System.out.println("SyncThread: worked."); + haveWork = false; + waiter.notify(); + if (verbose) System.out.println("SyncThread: notified."); + } + else if (verbose) { + System.out.println("SyncThread: notified without work."); + } + } + } + if (verbose) System.out.println("SyncThread: exiting."); + } + + public void setDone() { + synchronized (waiter) { + if (verbose) System.out.println("main: set done."); + done = true; + waiter.notify(); + } + } + + public void verifyCanBeSynced(Object obj) { + synchronized (waiter) { + if (verbose) System.out.println("main: queueing up work."); + this.obj = obj; + haveWork = true; + if (verbose) System.out.println("main: notifying SyncThread."); + waiter.notify(); + if (verbose) System.out.println("main: waiting for SyncThread."); + while (haveWork) { + try { + waiter.wait(); + } catch (InterruptedException ie) { + // This should not happen. + ie.printStackTrace(); + } + } + if (verbose) System.out.println("main: waited for SyncThread."); + } + } + + public void waitForStart() { + synchronized (waiter) { + this.start(); + + // Wait for SyncThread to actually get running: + if (verbose) System.out.println("main: wait for SyncThread start."); + try { + waiter.wait(); + } catch (InterruptedException ie) { + // This should not happen. + ie.printStackTrace(); + } + if (verbose) System.out.println("main: waited for SyncThread start."); + } + } +} From 56d315da480dcd2198e2000ead301c3be8b27d84 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 4 Dec 2024 09:47:40 +0000 Subject: [PATCH 115/171] 8343540: Report preview error for inherited effectively-preview methods Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/comp/Check.java | 31 +- .../tools/javac/preview/PreviewTest.java | 435 +++++++++++++++++- 3 files changed, 449 insertions(+), 19 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 62c12334629..62f7c15a95f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4728,7 +4728,7 @@ else if (ownOuter.hasTag(CLASS) && site != ownOuter) { chk.checkDeprecated(tree.pos(), env.info.scope.owner, sym); chk.checkSunAPI(tree.pos(), sym); chk.checkProfile(tree.pos(), sym); - chk.checkPreview(tree.pos(), env.info.scope.owner, sym); + chk.checkPreview(tree.pos(), env.info.scope.owner, site, sym); } if (pt.isErroneous()) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 5a442bac302..08084b5abed 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -1845,7 +1845,8 @@ void checkOverride(JCTree tree, } if (shouldCheckPreview(m, other, origin)) { - checkPreview(tree.pos(), m, other); + checkPreview(TreeInfo.diagnosticPositionFor(m, tree), + m, origin.type, other); } Type mt = types.memberType(origin.type, m); @@ -1925,7 +1926,8 @@ else if (unhandledUnerased.nonEmpty()) { private boolean shouldCheckPreview(MethodSymbol m, MethodSymbol other, ClassSymbol origin) { if (m.owner != origin || //performance - only do the expensive checks when the overridden method is a Preview API: - (other.flags() & PREVIEW_API) == 0) { + ((other.flags() & PREVIEW_API) == 0 && + (other.owner.flags() & PREVIEW_API) == 0)) { return false; } @@ -3828,8 +3830,29 @@ void checkProfile(final DiagnosticPosition pos, final Symbol s) { } void checkPreview(DiagnosticPosition pos, Symbol other, Symbol s) { - if ((s.flags() & PREVIEW_API) != 0 && !preview.participatesInPreview(syms, other, s) && !disablePreviewCheck) { - if ((s.flags() & PREVIEW_REFLECTIVE) == 0) { + checkPreview(pos, other, Type.noType, s); + } + + void checkPreview(DiagnosticPosition pos, Symbol other, Type site, Symbol s) { + boolean sIsPreview; + Symbol previewSymbol; + if ((s.flags() & PREVIEW_API) != 0) { + sIsPreview = true; + previewSymbol= s; + } else if ((s.kind == Kind.MTH || s.kind == Kind.VAR) && + site.tsym != null && + (site.tsym.flags() & PREVIEW_API) == 0 && + (s.owner.flags() & PREVIEW_API) != 0) { + //calling a method, or using a field, whose owner is a preview, but + //using a site that is not a preview. Also produce an error or warning: + sIsPreview = true; + previewSymbol = s.owner; + } else { + sIsPreview = false; + previewSymbol = null; + } + if (sIsPreview && !preview.participatesInPreview(syms, other, s) && !disablePreviewCheck) { + if ((previewSymbol.flags() & PREVIEW_REFLECTIVE) == 0) { if (!preview.isEnabled()) { log.error(pos, Errors.IsPreview(s)); } else { diff --git a/test/langtools/tools/javac/preview/PreviewTest.java b/test/langtools/tools/javac/preview/PreviewTest.java index 24cb3d57dde..ad67ef8ac87 100644 --- a/test/langtools/tools/javac/preview/PreviewTest.java +++ b/test/langtools/tools/javac/preview/PreviewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8282823 + * @bug 8282823 8343540 * @library /tools/lib * @enablePreview * @modules @@ -229,13 +229,13 @@ public default void test() {} .getOutputLines(Task.OutputKind.DIRECT); List expected = - List.of("IUseIntf2P.java:3:8: compiler.err.is.preview: test()", - "IUseIntfDef2P.java:3:8: compiler.err.is.preview: test()", + List.of("IUseIntf2P.java:4:25: compiler.err.is.preview: test()", + "IUseIntfDef2P.java:4:25: compiler.err.is.preview: test()", "UseClass2P.java:4:17: compiler.err.is.preview: test()", - "UseIntf2P.java:3:8: compiler.err.is.preview: test()", - "UseIntfDef2P.java:3:8: compiler.err.is.preview: test()", + "UseIntf2P.java:4:17: compiler.err.is.preview: test()", + "UseIntfDef2P.java:4:17: compiler.err.is.preview: test()", "UseSubClass12P.java:3:17: compiler.err.is.preview: test()", - "UseSubIntfDef12P.java:2:8: compiler.err.is.preview: test()", + "UseSubIntfDef12P.java:3:17: compiler.err.is.preview: test()", "7 errors"); if (!log.equals(expected)) @@ -257,13 +257,13 @@ public default void test() {} .getOutputLines(Task.OutputKind.DIRECT); expected = - List.of("IUseIntf2P.java:3:8: compiler.warn.is.preview: test()", - "IUseIntfDef2P.java:3:8: compiler.warn.is.preview: test()", + List.of("IUseIntf2P.java:4:25: compiler.warn.is.preview: test()", + "IUseIntfDef2P.java:4:25: compiler.warn.is.preview: test()", "UseClass2P.java:4:17: compiler.warn.is.preview: test()", - "UseIntf2P.java:3:8: compiler.warn.is.preview: test()", - "UseIntfDef2P.java:3:8: compiler.warn.is.preview: test()", + "UseIntf2P.java:4:17: compiler.warn.is.preview: test()", + "UseIntfDef2P.java:4:17: compiler.warn.is.preview: test()", "UseSubClass12P.java:3:17: compiler.warn.is.preview: test()", - "UseSubIntfDef12P.java:2:8: compiler.warn.is.preview: test()", + "UseSubIntfDef12P.java:3:17: compiler.warn.is.preview: test()", "7 warnings"); if (!log.equals(expected)) @@ -392,7 +392,7 @@ public void test() {} .getOutputLines(Task.OutputKind.DIRECT); List expected = - List.of("AbstractP.java:3:17: compiler.err.is.preview: test()", + List.of("AbstractP.java:4:26: compiler.err.is.preview: test()", "ReabstractP.java:4:26: compiler.err.is.preview: test()", "2 errors"); @@ -415,7 +415,7 @@ public void test() {} .getOutputLines(Task.OutputKind.DIRECT); expected = - List.of("AbstractP.java:3:17: compiler.warn.is.preview: test()", + List.of("AbstractP.java:4:26: compiler.warn.is.preview: test()", "ReabstractP.java:4:26: compiler.warn.is.preview: test()", "2 warnings"); @@ -463,6 +463,413 @@ public void test() {} } } + @Test //JDK-8343540: + public void nonPreviewImplementsPreview(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public interface Preview { + public static final int FIELD = 0; + public default void test() {} + } + """, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST, + reflective=true) + public interface ReflectivePreview { + public default void test() {} + } + """, + """ + package preview.api; + public interface NonPreviewIntf extends Preview { + } + """, + """ + package preview.api; + public class NonPreview implements Preview { + } + """, + """ + package preview.api; + public class ReflectiveNonPreview implements ReflectivePreview { + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.NonPreviewIntf; + import preview.api.Preview; + import preview.api.ReflectiveNonPreview; + public class Test { + public void test(NonPreview np, + Produce prod) { + np.test(); + acceptRunnable(np::test); + accept(NonPreview::test); + prod.produce().test(); + acceptRunnable(prod.produce()::test); + int i = np.FIELD; + } + public void test(T1 t1, T2 t2, T3 t3) { + t1.test(); + t2.test(); + t3.test(); + } + public void test(ReflectiveNonPreview np) { + np.test(); + } + public void test(Preview p) { + p.test(); + acceptRunnable(p::test); + accept(Preview::test); + } + private static class ExtendsNonPreview extends NonPreview { + public void test() {} //error/warning here: + } + private static class ImplementsPreview implements Preview { + //no error/warning (already was on Preview after implements) + public void test() {} + } + private static class ImplicitReceiver extends NonPreview { + public void g() { + test(); //implicit this - error/warning + int i = FIELD; //implicit this - error/warning + } + } + private void acceptRunnable(Runnable r) {} + private void accept(Accept accept) {} + interface Accept { + public void accept(T t); + } + interface Produce { + public T produce(); + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:4:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:26:22: compiler.err.is.preview: preview.api.Preview", + "Test.java:34:55: compiler.err.is.preview: preview.api.Preview", + "Test.java:9:11: compiler.err.is.preview: test()", + "Test.java:10:24: compiler.err.is.preview: test()", + "Test.java:11:16: compiler.err.is.preview: test()", + "Test.java:12:23: compiler.err.is.preview: test()", + "Test.java:13:24: compiler.err.is.preview: test()", + "Test.java:14:19: compiler.err.is.preview: FIELD", + "Test.java:19:11: compiler.err.is.preview: test()", + "Test.java:20:11: compiler.err.is.preview: test()", + "Test.java:21:11: compiler.err.is.preview: test()", + "Test.java:24:11: compiler.warn.is.preview.reflective: test()", + "Test.java:29:16: compiler.err.is.preview: preview.api.Preview", + "Test.java:32:21: compiler.err.is.preview: test()", + "Test.java:36:21: compiler.err.is.preview: test()", + "Test.java:40:13: compiler.err.is.preview: test()", + "Test.java:41:21: compiler.err.is.preview: FIELD", + "17 errors", + "1 warning"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview2(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public interface Preview { + public default void test() {} + } + """, + """ + package preview.api; + public interface NonPreviewIntf extends Preview { + public default void test() {} + } + """, + """ + package preview.api; + public class NonPreview implements Preview { + public void test() {} + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.NonPreviewIntf; + public class Test { + public void test(NonPreview np1, + NonPreviewIntf np2) { + np1.test(); + np2.test(); + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview3(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public int field; + public static void test() {} + } + """, + """ + package preview.api; + public class NonPreview extends Preview { + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.Preview; + public class Test { + public void test(NonPreview np, Preview p) { + NonPreview.test(); + Preview.test(); + int i1 = np.field; + int i2 = p.field; + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:3:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:5:37: compiler.err.is.preview: preview.api.Preview", + "Test.java:6:19: compiler.err.is.preview: test()", + "Test.java:7:9: compiler.err.is.preview: preview.api.Preview", + "Test.java:8:20: compiler.err.is.preview: field", + "5 errors"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview4(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public int field; + public static void test() {} + } + """, + """ + package preview.api; + public class NonPreview extends Preview { + public int field; + public static void test() {} + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.Preview; + public class Test { + public void test(NonPreview np, Preview p) { + NonPreview.test(); + Preview.test(); + int i1 = np.field; + int i2 = p.field; + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:3:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:5:37: compiler.err.is.preview: preview.api.Preview", + "Test.java:7:9: compiler.err.is.preview: preview.api.Preview", + "3 errors"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview5(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public interface Preview { + public static final int CONST1 = 0; + public static final int CONST2 = 0; + } + """, + """ + package preview.api; + public interface NonPreviewIntf extends Preview { + public static final int CONST2 = 0; + } + """, + """ + package preview.api; + public class NonPreview implements Preview { + public static final int CONST2 = 0; + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.NonPreviewIntf; + import preview.api.Preview; + public class Test { + public void test() { + int i1 = NonPreview.CONST1; + int i2 = NonPreviewIntf.CONST1; + int i3 = Preview.CONST1; + int i4 = NonPreview.CONST2; + int i5 = NonPreviewIntf.CONST2; + int i6 = Preview.CONST2; + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:4:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:7:28: compiler.err.is.preview: CONST1", + "Test.java:8:32: compiler.err.is.preview: CONST1", + "Test.java:9:18: compiler.err.is.preview: preview.api.Preview", + "Test.java:12:18: compiler.err.is.preview: preview.api.Preview", + "5 errors"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + private int verifyPreviewClassfiles(Path directory) throws Exception { Path[] classfiles = tb.findFiles("class", directory); From 0c7451ae5abb90e59293cdcb5f2318e3bc5193a2 Mon Sep 17 00:00:00 2001 From: Aleksei Efimov Date: Wed, 4 Dec 2024 11:34:41 +0000 Subject: [PATCH 116/171] 8332686: InetAddress.ofLiteral can throw StringIndexOutOfBoundsException Reviewed-by: dfuchs, jpai --- .../share/classes/java/net/Inet6Address.java | 6 +++++- .../share/classes/java/net/InetAddress.java | 3 +++ test/jdk/java/net/InetAddress/OfLiteralTest.java | 15 +++++++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/net/Inet6Address.java b/src/java.base/share/classes/java/net/Inet6Address.java index ca73a6e1cc1..06a74ca3adc 100644 --- a/src/java.base/share/classes/java/net/Inet6Address.java +++ b/src/java.base/share/classes/java/net/Inet6Address.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -554,6 +554,10 @@ public static InetAddress ofLiteral(String ipv6AddressLiteral) { */ static InetAddress parseAddressString(String addressLiteral, boolean removeSqBrackets) throws UnknownHostException { + // Empty strings are not parseable + if (addressLiteral.isEmpty()) { + return null; + } // Remove trailing and leading square brackets if requested if (removeSqBrackets && addressLiteral.charAt(0) == '[' && addressLiteral.length() > 2 && diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index e94df5a9e0a..4bceebdcd0e 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -1618,6 +1618,9 @@ static InetAddress[] getAllByName0(String host) throws UnknownHostException { */ public static InetAddress ofLiteral(String ipAddressLiteral) { Objects.requireNonNull(ipAddressLiteral); + if (ipAddressLiteral.isEmpty()) { + throw IPAddressUtil.invalidIpAddressLiteral(ipAddressLiteral); + } InetAddress inetAddress; try { // First try to parse the input as an IPv4 address literal diff --git a/test/jdk/java/net/InetAddress/OfLiteralTest.java b/test/jdk/java/net/InetAddress/OfLiteralTest.java index 090523a9ee9..f9f6cdb500f 100644 --- a/test/jdk/java/net/InetAddress/OfLiteralTest.java +++ b/test/jdk/java/net/InetAddress/OfLiteralTest.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 8272215 8315767 + * @bug 8272215 8315767 8332686 * @summary Test for ofLiteral, ofPosixLiteral APIs in InetAddress classes * @run junit/othervm -Djdk.net.hosts.file=nonExistingHostsFile.txt * OfLiteralTest @@ -373,7 +373,18 @@ private static Stream invalidLiteralArguments() { Arguments.of(InetAddressClass.INET4_ADDRESS_POSIX, ""), // empty Arguments.of(InetAddressClass.INET4_ADDRESS_POSIX, "0x1FFFFFFFF"), // 2^33 - 1 is too large Arguments.of(InetAddressClass.INET4_ADDRESS_POSIX, "0x100000000"), // 2^32 is too large - Arguments.of(InetAddressClass.INET4_ADDRESS_POSIX, "040000000000") + Arguments.of(InetAddressClass.INET4_ADDRESS_POSIX, "040000000000"), + + // Empty literals + Arguments.of(InetAddressClass.INET_ADDRESS, ""), + Arguments.of(InetAddressClass.INET4_ADDRESS, ""), + Arguments.of(InetAddressClass.INET6_ADDRESS, ""), + + // Blank literals + Arguments.of(InetAddressClass.INET_ADDRESS, " "), + Arguments.of(InetAddressClass.INET4_ADDRESS, " "), + Arguments.of(InetAddressClass.INET6_ADDRESS, " "), + Arguments.of(InetAddressClass.INET4_ADDRESS_POSIX, " ") ); // Construct arguments for a test case with IPv6-scoped address with scope-id // specified as a string with non-existing network interface name From 4000e923e8b4472fe022f1fd78a1c42b2045683f Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 4 Dec 2024 11:36:23 +0000 Subject: [PATCH 117/171] 8343704: Bad GC parallelism with processing Cleaner queues Reviewed-by: bchristi, vklang, ogillespie, kdnilsen --- .../classes/jdk/internal/ref/CleanerImpl.java | 149 ++++++++++++++++-- .../jdk/internal/ref/PhantomCleanable.java | 79 +++------- .../ref/Cleaner/CleanableListTest.java | 136 ++++++++++++++++ .../internal/ref/CleanableListTestHelper.java | 66 ++++++++ .../jdk/internal/ref/TestCleanable.java | 39 +++++ .../bench/java/lang/ref/CleanerChurn.java | 58 +++++++ .../bench/java/lang/ref/CleanerGC.java | 68 ++++++++ 7 files changed, 525 insertions(+), 70 deletions(-) create mode 100644 test/jdk/jdk/internal/ref/Cleaner/CleanableListTest.java create mode 100644 test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/CleanableListTestHelper.java create mode 100644 test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/TestCleanable.java create mode 100644 test/micro/org/openjdk/bench/java/lang/ref/CleanerChurn.java create mode 100644 test/micro/org/openjdk/bench/java/lang/ref/CleanerGC.java diff --git a/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java b/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java index b4565daa466..bb7293e5d2b 100644 --- a/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java +++ b/src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java @@ -48,9 +48,9 @@ public final class CleanerImpl implements Runnable { private static Function cleanerImplAccess = null; /** - * Heads of a CleanableList for each reference type. + * Currently active PhantomCleanable-s. */ - final PhantomCleanable phantomCleanableList; + final CleanableList activeList; // The ReferenceQueue of pending cleaning actions final ReferenceQueue queue; @@ -82,7 +82,7 @@ static CleanerImpl getCleanerImpl(Cleaner cleaner) { */ public CleanerImpl() { queue = new ReferenceQueue<>(); - phantomCleanableList = new PhantomCleanableRef(); + activeList = new CleanableList(); } /** @@ -129,7 +129,7 @@ public void run() { InnocuousThread mlThread = (t instanceof InnocuousThread) ? (InnocuousThread) t : null; - while (!phantomCleanableList.isListEmpty()) { + while (!activeList.isEmpty()) { if (mlThread != null) { // Clear the thread locals mlThread.eraseThreadLocals(); @@ -165,14 +165,6 @@ public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) { this.action = action; } - /** - * Constructor used only for root of phantom cleanable list. - */ - PhantomCleanableRef() { - super(); - this.action = null; - } - @Override protected void performCleanup() { action.run(); @@ -231,4 +223,137 @@ protected void performCleanup() { // no action } } + + /** + * A specialized implementation that tracks phantom cleanables. + */ + static final class CleanableList { + /** + * Capacity for a single node in the list. + * This balances memory overheads vs locality vs GC walking costs. + */ + static final int NODE_CAPACITY = 4096; + + /** + * Head node. This is the only node where PhantomCleanables are + * added to or removed from. This is the only node with variable size, + * all other nodes linked from the head are always at full capacity. + */ + private Node head; + + /** + * Cached node instance to provide better behavior near NODE_CAPACITY + * threshold: if list size flips around NODE_CAPACITY, it would reuse + * the cached node instead of wasting and re-allocating a new node all + * the time. + */ + private Node cache; + + public CleanableList() { + reset(); + } + + /** + * Testing support: reset list to initial state. + */ + synchronized void reset() { + this.head = new Node(); + } + + /** + * Returns true if cleanable list is empty. + * + * @return true if the list is empty + */ + public synchronized boolean isEmpty() { + // Head node size is zero only when the entire list is empty. + return head.size == 0; + } + + /** + * Insert this PhantomCleanable in the list. + */ + public synchronized void insert(PhantomCleanable phc) { + if (head.size == NODE_CAPACITY) { + // Head node is full, insert new one. + // If possible, pick a pre-allocated node from cache. + Node newHead; + if (cache != null) { + newHead = cache; + cache = null; + } else { + newHead = new Node(); + } + newHead.next = head; + head = newHead; + } + assert head.size < NODE_CAPACITY; + + // Put the incoming object in head node and record indexes. + final int lastIndex = head.size; + phc.node = head; + phc.index = lastIndex; + head.arr[lastIndex] = phc; + head.size++; + } + + /** + * Remove this PhantomCleanable from the list. + * + * @return true if Cleanable was removed or false if not because + * it had already been removed before + */ + public synchronized boolean remove(PhantomCleanable phc) { + if (phc.node == null) { + // Not in the list. + return false; + } + assert phc.node.arr[phc.index] == phc; + + // Replace with another element from the head node, as long + // as it is not the same element. This keeps all non-head + // nodes at full capacity. + final int lastIndex = head.size - 1; + assert lastIndex >= 0; + if (head != phc.node || (phc.index != lastIndex)) { + PhantomCleanable mover = head.arr[lastIndex]; + mover.node = phc.node; + mover.index = phc.index; + phc.node.arr[phc.index] = mover; + } + + // Now we can unlink the removed element. + phc.node = null; + + // Remove the last element from the head node. + head.arr[lastIndex] = null; + head.size--; + + // If head node becomes empty after this, and there are + // nodes that follow it, replace the head node with another + // full one. If needed, stash the now free node in cache. + if (head.size == 0 && head.next != null) { + Node newHead = head.next; + if (cache == null) { + cache = head; + cache.next = null; + } + head = newHead; + } + + return true; + } + + /** + * Segment node. + */ + static class Node { + // Array of tracked cleanables, and the amount of elements in it. + final PhantomCleanable[] arr = new PhantomCleanable[NODE_CAPACITY]; + int size; + + // Linked list structure. + Node next; + } + } } diff --git a/src/java.base/share/classes/jdk/internal/ref/PhantomCleanable.java b/src/java.base/share/classes/jdk/internal/ref/PhantomCleanable.java index 24db6d8ef9a..3564a16d0a0 100644 --- a/src/java.base/share/classes/jdk/internal/ref/PhantomCleanable.java +++ b/src/java.base/share/classes/jdk/internal/ref/PhantomCleanable.java @@ -44,14 +44,21 @@ public abstract class PhantomCleanable extends PhantomReference implements Cleaner.Cleanable { /** - * Links to previous and next in a doubly-linked list. + * The list of PhantomCleanable; synchronizes insert and remove. */ - PhantomCleanable prev = this, next = this; + private final CleanerImpl.CleanableList list; /** - * The list of PhantomCleanable; synchronizes insert and remove. + * Node for this PhantomCleanable in the list. + * Synchronized by the same lock as the list itself. */ - private final PhantomCleanable list; + CleanerImpl.CleanableList.Node node; + + /** + * Index of this PhantomCleanable in the list node. + * Synchronized by the same lock as the list itself. + */ + int index; /** * Constructs new {@code PhantomCleanable} with @@ -62,73 +69,29 @@ public abstract class PhantomCleanable extends PhantomReference * @param referent the referent to track * @param cleaner the {@code Cleaner} to register with */ + @SuppressWarnings("this-escape") public PhantomCleanable(T referent, Cleaner cleaner) { super(Objects.requireNonNull(referent), CleanerImpl.getCleanerImpl(cleaner).queue); - this.list = CleanerImpl.getCleanerImpl(cleaner).phantomCleanableList; - insert(); + index = -1; + list = CleanerImpl.getCleanerImpl(cleaner).activeList; + list.insert(this); + + // Check that list insertion populated the backlinks. + assert node != null; + assert index >= 0; // Ensure referent and cleaner remain accessible Reference.reachabilityFence(referent); Reference.reachabilityFence(cleaner); } - /** - * Construct a new root of the list; not inserted. - */ - PhantomCleanable() { - super(null, null); - this.list = this; - } - - /** - * Insert this PhantomCleanable after the list head. - */ - private void insert() { - synchronized (list) { - prev = list; - next = list.next; - next.prev = this; - list.next = this; - } - } - - /** - * Remove this PhantomCleanable from the list. - * - * @return true if Cleanable was removed or false if not because - * it had already been removed before - */ - private boolean remove() { - synchronized (list) { - if (next != this) { - next.prev = prev; - prev.next = next; - prev = this; - next = this; - return true; - } - return false; - } - } - - /** - * Returns true if the list's next reference refers to itself. - * - * @return true if the list is empty - */ - boolean isListEmpty() { - synchronized (list) { - return list == list.next; - } - } - /** * Unregister this PhantomCleanable and invoke {@link #performCleanup()}, * ensuring at-most-once semantics. */ @Override public final void clean() { - if (remove()) { + if (list.remove(this)) { super.clear(); performCleanup(); } @@ -140,7 +103,7 @@ public final void clean() { */ @Override public void clear() { - if (remove()) { + if (list.remove(this)) { super.clear(); } } diff --git a/test/jdk/jdk/internal/ref/Cleaner/CleanableListTest.java b/test/jdk/jdk/internal/ref/Cleaner/CleanableListTest.java new file mode 100644 index 00000000000..eedd21d3f53 --- /dev/null +++ b/test/jdk/jdk/internal/ref/Cleaner/CleanableListTest.java @@ -0,0 +1,136 @@ +/* + * Copyright Amazon.com Inc. 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 8343704 + * @key randomness + * @library /test/lib + * @compile/module=java.base jdk/internal/ref/CleanableListTestHelper.java jdk/internal/ref/TestCleanable.java + * @modules java.base/jdk.internal.ref + * @run testng/othervm CleanableListTest + */ + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import java.util.Random; + +import jdk.internal.ref.CleanableListTestHelper; +import jdk.internal.ref.TestCleanable; +import jdk.test.lib.RandomFactory; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.Ignore; + +public class CleanableListTest { + + static final int SINGLE_NODE_CAPACITY = CleanableListTestHelper.NODE_CAPACITY; + static final int MULTI_NODE_CAPACITY = CleanableListTestHelper.NODE_CAPACITY * 4; + + static final Random RND = RandomFactory.getRandom(); + static final int RANDOM_ITERATIONS = 10_000_000; + + @Test + public void testSingle() { + CleanableListTestHelper list = new CleanableListTestHelper(); + Assert.assertTrue(list.isEmpty()); + TestCleanable tc = list.newCleanable(); + Assert.assertFalse(list.isEmpty()); + Assert.assertTrue(list.remove(tc)); + Assert.assertTrue(list.isEmpty()); + Assert.assertFalse(list.remove(tc)); + } + + @Test + public void testSequential_Single() { + doSequential(SINGLE_NODE_CAPACITY); + } + + @Test + public void testSequential_Multi() { + doSequential(MULTI_NODE_CAPACITY); + } + + private void doSequential(int size) { + CleanableListTestHelper list = new CleanableListTestHelper(); + Assert.assertTrue(list.isEmpty()); + + List tcs = new ArrayList<>(); + for (int c = 0; c < size; c++) { + tcs.add(list.newCleanable()); + } + Assert.assertFalse(list.isEmpty()); + + for (TestCleanable tc : tcs) { + Assert.assertTrue(list.remove(tc)); + } + Assert.assertTrue(list.isEmpty()); + } + + @Test + public void testRandom_Single() { + doRandom(SINGLE_NODE_CAPACITY); + } + + @Test + public void testRandom_Multi() { + doRandom(MULTI_NODE_CAPACITY); + } + + private void doRandom(int size) { + CleanableListTestHelper list = new CleanableListTestHelper(); + Assert.assertTrue(list.isEmpty()); + + BitSet bs = new BitSet(size); + + List tcs = new ArrayList<>(); + for (int c = 0; c < size; c++) { + tcs.add(list.newCleanable()); + bs.set(c, true); + } + Assert.assertFalse(list.isEmpty()); + + for (int t = 0; t < RANDOM_ITERATIONS; t++) { + int idx = RND.nextInt(size); + TestCleanable tc = tcs.get(idx); + if (bs.get(idx)) { + Assert.assertTrue(list.remove(tc)); + bs.set(idx, false); + } else { + Assert.assertFalse(list.remove(tc)); + list.insert(tc); + bs.set(idx, true); + } + } + + for (int c = 0; c < size; c++) { + if (bs.get(c)) { + TestCleanable tc = tcs.get(c); + Assert.assertTrue(list.remove(tc)); + } + } + } + +} diff --git a/test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/CleanableListTestHelper.java b/test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/CleanableListTestHelper.java new file mode 100644 index 00000000000..d3ee187a097 --- /dev/null +++ b/test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/CleanableListTestHelper.java @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com Inc. 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. + */ + +package jdk.internal.ref; + +import java.lang.ref.Cleaner; +import jdk.internal.ref.PhantomCleanable; +import jdk.internal.ref.CleanerImpl; +import jdk.internal.ref.CleanerImpl.CleanableList; + +/** + * This class provides package-private access to CleanableList internals. + */ +public class CleanableListTestHelper { + + public static final int NODE_CAPACITY = CleanableList.NODE_CAPACITY; + + final Cleaner cleaner; + final CleanableList list; + + public CleanableListTestHelper() { + cleaner = Cleaner.create(); + list = CleanerImpl.getCleanerImpl(cleaner).activeList; + + // List contains CleanerCleanable for Cleaner itself. + // For testing empty list paths, we want to drop it. + list.reset(); + } + + public TestCleanable newCleanable() { + return new TestCleanable(cleaner); + } + + public void insert(PhantomCleanable cl) { + list.insert(cl); + } + + public boolean remove(PhantomCleanable cl) { + return list.remove(cl); + } + + public boolean isEmpty() { + return list.isEmpty(); + } + +} diff --git a/test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/TestCleanable.java b/test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/TestCleanable.java new file mode 100644 index 00000000000..9e8da6f4830 --- /dev/null +++ b/test/jdk/jdk/internal/ref/Cleaner/java.base/jdk/internal/ref/TestCleanable.java @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com Inc. 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. + */ + +package jdk.internal.ref; + +import java.lang.ref.Cleaner; + +public class TestCleanable extends PhantomCleanable { + static final Object TARGET = new Object(); + + public TestCleanable(Cleaner cleaner) { + super(TARGET, cleaner); + } + + @Override + protected void performCleanup() { + // no action + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/ref/CleanerChurn.java b/test/micro/org/openjdk/bench/java/lang/ref/CleanerChurn.java new file mode 100644 index 00000000000..eab4c0f59d0 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ref/CleanerChurn.java @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package org.openjdk.bench.java.lang.ref; + +import java.lang.ref.Cleaner; +import java.lang.ref.Cleaner.Cleanable; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3, jvmArgs = {"-Xmx256m", "-Xms256m", "-XX:+AlwaysPreTouch"}) +public class CleanerChurn { + + @Param({"128", "256", "512", "1024", "2048"}) + int recipFreq; + + @Benchmark + public Object test() { + boolean register = ThreadLocalRandom.current().nextInt(recipFreq) == 0; + return new Target(register); + } + + static class Target { + private static final Cleaner CLEANER = Cleaner.create(); + public Target(boolean register) { + if (register) { + CLEANER.register(this, () -> {}); + } + } + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/ref/CleanerGC.java b/test/micro/org/openjdk/bench/java/lang/ref/CleanerGC.java new file mode 100644 index 00000000000..8d43f25c6c6 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ref/CleanerGC.java @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package org.openjdk.bench.java.lang.ref; + +import java.lang.ref.Cleaner; +import java.lang.ref.Cleaner.Cleanable; +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3, jvmArgs = {"-Xmx1g", "-Xms1g", "-XX:+AlwaysPreTouch"}) +public class CleanerGC { + + @Param({"16384", "65536", "262144", "1048576", "4194304"}) + int count; + + // Make sure all targets are reachable and available for GC in scalable manner. + // This exposes the potential GC problem in Cleaner lists. + ArrayList targets; + + @Setup + public void setup() { + targets = new ArrayList<>(); + for (int c = 0; c < count; c++) { + targets.add(new Target()); + } + } + + @Benchmark + public void test() { + System.gc(); + } + + static class Target { + private static final Cleaner CLEANER = Cleaner.create(); + public Target() { + CLEANER.register(this, () -> {}); + } + } + +} From e13206d3a16a67a604076faecded88cbed85db1a Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Wed, 4 Dec 2024 12:44:23 +0000 Subject: [PATCH 118/171] 8345065: Cleanup DomainCombiner, SubjectDomainCombiner, Subject, and PrivilegedAction specifications Reviewed-by: weijun --- .../share/classes/java/security/DomainCombiner.java | 5 +---- .../classes/java/security/PrivilegedAction.java | 8 ++++---- .../java/security/PrivilegedActionException.java | 8 ++++---- .../java/security/PrivilegedExceptionAction.java | 8 ++++---- .../share/classes/javax/security/auth/Subject.java | 4 ++-- .../javax/security/auth/SubjectDomainCombiner.java | 13 ++++--------- 6 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/java.base/share/classes/java/security/DomainCombiner.java b/src/java.base/share/classes/java/security/DomainCombiner.java index 704c6315bb1..1594a48783f 100644 --- a/src/java.base/share/classes/java/security/DomainCombiner.java +++ b/src/java.base/share/classes/java/security/DomainCombiner.java @@ -46,8 +46,7 @@ public interface DomainCombiner { * set of Permissions, for example). * * @param currentDomains the ProtectionDomains associated with the - * current execution thread, up to the most recent - * privileged {@code ProtectionDomain}. + * current execution thread. * The ProtectionDomains are listed in order of execution, * with the most recently executing {@code ProtectionDomain} * residing at the beginning of the array. This parameter may @@ -55,8 +54,6 @@ public interface DomainCombiner { * has no associated ProtectionDomains. * * @param assignedDomains an array of inherited ProtectionDomains. - * ProtectionDomains may be inherited from a parent thread, - * or from a privileged {@code AccessControlContext}. * This parameter may be {@code null} * if there are no inherited ProtectionDomains. * diff --git a/src/java.base/share/classes/java/security/PrivilegedAction.java b/src/java.base/share/classes/java/security/PrivilegedAction.java index 39db3492c01..56f7878bc6a 100644 --- a/src/java.base/share/classes/java/security/PrivilegedAction.java +++ b/src/java.base/share/classes/java/security/PrivilegedAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -27,8 +27,8 @@ /** - * A computation to be performed with privileges enabled. The computation is - * performed by invoking {@code AccessController.doPrivileged} on the + * A computation to be performed by invoking + * {@code AccessController.doPrivileged} on the * {@code PrivilegedAction} object. This interface is used only for * computations that do not throw checked exceptions; computations that * throw checked exceptions must use {@code PrivilegedExceptionAction} @@ -44,7 +44,7 @@ public interface PrivilegedAction { /** * Performs the computation. This method will be called by - * {@code AccessController.doPrivileged} after enabling privileges. + * {@code AccessController.doPrivileged}. * * @return a class-dependent value that may represent the results of the * computation. Each class that implements diff --git a/src/java.base/share/classes/java/security/PrivilegedActionException.java b/src/java.base/share/classes/java/security/PrivilegedActionException.java index 9c52d8739b8..1c1418f4267 100644 --- a/src/java.base/share/classes/java/security/PrivilegedActionException.java +++ b/src/java.base/share/classes/java/security/PrivilegedActionException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -63,7 +63,7 @@ public PrivilegedActionException(Exception exception) { } /** - * Returns the exception thrown by the privileged computation that + * Returns the exception thrown by the computation that * resulted in this {@code PrivilegedActionException}. * * @apiNote @@ -71,7 +71,7 @@ public PrivilegedActionException(Exception exception) { * The {@link Throwable#getCause()} method is now the preferred means of * obtaining this information. * - * @return the exception thrown by the privileged computation that + * @return the exception thrown by the computation that * resulted in this {@code PrivilegedActionException}. * @see PrivilegedExceptionAction * @see AccessController#doPrivileged(PrivilegedExceptionAction) @@ -90,7 +90,7 @@ public String toString() { /** - * The exception thrown by the privileged computation that resulted + * The exception thrown by the computation that resulted * in this {@code PrivilegedActionException}. * * @serialField exception Exception the thrown Exception diff --git a/src/java.base/share/classes/java/security/PrivilegedExceptionAction.java b/src/java.base/share/classes/java/security/PrivilegedExceptionAction.java index 00863ddf4a1..d875a2a4159 100644 --- a/src/java.base/share/classes/java/security/PrivilegedExceptionAction.java +++ b/src/java.base/share/classes/java/security/PrivilegedExceptionAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -27,8 +27,8 @@ /** - * A computation to be performed with privileges enabled, that throws one or - * more checked exceptions. The computation is performed by invoking + * A computation to be performed that throws one or more checked exceptions. + * The computation is performed by invoking * {@code AccessController.doPrivileged} on the * {@code PrivilegedExceptionAction} object. This interface is * used only for computations that throw checked exceptions; @@ -47,7 +47,7 @@ public interface PrivilegedExceptionAction { /** * Performs the computation. This method will be called by - * {@code AccessController.doPrivileged} after enabling privileges. + * {@code AccessController.doPrivileged}. * * @return a class-dependent value that may represent the results of the * computation. Each class that implements diff --git a/src/java.base/share/classes/javax/security/auth/Subject.java b/src/java.base/share/classes/javax/security/auth/Subject.java index 97ab672e1cc..ceb860e653a 100644 --- a/src/java.base/share/classes/javax/security/auth/Subject.java +++ b/src/java.base/share/classes/javax/security/auth/Subject.java @@ -453,7 +453,7 @@ public static T doAs(final Subject subject, } /** - * Perform privileged work as a particular {@code Subject}. + * Perform work as a particular {@code Subject}. * *

      This method launches {@code action} and binds {@code subject} to * the period of its execution. @@ -513,7 +513,7 @@ public static T doAsPrivileged(final Subject subject, } /** - * Perform privileged work as a particular {@code Subject}. + * Perform work as a particular {@code Subject}. * *

      This method launches {@code action} and binds {@code subject} to * the period of its execution. diff --git a/src/java.base/share/classes/javax/security/auth/SubjectDomainCombiner.java b/src/java.base/share/classes/javax/security/auth/SubjectDomainCombiner.java index bab2c5b9da9..643aea9e93c 100644 --- a/src/java.base/share/classes/javax/security/auth/SubjectDomainCombiner.java +++ b/src/java.base/share/classes/javax/security/auth/SubjectDomainCombiner.java @@ -112,21 +112,16 @@ public Subject getSubject() { * In addition, caching of ProtectionDomains may be permitted. * * @param currentDomains the ProtectionDomains associated with the - * current execution Thread, up to the most recent - * privileged {@code ProtectionDomain}. + * current execution Thread. * The ProtectionDomains are listed in order of execution, * with the most recently executing {@code ProtectionDomain} * residing at the beginning of the array. This parameter may * be {@code null} if the current execution Thread * has no associated ProtectionDomains. * - * @param assignedDomains the ProtectionDomains inherited from the - * parent Thread, or the ProtectionDomains from the - * privileged {@code context}, if a call to - * {@code AccessController.doPrivileged(..., context)} - * had occurred This parameter may be {@code null} - * if there were no ProtectionDomains inherited from the - * parent Thread, or from the privileged {@code context}. + * @param assignedDomains the inherited ProtectionDomains. + * This parameter may be {@code null} + * if there were no inherited ProtectionDomains. * * @return a new array consisting of the updated ProtectionDomains, * or {@code null}. From 6fa5cea984c31fbb74b39e1eec68daffea22ca92 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Wed, 4 Dec 2024 13:29:53 +0000 Subject: [PATCH 119/171] 8341982: Simplify JButton/bug4323121.java Reviewed-by: abhiscxk, honkar, dnguyen, achung --- test/jdk/javax/swing/JButton/bug4323121.java | 106 ++++++++----------- 1 file changed, 45 insertions(+), 61 deletions(-) diff --git a/test/jdk/javax/swing/JButton/bug4323121.java b/test/jdk/javax/swing/JButton/bug4323121.java index 0b352ce57eb..f23cfe43f6a 100644 --- a/test/jdk/javax/swing/JButton/bug4323121.java +++ b/test/jdk/javax/swing/JButton/bug4323121.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -24,59 +24,81 @@ /* * @test * @bug 4323121 - * @summary Tests whether any button that extends JButton always - returns true for isArmed() + * @summary Tests whether a button model always returns true for isArmed() + * when mouse hovers over the button * @key headful * @run main bug4323121 */ -import java.awt.Graphics; import java.awt.Point; import java.awt.Robot; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; +import java.util.concurrent.CountDownLatch; + import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.SwingUtilities; -public class bug4323121 { +import static java.util.concurrent.TimeUnit.SECONDS; + +public final class bug4323121 { static JFrame frame; - static testButton button; - static volatile Point pt; - static volatile int buttonW; - static volatile int buttonH; - static volatile boolean failed = false; + static JButton button; + + static volatile Point buttonCenter; + + private static final CountDownLatch mouseEntered = new CountDownLatch(1); + + // Usage of this flag is thread-safe because of using the mouseEntered latch + private static boolean modelArmed; public static void main(String[] args) throws Exception { Robot robot = new Robot(); robot.setAutoDelay(100); + try { SwingUtilities.invokeAndWait(() -> { + button = new JButton("gotcha"); + button.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + if (button.getModel().isArmed()) { + modelArmed = true; + } + mouseEntered.countDown(); + } + }); + frame = new JFrame("bug4323121"); - button = new testButton("gotcha"); frame.getContentPane().add(button); + frame.pack(); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); + robot.waitForIdle(); - robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { - pt = button.getLocationOnScreen(); - buttonW = button.getSize().width; - buttonH = button.getSize().height; + Point location = button.getLocationOnScreen(); + buttonCenter = new Point(location.x + button.getWidth() / 2, + location.y + button.getHeight() / 2); }); - robot.mouseMove(pt.x + buttonW / 2, pt.y + buttonH / 2); - robot.waitForIdle(); - if (failed) { - throw new RuntimeException("Any created button returns " + - "true for isArmed()"); + + robot.mouseMove(buttonCenter.x, buttonCenter.y); + + if (!mouseEntered.await(1, SECONDS)) { + throw new RuntimeException("Mouse entered event wasn't received"); + } + if (modelArmed) { + throw new RuntimeException("getModel().isArmed() returns true " + + "when mouse hovers over the button"); } } finally { - SwingUtilities.invokeAndWait(() -> { + SwingUtilities.invokeAndWait(() -> { if (frame != null) { frame.dispose(); } @@ -84,42 +106,4 @@ public static void main(String[] args) throws Exception { } } - static class testButton extends JButton implements MouseMotionListener, MouseListener { - public testButton(String label) { - super(label); - addMouseMotionListener(this); - addMouseListener(this); - } - - protected void paintComponent(Graphics g) { - super.paintComponent(g); - } - - protected void paintBorder(Graphics g) { - } - - public void mousePressed(MouseEvent e) { - } - - public void mouseDragged(MouseEvent e) { - } - - public void mouseMoved(MouseEvent e) { - } - - public void mouseReleased(MouseEvent e) { - } - - public void mouseEntered(MouseEvent e) { - if (getModel().isArmed()) { - failed = true; - } - } - - public void mouseExited(MouseEvent e) { - } - - public void mouseClicked(MouseEvent e) { - } - } } From 39cdadfb6ac8db7c84cf3259802d1f6ab26df8bf Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Wed, 4 Dec 2024 14:17:00 +0000 Subject: [PATCH 120/171] 8345186: Incorrect @throws doc for MemorySegment::getString Reviewed-by: mcimadamore, jvernee --- .../java/lang/foreign/MemorySegment.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index cd38bd19227..024dfacb71b 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1277,9 +1277,9 @@ MemorySegment reinterpret(long newSize, * @throws IllegalArgumentException if the size of the string is greater than the * largest string supported by the platform * @throws IndexOutOfBoundsException if {@code offset < 0} - * @throws IndexOutOfBoundsException if {@code offset > byteSize() - (B + 1)}, where - * {@code B} is the size, in bytes, of the string encoded using UTF-8 charset - * {@code str.getBytes(StandardCharsets.UTF_8).length}) + * @throws IndexOutOfBoundsException if no string terminator (e.g. {@code '\0'}) is + * present in this segment between the given {@code offset} and the end of + * this segment. * @throws IllegalStateException if the {@linkplain #scope() scope} associated with * this segment is not {@linkplain Scope#isAlive() alive} * @throws WrongThreadException if this method is called from a thread {@code T}, @@ -1315,14 +1315,11 @@ MemorySegment reinterpret(long newSize, * @throws IllegalArgumentException if the size of the string is greater than the * largest string supported by the platform * @throws IndexOutOfBoundsException if {@code offset < 0} - * @throws IndexOutOfBoundsException if {@code offset > byteSize() - (B + N)}, where: - *

        - *
      • {@code B} is the size, in bytes, of the string encoded using the - * provided charset (e.g. {@code str.getBytes(charset).length});
      • - *
      • {@code N} is the size (in bytes) of the terminator char according - * to the provided charset. For instance, this is 1 for - * {@link StandardCharsets#US_ASCII} and 2 for {@link StandardCharsets#UTF_16}.
      • - *
      + * @throws IndexOutOfBoundsException if no string terminator (e.g. {@code '\0'}) is + * present in this segment between the given {@code offset} and the end of + * this segment. The byte size of the string terminator depends on the + * selected {@code charset}. For instance, this is 1 for + * {@link StandardCharsets#US_ASCII} and 2 for {@link StandardCharsets#UTF_16} * @throws IllegalStateException if the {@linkplain #scope() scope} associated with * this segment is not {@linkplain Scope#isAlive() alive} * @throws WrongThreadException if this method is called from a thread {@code T}, From bd6d911cbe4b04221e52120cd0f8f04e219eca4d Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Wed, 4 Dec 2024 14:23:08 +0000 Subject: [PATCH 121/171] 8344177: Remove SecurityManager and related calls from java.management Reviewed-by: dfuchs --- .../DefaultMBeanServerInterceptor.java | 298 +----------------- .../ClassLoaderRepositorySupport.java | 19 +- .../DefaultMXBeanMappingFactory.java | 5 - .../jmx/mbeanserver/GetPropertyAction.java | 46 --- .../com/sun/jmx/mbeanserver/Introspector.java | 19 +- .../sun/jmx/mbeanserver/JmxMBeanServer.java | 102 +----- .../jmx/mbeanserver/MBeanInstantiator.java | 62 +--- .../jmx/mbeanserver/MBeanIntrospector.java | 4 +- .../com/sun/jmx/mbeanserver/MBeanSupport.java | 4 +- .../com/sun/jmx/mbeanserver/MXBeanLookup.java | 7 +- .../ObjectInputStreamWithLoader.java | 4 +- .../com/sun/jmx/mbeanserver/PerInterface.java | 13 +- .../internal/ArrayNotificationBuffer.java | 79 +---- .../remote/internal/ClientNotifForwarder.java | 47 +-- .../remote/internal/ServerNotifForwarder.java | 87 +---- .../jmx/remote/security/FileLoginModule.java | 33 +- .../security/HashedPasswordManager.java | 11 +- .../security/JMXPluggableAuthenticator.java | 40 +-- .../security/JMXSubjectDomainCombiner.java | 107 ------- .../security/MBeanServerAccessController.java | 76 ++--- .../com/sun/jmx/remote/util/EnvHelp.java | 11 +- .../jmx/remote/util/OrderClassLoaders.java | 5 +- .../lang/management/ManagementFactory.java | 76 ++--- .../java/lang/management/RuntimeMXBean.java | 6 +- .../management/ClassAttributeValueExp.java | 4 - .../share/classes/javax/management/JMX.java | 3 +- .../javax/management/MBeanAttributeInfo.java | 2 - .../classes/javax/management/MBeanInfo.java | 31 +- .../classes/javax/management/MBeanServer.java | 7 - .../javax/management/MBeanServerFactory.java | 65 +--- .../javax/management/Notification.java | 4 - .../javax/management/NumericValueExp.java | 5 - .../classes/javax/management/ObjectName.java | 2 - .../javax/management/StandardMBean.java | 18 +- .../modelmbean/DescriptorSupport.java | 5 - .../InvalidTargetObjectTypeException.java | 3 - .../modelmbean/ModelMBeanAttributeInfo.java | 2 - .../modelmbean/ModelMBeanConstructorInfo.java | 2 - .../modelmbean/ModelMBeanInfoSupport.java | 2 - .../ModelMBeanNotificationInfo.java | 2 - .../modelmbean/ModelMBeanOperationInfo.java | 2 - .../modelmbean/RequiredModelMBean.java | 160 +++------- .../modelmbean/XMLParseException.java | 3 - .../javax/management/monitor/Monitor.java | 35 +- .../OpenMBeanAttributeInfoSupport.java | 9 - .../javax/management/openmbean/OpenType.java | 25 +- .../openmbean/TabularDataSupport.java | 7 +- .../MBeanServerNotificationFilter.java | 2 - .../relation/RelationNotification.java | 3 - .../relation/RelationTypeSupport.java | 3 - .../javax/management/relation/Role.java | 3 - .../javax/management/relation/RoleInfo.java | 4 - .../javax/management/relation/RoleResult.java | 5 - .../management/relation/RoleUnresolved.java | 3 - .../remote/JMXConnectorFactory.java | 31 +- .../sun/management/ClassLoadingImpl.java | 4 +- .../management/ManagementFactoryHelper.java | 76 ++--- .../sun/management/MappedMXBeanType.java | 22 +- .../classes/sun/management/MemoryImpl.java | 4 +- .../sun/management/MemoryPoolImpl.java | 8 +- .../classes/sun/management/RuntimeImpl.java | 3 +- .../classes/sun/management/ThreadImpl.java | 13 - .../share/classes/sun/management/Util.java | 31 +- .../sun/management/VMManagementImpl.java | 2 +- 64 files changed, 213 insertions(+), 1563 deletions(-) delete mode 100644 src/java.management/share/classes/com/sun/jmx/mbeanserver/GetPropertyAction.java delete mode 100644 src/java.management/share/classes/com/sun/jmx/remote/security/JMXSubjectDomainCombiner.java diff --git a/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index e5aa863f9a6..555dfdc918b 100644 --- a/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -39,10 +39,6 @@ import com.sun.jmx.remote.util.EnvHelp; import java.lang.ref.WeakReference; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Permission; -import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashSet; @@ -64,13 +60,11 @@ import javax.management.ListenerNotFoundException; import javax.management.MBeanException; import javax.management.MBeanInfo; -import javax.management.MBeanPermission; import javax.management.MBeanRegistration; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; import javax.management.MBeanServerNotification; -import javax.management.MBeanTrustPermission; import javax.management.NotCompliantMBeanException; import javax.management.Notification; import javax.management.NotificationBroadcaster; @@ -248,9 +242,6 @@ private ObjectInstance createMBean(String className, ObjectName name, name = nonDefaultDomain(name); } - checkMBeanPermission(className, null, null, "instantiate"); - checkMBeanPermission(className, null, name, "registerMBean"); - /* Load the appropriate class. */ if (withDefaultLoaderRepository) { if (MBEANSERVER_LOGGER.isLoggable(Level.TRACE)) { @@ -281,8 +272,6 @@ private ObjectInstance createMBean(String className, ObjectName name, theClass = instantiator.findClass(className, loaderName); } - checkMBeanTrustPermission(theClass); - // Check that the MBean can be instantiated by the MBeanServer. Introspector.testCreation(theClass); @@ -309,9 +298,6 @@ public ObjectInstance registerMBean(Object object, ObjectName name) final String infoClassName = getNewMBeanClassName(object); - checkMBeanPermission(infoClassName, null, name, "registerMBean"); - checkMBeanTrustPermission(theClass); - return registerObject(infoClassName, object, name); } @@ -417,8 +403,6 @@ private void exclusiveUnregisterMBean(ObjectName name) DynamicMBean instance = getMBean(name); // may throw InstanceNotFoundException - checkMBeanPermission(instance, null, name, "unregisterMBean"); - if (instance instanceof MBeanRegistration) preDeregisterInvoke((MBeanRegistration) instance); @@ -451,47 +435,13 @@ public ObjectInstance getObjectInstance(ObjectName name) name = nonDefaultDomain(name); DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "getObjectInstance"); - final String className = getClassName(instance); return new ObjectInstance(name, className); } public Set queryMBeans(ObjectName name, QueryExp query) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - // Check if the caller has the right to invoke 'queryMBeans' - // - checkMBeanPermission((String) null, null, null, "queryMBeans"); - - // Perform query without "query". - // - Set list = queryMBeansImpl(name, null); - - // Check if the caller has the right to invoke 'queryMBeans' - // on each specific classname/objectname in the list. - // - Set allowedList = new HashSet<>(list.size()); - for (ObjectInstance oi : list) { - try { - checkMBeanPermission(oi.getClassName(), null, - oi.getObjectName(), "queryMBeans"); - allowedList.add(oi); - } catch (SecurityException e) { - // OK: Do not add this ObjectInstance to the list - } - } - - // Apply query to allowed MBeans only. - // - return filterListOfObjectInstances(allowedList, query); - } else { - // Perform query. - // - return queryMBeansImpl(name, query); - } + return queryMBeansImpl(name, query); } private Set queryMBeansImpl(ObjectName name, @@ -504,46 +454,7 @@ private Set queryMBeansImpl(ObjectName name, } public Set queryNames(ObjectName name, QueryExp query) { - Set queryList; - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - // Check if the caller has the right to invoke 'queryNames' - // - checkMBeanPermission((String) null, null, null, "queryNames"); - - // Perform query without "query". - // - Set list = queryMBeansImpl(name, null); - - // Check if the caller has the right to invoke 'queryNames' - // on each specific classname/objectname in the list. - // - Set allowedList = new HashSet<>(list.size()); - for (ObjectInstance oi : list) { - try { - checkMBeanPermission(oi.getClassName(), null, - oi.getObjectName(), "queryNames"); - allowedList.add(oi); - } catch (SecurityException e) { - // OK: Do not add this ObjectInstance to the list - } - } - - // Apply query to allowed MBeans only. - // - Set queryObjectInstanceList = - filterListOfObjectInstances(allowedList, query); - queryList = new HashSet<>(queryObjectInstanceList.size()); - for (ObjectInstance oi : queryObjectInstanceList) { - queryList.add(oi.getObjectName()); - } - } else { - // Perform query. - // - queryList = queryNamesImpl(name, query); - } - return queryList; + return queryNamesImpl(name, query); } private Set queryNamesImpl(ObjectName name, QueryExp query) { @@ -562,45 +473,11 @@ public boolean isRegistered(ObjectName name) { } name = nonDefaultDomain(name); - - /* No Permission check */ - // isRegistered is always unchecked as per JMX spec. - return (repository.contains(name)); } public String[] getDomains() { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - // Check if the caller has the right to invoke 'getDomains' - // - checkMBeanPermission((String) null, null, null, "getDomains"); - - // Return domains - // - String[] domains = repository.getDomains(); - - // Check if the caller has the right to invoke 'getDomains' - // on each specific domain in the list. - // - List result = new ArrayList<>(domains.length); - for (int i = 0; i < domains.length; i++) { - try { - ObjectName dom = Util.newObjectName(domains[i] + ":x=x"); - checkMBeanPermission((String) null, null, dom, "getDomains"); - result.add(domains[i]); - } catch (SecurityException e) { - // OK: Do not add this domain to the list - } - } - - // Make an array from result. - // - return result.toArray(new String[result.size()]); - } else { - return repository.getDomains(); - } + return repository.getDomains(); } public Integer getMBeanCount() { @@ -630,7 +507,6 @@ public Object getAttribute(ObjectName name, String attribute) } final DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, attribute, name, "getAttribute"); try { return instance.getAttribute(attribute); @@ -664,33 +540,7 @@ public AttributeList getAttributes(ObjectName name, String[] attributes) } final DynamicMBean instance = getMBean(name); - final String[] allowedAttributes; - @SuppressWarnings("removal") - final SecurityManager sm = System.getSecurityManager(); - if (sm == null) - allowedAttributes = attributes; - else { - final String classname = getClassName(instance); - - // Check if the caller has the right to invoke 'getAttribute' - // - checkMBeanPermission(classname, null, name, "getAttribute"); - - // Check if the caller has the right to invoke 'getAttribute' - // on each specific attribute - // - List allowedList = new ArrayList<>(attributes.length); - for (String attr : attributes) { - try { - checkMBeanPermission(classname, attr, name, "getAttribute"); - allowedList.add(attr); - } catch (SecurityException e) { - // OK: Do not add this attribute to the list - } - } - allowedAttributes = - allowedList.toArray(new String[allowedList.size()]); - } + final String[] allowedAttributes = attributes; try { return instance.getAttributes(allowedAttributes); @@ -725,7 +575,6 @@ public void setAttribute(ObjectName name, Attribute attribute) } DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, attribute.getName(), name, "setAttribute"); try { instance.setAttribute(attribute); @@ -756,32 +605,8 @@ public AttributeList setAttributes(ObjectName name, name = nonDefaultDomain(name); final DynamicMBean instance = getMBean(name); - final AttributeList allowedAttributes; - @SuppressWarnings("removal") - final SecurityManager sm = System.getSecurityManager(); - if (sm == null) - allowedAttributes = attributes; - else { - String classname = getClassName(instance); - - // Check if the caller has the right to invoke 'setAttribute' - // - checkMBeanPermission(classname, null, name, "setAttribute"); + final AttributeList allowedAttributes = attributes; - // Check if the caller has the right to invoke 'setAttribute' - // on each specific attribute - // - allowedAttributes = new AttributeList(attributes.size()); - for (Attribute attribute : attributes.asList()) { - try { - checkMBeanPermission(classname, attribute.getName(), - name, "setAttribute"); - allowedAttributes.add(attribute); - } catch (SecurityException e) { - // OK: Do not add this attribute to the list - } - } - } try { return instance.setAttributes(allowedAttributes); } catch (Throwable t) { @@ -798,7 +623,6 @@ public Object invoke(ObjectName name, String operationName, name = nonDefaultDomain(name); DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, operationName, name, "invoke"); try { return instance.invoke(operationName, params, signature); } catch (Throwable t) { @@ -922,8 +746,6 @@ private ObjectInstance registerDynamicMBean(String classname, ObjectName.getInstance(nonDefaultDomain(logicalName)); } - checkMBeanPermission(classname, null, logicalName, "registerMBean"); - if (logicalName == null) { final RuntimeException wrapped = new IllegalArgumentException("No object name specified"); @@ -1159,7 +981,6 @@ public void addNotificationListener(ObjectName name, } DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "addNotificationListener"); NotificationBroadcaster broadcaster = getNotificationBroadcaster(name, instance, @@ -1288,7 +1109,6 @@ private void removeNotificationListener(ObjectName name, } DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "removeNotificationListener"); /* We could simplify the code by assigning broadcaster after assigning listenerWrapper, but that would change the error @@ -1356,8 +1176,6 @@ public MBeanInfo getMBeanInfo(ObjectName name) throw new JMRuntimeException("MBean " + name + "has no MBeanInfo"); - checkMBeanPermission(mbi.getClassName(), null, name, "getMBeanInfo"); - return mbi; } @@ -1365,7 +1183,6 @@ public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { final DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "isInstanceOf"); try { Object resource = getResource(instance); @@ -1407,7 +1224,6 @@ public ClassLoader getClassLoaderFor(ObjectName mbeanName) throws InstanceNotFoundException { DynamicMBean instance = getMBean(mbeanName); - checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor"); return getResource(instance).getClass().getClassLoader(); } @@ -1422,12 +1238,10 @@ public ClassLoader getClassLoader(ObjectName loaderName) throws InstanceNotFoundException { if (loaderName == null) { - checkMBeanPermission((String) null, null, null, "getClassLoader"); return server.getClass().getClassLoader(); } DynamicMBean instance = getMBean(loaderName); - checkMBeanPermission(instance, null, loaderName, "getClassLoader"); Object resource = getResource(instance); @@ -1565,48 +1379,6 @@ private static String safeGetClassName(DynamicMBean mbean) { } } - /** - * Applies the specified queries to the set of ObjectInstances. - */ - private Set - filterListOfObjectInstances(Set list, - QueryExp query) { - // Null query. - // - if (query == null) { - return list; - } else { - Set result = new HashSet<>(); - // Access the filter. - // - for (ObjectInstance oi : list) { - boolean res = false; - MBeanServer oldServer = QueryEval.getMBeanServer(); - query.setMBeanServer(server); - try { - res = query.apply(oi.getObjectName()); - } catch (Exception e) { - res = false; - } finally { - /* - * query.setMBeanServer is probably - * QueryEval.setMBeanServer so put back the old - * value. Since that method uses a ThreadLocal - * variable, this code is only needed for the - * unusual case where the user creates a custom - * QueryExp that calls a nested query on another - * MBeanServer. - */ - query.setMBeanServer(oldServer); - } - if (res) { - result.add(oi); - } - } - return result; - } - } - /* * Get the existing wrapper for this listener, name, and mbean, if * there is one. Otherwise, if "create" is true, create and @@ -1749,56 +1521,6 @@ private static String getClassName(DynamicMBean mbean) { return mbean.getMBeanInfo().getClassName(); } - private static void checkMBeanPermission(DynamicMBean mbean, - String member, - ObjectName objectName, - String actions) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - checkMBeanPermission(safeGetClassName(mbean), - member, - objectName, - actions); - } - } - - private static void checkMBeanPermission(String classname, - String member, - ObjectName objectName, - String actions) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanPermission(classname, - member, - objectName, - actions); - sm.checkPermission(perm); - } - } - - private static void checkMBeanTrustPermission(final Class theClass) - throws SecurityException { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanTrustPermission("register"); - PrivilegedAction act = - new PrivilegedAction<>() { - public ProtectionDomain run() { - return theClass.getProtectionDomain(); - } - }; - @SuppressWarnings("removal") - ProtectionDomain pd = AccessController.doPrivileged(act); - @SuppressWarnings("removal") - AccessControlContext acc = - new AccessControlContext(new ProtectionDomain[] { pd }); - sm.checkPermission(perm, acc); - } - } - // ------------------------------------------------------------------ // // Dealing with registration of special MBeans in the repository. @@ -2006,13 +1728,7 @@ private ResourceContext makeResourceContextFor(Object resource, return ResourceContext.NONE; } - @SuppressWarnings("removal") private ModifiableClassLoaderRepository getInstantiatorCLR() { - return AccessController.doPrivileged(new PrivilegedAction<>() { - @Override - public ModifiableClassLoaderRepository run() { - return instantiator != null ? instantiator.getClassLoaderRepository() : null; - } - }); + return instantiator != null ? instantiator.getClassLoaderRepository() : null; } } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/ClassLoaderRepositorySupport.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/ClassLoaderRepositorySupport.java index a73e46e6020..78dc5ce2ea1 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/ClassLoaderRepositorySupport.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/ClassLoaderRepositorySupport.java @@ -27,18 +27,15 @@ import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; -import java.security.Permission; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.lang.System.Logger.Level; -import javax.management.MBeanPermission; import javax.management.ObjectName; import javax.management.loading.PrivateClassLoader; -import sun.reflect.misc.ReflectUtil; /** * This class keeps the list of Class Loaders registered in the MBean Server. @@ -192,7 +189,6 @@ private Class loadClass(final LoaderEntry list[], final ClassLoader without, final ClassLoader stop) throws ClassNotFoundException { - ReflectUtil.checkPackageAccess(className); final int size = list.length; for(int i=0; iA converter between Java types and the limited set of classes @@ -301,7 +300,6 @@ recognizes the Type (Chain of Responsibility pattern). */ private static > MXBeanMapping makeEnumMapping(Class enumClass, Class fake) { - ReflectUtil.checkPackageAccess(enumClass); return new EnumMapping<>(Util.>cast(enumClass)); } @@ -426,7 +424,6 @@ private MXBeanMapping makeCompositeMapping(Class c, (c.getName().equals("com.sun.management.GcInfo") && c.getClassLoader() == null); - ReflectUtil.checkPackageAccess(c); final List methods = MBeanAnalyzer.eliminateCovariantMethods(Arrays.asList(c.getMethods())); final SortedMap getterMap = newSortedMap(); @@ -1119,7 +1116,6 @@ Object fromCompositeData(CompositeData cd, Object o; try { final Class targetClass = getTargetClass(); - ReflectUtil.checkPackageAccess(targetClass); @SuppressWarnings("deprecation") Object tmp = targetClass.newInstance(); o = tmp; @@ -1375,7 +1371,6 @@ Object fromCompositeData(CompositeData cd, } try { - ReflectUtil.checkPackageAccess(max.constructor.getDeclaringClass()); return max.constructor.newInstance(params); } catch (Exception e) { final String msg = diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/GetPropertyAction.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/GetPropertyAction.java deleted file mode 100644 index 2628c4ac2a3..00000000000 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/GetPropertyAction.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2002, 2004, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package com.sun.jmx.mbeanserver; - -import java.security.PrivilegedAction; - -/** - * Utility class to be used by the method {@code AccessControler.doPrivileged} - * to get a system property. - * - * @since 1.5 - */ -public class GetPropertyAction implements PrivilegedAction { - private final String key; - - public GetPropertyAction(String key) { - this.key = key; - } - - public String run() { - return System.getProperty(key); - } -} diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java index ea1d685b5fc..374dd57a4ee 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -52,12 +52,10 @@ import com.sun.jmx.remote.util.EnvHelp; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; -import java.security.AccessController; import javax.management.AttributeNotFoundException; import javax.management.openmbean.CompositeData; import sun.reflect.misc.MethodUtil; -import sun.reflect.misc.ReflectUtil; /** * This class contains the methods for performing all the tests needed to verify @@ -66,12 +64,7 @@ * @since 1.5 */ public class Introspector { - public static final boolean ALLOW_NONPUBLIC_MBEAN; - static { - @SuppressWarnings("removal") - String val = AccessController.doPrivileged(new GetPropertyAction("jdk.jmx.mbeans.allowNonPublic")); - ALLOW_NONPUBLIC_MBEAN = Boolean.parseBoolean(val); - } + public static final boolean ALLOW_NONPUBLIC_MBEAN = Boolean.parseBoolean(System.getProperty("jdk.jmx.mbeans.allowNonPublic")); /* * ------------------------------------------ @@ -276,7 +269,6 @@ public static void testComplianceMBeanInterface(Class interfaceClass) throws NotCompliantMBeanException { if (mbeanInterface == null) mbeanInterface = getStandardMBeanInterface(baseClass); - ReflectUtil.checkPackageAccess(mbeanInterface); MBeanIntrospector introspector = StandardMBeanIntrospector.getInstance(); return getClassMBeanInfo(introspector, baseClass, mbeanInterface); } @@ -401,18 +393,12 @@ public static Descriptor descriptorForAnnotations(Annotation[] annots) { for (Annotation a : annots) { Class c = a.annotationType(); Method[] elements = c.getMethods(); - boolean packageAccess = false; for (Method element : elements) { DescriptorKey key = element.getAnnotation(DescriptorKey.class); if (key != null) { String name = key.value(); Object value; try { - // Avoid checking access more than once per annotation - if (!packageAccess) { - ReflectUtil.checkPackageAccess(c); - packageAccess = true; - } value = MethodUtil.invoke(element, a, null); } catch (RuntimeException e) { // we don't expect this - except for possibly @@ -560,7 +546,6 @@ public static Object elementFromComplex(Object complex, String element) readMethod = SimpleIntrospector.getReadMethod(clazz, element); } if (readMethod != null) { - ReflectUtil.checkPackageAccess(readMethod.getDeclaringClass()); return MethodUtil.invoke(readMethod, complex, new Class[0]); } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java index 85f433ab11f..fd7bf8fbcae 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -30,10 +30,6 @@ import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; import java.io.ObjectInputStream; -import java.security.AccessController; -import java.security.Permission; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; import java.util.List; import java.util.Set; import java.lang.System.Logger.Level; @@ -48,11 +44,9 @@ import javax.management.ListenerNotFoundException; import javax.management.MBeanException; import javax.management.MBeanInfo; -import javax.management.MBeanPermission; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; -import javax.management.MBeanServerPermission; import javax.management.NotCompliantMBeanException; import javax.management.NotificationFilter; import javax.management.NotificationListener; @@ -232,13 +226,7 @@ public final class JmxMBeanServer final MBeanInstantiator fInstantiator = instantiator; this.secureClr = new - SecureClassLoaderRepository(AccessController.doPrivileged(new PrivilegedAction<>() { - @Override - public ClassLoaderRepository run() { - return fInstantiator.getClassLoaderRepository(); - } - }) - ); + SecureClassLoaderRepository(fInstantiator.getClassLoaderRepository()); if (delegate == null) delegate = new MBeanServerDelegateImpl(); if (outer == null) @@ -947,9 +935,6 @@ public MBeanInfo getMBeanInfo(ObjectName name) throws public Object instantiate(String className) throws ReflectionException, MBeanException { - /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); - return instantiator.instantiate(className); } @@ -984,9 +969,6 @@ public Object instantiate(String className, ObjectName loaderName) throws ReflectionException, MBeanException, InstanceNotFoundException { - /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); - ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.instantiate(className, loaderName, myLoader); } @@ -1022,9 +1004,6 @@ public Object instantiate(String className, Object params[], String signature[]) throws ReflectionException, MBeanException { - /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); - ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.instantiate(className, params, signature, myLoader); @@ -1065,9 +1044,6 @@ public Object instantiate(String className, ObjectName loaderName, throws ReflectionException, MBeanException, InstanceNotFoundException { - /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); - ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.instantiate(className,loaderName,params,signature, myLoader); @@ -1112,8 +1088,6 @@ public boolean isInstanceOf(ObjectName name, String className) public ObjectInputStream deserialize(ObjectName name, byte[] data) throws InstanceNotFoundException, OperationsException { - /* Permission check */ - // This call requires MBeanPermission 'getClassLoaderFor' final ClassLoader loader = getClassLoaderFor(name); return instantiator.deserialize(loader, data); @@ -1145,8 +1119,6 @@ public ObjectInputStream deserialize(String className, byte[] data) "Null className passed in parameter"); } - /* Permission check */ - // This call requires MBeanPermission 'getClassLoaderRepository' final ClassLoaderRepository clr = getClassLoaderRepository(); Class theClass; @@ -1197,16 +1169,6 @@ public ObjectInputStream deserialize(String className, // loaderName = cloneObjectName(loaderName); - /* Permission check */ - // Make this call just to force the 'getClassLoader' - // permission check - try { - getClassLoader(loaderName); - } catch (SecurityException e) { - throw e; - } catch (Exception e) { - } - ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.deserialize(className, loaderName, data, myLoader); } @@ -1222,14 +1184,7 @@ private void initialize() { // Registers the MBeanServer identification MBean try { - AccessController.doPrivileged(new PrivilegedExceptionAction<>() { - public Object run() throws Exception { - mbsInterceptor.registerMBean( - mBeanServerDelegateObject, - MBeanServerDelegate.DELEGATE_NAME); - return null; - } - }); + mbsInterceptor.registerMBean(mBeanServerDelegateObject, MBeanServerDelegate.DELEGATE_NAME); } catch (SecurityException e) { if (MBEANSERVER_LOGGER.isLoggable(Level.DEBUG)) { MBEANSERVER_LOGGER.log(Level.DEBUG, @@ -1251,13 +1206,7 @@ public Object run() throws Exception { class loader. The ClassLoaderRepository knows how to handle that case. */ ClassLoader myLoader = outerShell.getClass().getClassLoader(); - final ModifiableClassLoaderRepository loaders = AccessController.doPrivileged(new PrivilegedAction<>() { - - @Override - public ModifiableClassLoaderRepository run() { - return instantiator.getClassLoaderRepository(); - } - }); + final ModifiableClassLoaderRepository loaders = instantiator.getClassLoaderRepository(); if (loaders != null) { loaders.addClassLoader(myLoader); @@ -1266,17 +1215,8 @@ public ModifiableClassLoaderRepository run() { loaded by the bootstrap class loader we can still load MBeans from the classpath using createMBean(className, objectName). + */ - If this class (JmxMBeanServer) was not loaded by the - system class loader or a parent of it, then the caller - must have RuntimePermission("getClassLoader") for the - getSystemClassLoader() call to succeed. If the caller - does not have that permission, any call to - Class.getClassLoader() will fail. Since there are lots - of those in JMX, we better throw the exception now. - - This permission question is irrelevant when JMX is part - of J2SE (as of 1.5). */ ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); if (systemLoader != myLoader) loaders.addClassLoader(systemLoader); @@ -1341,8 +1281,6 @@ public ClassLoader getClassLoader(ObjectName loaderName) * @return The ClassLoaderRepository for that MBeanServer. **/ public ClassLoaderRepository getClassLoaderRepository() { - /* Permission check */ - checkMBeanPermission(null, null, null, "getClassLoaderRepository"); return secureClr; } @@ -1425,8 +1363,6 @@ public static MBeanServer newMBeanServer(String defaultDomain, // Default is true. final boolean fairLock = DEFAULT_FAIR_LOCK_POLICY; - checkNewMBeanServerPermission(); - // This constructor happens to disregard the value of the interceptors // flag - that is, it always uses the default value - false. // This is admittedly a bug, but we chose not to fix it for now @@ -1493,32 +1429,4 @@ private AttributeList cloneAttributeList(AttributeList list) { } return list; } - - // SECURITY CHECKS - //---------------- - - private static void checkMBeanPermission(String classname, - String member, - ObjectName objectName, - String actions) - throws SecurityException { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanPermission(classname, - member, - objectName, - actions); - sm.checkPermission(perm); - } - } - - private static void checkNewMBeanServerPermission() { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanServerPermission("newMBeanServer"); - sm.checkPermission(perm); - } - } } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanInstantiator.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanInstantiator.java index 6de9ca2873c..53eeb721a13 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanInstantiator.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanInstantiator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -33,18 +33,12 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Permission; -import java.security.Permissions; -import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.Map; import java.lang.System.Logger.Level; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; -import javax.management.MBeanPermission; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.OperationsException; @@ -53,7 +47,6 @@ import javax.management.RuntimeMBeanException; import javax.management.RuntimeOperationsException; import sun.reflect.misc.ConstructorUtil; -import sun.reflect.misc.ReflectUtil; /** * Implements the MBeanInstantiator interface. Provides methods for @@ -95,7 +88,6 @@ public Class findClassWithDefaultLoaderRepository(String className) "Exception occurred during object instantiation"); } - ReflectUtil.checkPackageAccess(className); try { if (clr == null) throw new ClassNotFoundException(className); theClass = clr.loadClass(className); @@ -169,7 +161,6 @@ public Class[] findSignatureClasses(String signature[], continue; } - ReflectUtil.checkPackageAccess(signature[i]); // Ok we do not have a primitive type ! We need to build // the signature of the method // @@ -210,8 +201,6 @@ public Class[] findSignatureClasses(String signature[], public Object instantiate(Class theClass) throws ReflectionException, MBeanException { - checkMBeanPermission(theClass, null, null, "instantiate"); - Object moi; // ------------------------------ @@ -223,7 +212,6 @@ public Object instantiate(Class theClass) } // Instantiate the new object try { - ReflectUtil.checkPackageAccess(theClass); ensureClassAccess(theClass); moi= cons.newInstance(); } catch (InvocationTargetException e) { @@ -262,8 +250,6 @@ public Object instantiate(Class theClass, Object params[], String signature[], ClassLoader loader) throws ReflectionException, MBeanException { - checkMBeanPermission(theClass, null, null, "instantiate"); - // Instantiate the new object // ------------------------------ // ------------------------------ @@ -293,7 +279,6 @@ public Object instantiate(Class theClass, Object params[], NoSuchMethodException("No such constructor")); } try { - ReflectUtil.checkPackageAccess(theClass); ensureClassAccess(theClass); moi = cons.newInstance(params); } @@ -408,7 +393,6 @@ public ObjectInputStream deserialize(String className, IllegalArgumentException(), "Null className passed in parameter"); } - ReflectUtil.checkPackageAccess(className); Class theClass; if (loaderName == null) { // Load the class using the agent class loader @@ -611,7 +595,6 @@ public Object instantiate(String className, * Return the Default Loader Repository used by this instantiator object. **/ public ModifiableClassLoaderRepository getClassLoaderRepository() { - checkMBeanPermission((String)null, null, null, "getClassLoaderRepository"); return clr; } @@ -627,7 +610,6 @@ static Class loadClass(String className, ClassLoader loader) IllegalArgumentException("The class name cannot be null"), "Exception occurred during object instantiation"); } - ReflectUtil.checkPackageAccess(className); try { if (loader == null) loader = MBeanInstantiator.class.getClassLoader(); @@ -678,7 +660,6 @@ static Class[] loadSignatureClasses(String signature[], // We need to load the class through the class // loader of the target object. // - ReflectUtil.checkPackageAccess(signature[i]); tab[i] = Class.forName(signature[i], false, aLoader); } } catch (ClassNotFoundException e) { @@ -715,31 +696,6 @@ private Constructor findConstructor(Class c, Class[] params) { primitiveClasses.put(c.getName(), c); } - private static void checkMBeanPermission(Class clazz, - String member, - ObjectName objectName, - String actions) { - if (clazz != null) { - checkMBeanPermission(clazz.getName(), member, objectName, actions); - } - } - - private static void checkMBeanPermission(String classname, - String member, - ObjectName objectName, - String actions) - throws SecurityException { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanPermission(classname, - member, - objectName, - actions); - sm.checkPermission(perm); - } - } - private static void ensureClassAccess(Class clazz) throws IllegalAccessException { @@ -752,20 +708,8 @@ private static void ensureClassAccess(Class clazz) private ClassLoader getClassLoader(final ObjectName name) { if(clr == null){ return null; + } else { + return clr.getClassLoader(name); } - // Restrict to getClassLoader permission only - Permissions permissions = new Permissions(); - permissions.add(new MBeanPermission("*", null, name, "getClassLoader")); - ProtectionDomain protectionDomain = new ProtectionDomain(null, permissions); - ProtectionDomain[] domains = {protectionDomain}; - @SuppressWarnings("removal") - AccessControlContext ctx = new AccessControlContext(domains); - @SuppressWarnings("removal") - ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<>() { - public ClassLoader run() { - return clr.getClassLoader(name); - } - }, ctx); - return loader; } } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java index b80bb165a7b..fdbb961630c 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -50,7 +50,6 @@ import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; import javax.management.ReflectionException; -import sun.reflect.misc.ReflectUtil; /** * An introspector for MBeans of a certain type. There is one instance @@ -176,7 +175,6 @@ abstract MBeanOperationInfo getMBeanOperationInfo(String operationName, * Get the methods to be analyzed to build the MBean interface. */ final List getMethods(final Class mbeanType) { - ReflectUtil.checkPackageAccess(mbeanType); return Arrays.asList(mbeanType.getMethods()); } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java index ec40a30406b..f63222f251f 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -38,7 +38,6 @@ import javax.management.ObjectName; import javax.management.ReflectionException; import com.sun.jmx.mbeanserver.MXBeanMappingFactory; -import sun.reflect.misc.ReflectUtil; /** * Base class for MBeans. There is one instance of this class for @@ -132,7 +131,6 @@ MBeanSupport(T resource, Class mbeanInterfaceType) " is not an instance of " + mbeanInterfaceType.getName(); throw new NotCompliantMBeanException(msg); } - ReflectUtil.checkPackageAccess(mbeanInterfaceType); this.resource = resource; MBeanIntrospector introspector = getMBeanIntrospector(); this.perInterface = introspector.getPerInterface(mbeanInterfaceType); diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java index fd291133d08..0027710d06a 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -30,7 +30,6 @@ import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; -import java.security.AccessController; import javax.management.InstanceAlreadyExistsException; import javax.management.JMX; import javax.management.MBeanServerConnection; @@ -145,9 +144,7 @@ synchronized void addReference(ObjectName name, Object mxbean) throws InstanceAlreadyExistsException { ObjectName existing = mxbeanToObjectName.get(mxbean); if (existing != null) { - @SuppressWarnings("removal") - String multiname = AccessController.doPrivileged( - new GetPropertyAction("jmx.mxbean.multiname")); + String multiname = System.getProperty("jmx.mxbean.multiname"); if (!"true".equalsIgnoreCase(multiname)) { throw new InstanceAlreadyExistsException( "MXBean already registered with name " + existing); diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/ObjectInputStreamWithLoader.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/ObjectInputStreamWithLoader.java index 4f3e4a43893..339b708ef18 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/ObjectInputStreamWithLoader.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/ObjectInputStreamWithLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -30,7 +30,6 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; -import sun.reflect.misc.ReflectUtil; /** * This class deserializes an object in the context of a specific class loader. @@ -61,7 +60,6 @@ protected Class resolveClass(ObjectStreamClass aClass) return super.resolveClass(aClass); } else { String name = aClass.getName(); - ReflectUtil.checkPackageAccess(name); // Query the class loader ... return Class.forName(name, false, loader); } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/PerInterface.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/PerInterface.java index 9522208aafe..6684e0390cf 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/PerInterface.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/PerInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -25,7 +25,6 @@ package com.sun.jmx.mbeanserver; -import java.security.AccessController; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -175,15 +174,7 @@ private Object noSuchMethod(String msg, Object resource, String operation, throw exception; // No compatibility requirement here // Is the compatibility property set? - GetPropertyAction act = new GetPropertyAction("jmx.invoke.getters"); - String invokeGettersS; - try { - invokeGettersS = AccessController.doPrivileged(act); - } catch (Exception e) { - // We don't expect an exception here but if we get one then - // we'll simply assume that the property is not set. - invokeGettersS = null; - } + String invokeGettersS = System.getProperty("jmx.invoke.getters"); if (invokeGettersS == null) throw exception; diff --git a/src/java.management/share/classes/com/sun/jmx/remote/internal/ArrayNotificationBuffer.java b/src/java.management/share/classes/com/sun/jmx/remote/internal/ArrayNotificationBuffer.java index b2f8e4c028e..d06456a73dd 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/internal/ArrayNotificationBuffer.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/internal/ArrayNotificationBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,10 +25,6 @@ package com.sun.jmx.remote.internal; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -396,20 +392,6 @@ public void dispose() { if (nextSeq < nextSequenceNumber()) { candidate = notificationAt(nextSeq); - // Skip security check if NotificationBufferFilter is not overloaded - if (!(filter instanceof ServerNotifForwarder.NotifForwarderBufferFilter)) { - try { - ServerNotifForwarder.checkMBeanPermission(this.mBeanServer, - candidate.getObjectName(),"addNotificationListener"); - } catch (InstanceNotFoundException | SecurityException e) { - if (logger.debugOn()) { - logger.debug("fetchNotifications", "candidate: " + candidate + " skipped. exception " + e); - } - ++nextSeq; - continue; - } - } - if (logger.debugOn()) { logger.debug("fetchNotifications", "candidate: " + candidate); @@ -653,54 +635,27 @@ private void removeBufferListener(ObjectName name) { } } - @SuppressWarnings("removal") private void addNotificationListener(final ObjectName name, final NotificationListener listener, final NotificationFilter filter, final Object handback) throws Exception { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Void run() throws InstanceNotFoundException { - mBeanServer.addNotificationListener(name, - listener, - filter, - handback); - return null; - } - }); - } catch (Exception e) { - throw extractException(e); - } + mBeanServer.addNotificationListener(name, + listener, + filter, + handback); } - @SuppressWarnings("removal") private void removeNotificationListener(final ObjectName name, final NotificationListener listener) throws Exception { - try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Void run() throws Exception { - mBeanServer.removeNotificationListener(name, listener); - return null; - } - }); - } catch (Exception e) { - throw extractException(e); - } + mBeanServer.removeNotificationListener(name, listener); } - @SuppressWarnings("removal") private Set queryNames(final ObjectName name, final QueryExp query) { - PrivilegedAction> act = - new PrivilegedAction<>() { - public Set run() { - return mBeanServer.queryNames(name, query); - } - }; try { - return AccessController.doPrivileged(act); + return mBeanServer.queryNames(name, query); } catch (RuntimeException e) { logger.fine("queryNames", "Failed to query names: " + e); logger.debug("queryNames", e); @@ -708,18 +663,11 @@ public Set run() { } } - @SuppressWarnings("removal") private static boolean isInstanceOf(final MBeanServer mbs, final ObjectName name, final String className) { - PrivilegedExceptionAction act = - new PrivilegedExceptionAction<>() { - public Boolean run() throws InstanceNotFoundException { - return mbs.isInstanceOf(name, className); - } - }; try { - return AccessController.doPrivileged(act); + return mbs.isInstanceOf(name, className); } catch (Exception e) { logger.fine("isInstanceOf", "failed: " + e); logger.debug("isInstanceOf", e); @@ -825,17 +773,6 @@ private void checkNoLocks() { logger.warning("checkNoLocks", "lock protocol violation"); } - /** - * Iterate until we extract the real exception - * from a stack of PrivilegedActionExceptions. - */ - private static Exception extractException(Exception e) { - while (e instanceof PrivilegedActionException) { - e = ((PrivilegedActionException)e).getException(); - } - return e; - } - private static final ClassLogger logger = new ClassLogger("javax.management.remote.misc", "ArrayNotificationBuffer"); diff --git a/src/java.management/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java b/src/java.management/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java index 471cccecc8a..87947e06ac8 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java @@ -33,9 +33,6 @@ import java.util.Map; import java.util.concurrent.Executor; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; import javax.security.auth.Subject; import javax.management.Notification; @@ -56,9 +53,6 @@ public abstract class ClientNotifForwarder { - @SuppressWarnings("removal") - private final AccessControlContext acc; - public ClientNotifForwarder(Map env) { this(null, env); } @@ -139,7 +133,6 @@ else if (logger.traceOn()) this.defaultClassLoader = defaultClassLoader; this.executor = ex; - this.acc = AccessController.getContext(); } /** @@ -419,40 +412,14 @@ private void logOnce(String msg, SecurityException x) { } // Set new context class loader, returns previous one. - @SuppressWarnings("removal") private final ClassLoader setContextClassLoader(final ClassLoader loader) { - final AccessControlContext ctxt = ClientNotifForwarder.this.acc; - // if ctxt is null, log a config message and throw a - // SecurityException. - if (ctxt == null) { - logOnce("AccessControlContext must not be null.",null); - throw new SecurityException("AccessControlContext must not be null"); - } - return AccessController.doPrivileged( - new PrivilegedAction<>() { - public ClassLoader run() { - try { - // get context class loader - may throw - // SecurityException - though unlikely. - final ClassLoader previous = - Thread.currentThread().getContextClassLoader(); - - // if nothing needs to be done, break here... - if (loader == previous) return previous; - - // reset context class loader - may throw - // SecurityException - Thread.currentThread().setContextClassLoader(loader); - return previous; - } catch (SecurityException x) { - logOnce("Permission to set ContextClassLoader missing. " + - "Notifications will not be dispatched. " + - "Please check your Java policy configuration: " + - x, x); - throw x; - } - } - }, ctxt); + final ClassLoader previous = Thread.currentThread().getContextClassLoader(); + + // if nothing needs to be done, break here... + if (loader == previous) return previous; + + Thread.currentThread().setContextClassLoader(loader); + return previous; } public void run() { diff --git a/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java b/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java index dd557523e65..a1f5ece89a2 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java @@ -29,10 +29,6 @@ import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; import java.io.IOException; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -85,30 +81,16 @@ public Integer addNotificationListener(final ObjectName name, checkState(); - // Explicitly check MBeanPermission for addNotificationListener - // - checkMBeanPermission(name, "addNotificationListener"); if (notificationAccessController != null) { notificationAccessController.addNotificationListener( connectionId, name, getSubject()); } - try { - @SuppressWarnings("removal") - boolean instanceOf = - AccessController.doPrivileged( - new PrivilegedExceptionAction<>() { - public Boolean run() throws InstanceNotFoundException { - return mbeanServer.isInstanceOf(name, broadcasterClass); - } - }); - if (!instanceOf) { - throw new IllegalArgumentException("The specified MBean [" + - name + "] is not a " + - "NotificationBroadcaster " + - "object."); - } - } catch (PrivilegedActionException e) { - throw (InstanceNotFoundException) extractException(e); + boolean instanceOf = mbeanServer.isInstanceOf(name, broadcasterClass); + if (!instanceOf) { + throw new IllegalArgumentException("The specified MBean [" + + name + "] is not a " + + "NotificationBroadcaster " + + "object."); } final Integer id = getListenerID(); @@ -154,9 +136,6 @@ public void removeNotificationListener(ObjectName name, checkState(); - // Explicitly check MBeanPermission for removeNotificationListener - // - checkMBeanPermission(name, "removeNotificationListener"); if (notificationAccessController != null) { notificationAccessController.removeNotificationListener( connectionId, name, getSubject()); @@ -373,55 +352,12 @@ private Integer getListenerID() { } } - /** - * Explicitly check the MBeanPermission for - * the current access control context. - */ - public final void checkMBeanPermission( - final ObjectName name, final String actions) - throws InstanceNotFoundException, SecurityException { - checkMBeanPermission(mbeanServer,name,actions); - } - - @SuppressWarnings("removal") - static void checkMBeanPermission( - final MBeanServer mbs, final ObjectName name, final String actions) - throws InstanceNotFoundException, SecurityException { - - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - AccessControlContext acc = AccessController.getContext(); - ObjectInstance oi; - try { - oi = AccessController.doPrivileged( - new PrivilegedExceptionAction<>() { - public ObjectInstance run() - throws InstanceNotFoundException { - return mbs.getObjectInstance(name); - } - }); - } catch (PrivilegedActionException e) { - throw (InstanceNotFoundException) extractException(e); - } - String classname = oi.getClassName(); - MBeanPermission perm = new MBeanPermission( - classname, - null, - name, - actions); - sm.checkPermission(perm, acc); - } - } - /** * Check if the caller has the right to get the following notifications. */ private boolean allowNotificationEmission(ObjectName name, TargetedNotification tn) { try { - if (checkNotificationEmission) { - checkMBeanPermission(name, "addNotificationListener"); - } if (notificationAccessController != null) { notificationAccessController.fetchNotification( connectionId, name, tn.getNotification(), getSubject()); @@ -444,17 +380,6 @@ private boolean allowNotificationEmission(ObjectName name, } } - /** - * Iterate until we extract the real exception - * from a stack of PrivilegedActionExceptions. - */ - private static Exception extractException(Exception e) { - while (e instanceof PrivilegedActionException) { - e = ((PrivilegedActionException)e).getException(); - } - return e; - } - private static class IdAndFilter { private Integer id; private NotificationFilter filter; diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java b/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java index bb5d5e88ff3..65a1b424477 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java @@ -24,13 +24,9 @@ */ package com.sun.jmx.remote.security; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; import java.io.File; -import java.io.FilePermission; import java.io.IOException; -import java.security.AccessControlException; -import java.security.AccessController; import java.util.Arrays; import java.util.Map; @@ -109,12 +105,9 @@ public class FileLoginModule implements LoginModule { private static final String PASSWORD_FILE_NAME = "jmxremote.password"; // Location of the default password file - @SuppressWarnings("removal") private static final String DEFAULT_PASSWORD_FILE_NAME = - AccessController.doPrivileged(new GetPropertyAction("java.home")) + - File.separatorChar + "conf" + - File.separatorChar + "management" + File.separatorChar + - PASSWORD_FILE_NAME; + System.getProperty("java.home") + File.separatorChar + "conf" + File.separatorChar + + "management" + File.separatorChar + PASSWORD_FILE_NAME; // Key to retrieve the stored username private static final String USERNAME_KEY = @@ -152,7 +145,6 @@ public class FileLoginModule implements LoginModule { private String passwordFile; private String passwordFileDisplayName; private boolean userSuppliedPasswordFile; - private boolean hasJavaHomePermission; private HashedPasswordManager hashPwdMgr; /** @@ -196,14 +188,7 @@ public void initialize(Subject subject, CallbackHandler callbackHandler, if (passwordFile == null) { passwordFile = DEFAULT_PASSWORD_FILE_NAME; userSuppliedPasswordFile = false; - try { - System.getProperty("java.home"); - hasJavaHomePermission = true; - passwordFileDisplayName = passwordFile; - } catch (SecurityException e) { - hasJavaHomePermission = false; - passwordFileDisplayName = PASSWORD_FILE_NAME; - } + passwordFileDisplayName = passwordFile; } } @@ -233,18 +218,6 @@ public boolean login() throws LoginException { "Error: unable to load the password file: " + passwordFileDisplayName); throw EnvHelp.initCause(le, ioe); - } catch (SecurityException e) { - if (userSuppliedPasswordFile || hasJavaHomePermission) { - throw e; - } else { - final FilePermission fp - = new FilePermission(passwordFileDisplayName, "read"); - @SuppressWarnings("removal") - AccessControlException ace = new AccessControlException( - "access denied " + fp.toString()); - ace.initCause(e); - throw ace; - } } if (logger.debugOn()) { diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java b/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java index 8de1a71fd6a..c50b22fade6 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -232,16 +232,9 @@ public synchronized boolean authenticate(String userName, char[] inputPassword) * SHA3-512 hash * * @throws IOException If unable to access the file - * @throws SecurityException If read/write file permissions are not granted */ public synchronized void loadPasswords() - throws IOException, SecurityException { - - @SuppressWarnings("removal") - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkRead(passwordFile); - } + throws IOException { AtomicBoolean hasClearPasswords = new AtomicBoolean(false); StringBuilder sbuf = new StringBuilder(); diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java index f67bde263ea..89a77b3ddaf 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -26,11 +26,8 @@ package com.sun.jmx.remote.security; import java.io.IOException; -import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -105,32 +102,10 @@ public JMXPluggableAuthenticator(Map env) { } else { // use the default JAAS login configuration (file-based) - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission( - new AuthPermission("createLoginContext." + - LOGIN_CONFIG_NAME)); - } - - final String pf = passwordFile; - final String hashPass = hashPasswords; - try { - @SuppressWarnings("removal") - var tmp = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public LoginContext run() throws LoginException { - return new LoginContext( - LOGIN_CONFIG_NAME, + loginContext = new LoginContext(LOGIN_CONFIG_NAME, null, new JMXCallbackHandler(), - new FileLoginConfig(pf, hashPass)); - } - }); - loginContext = tmp; - } catch (PrivilegedActionException pae) { - throw (LoginException) pae.getException(); - } + new FileLoginConfig(passwordFile, hashPasswords)); } } catch (LoginException | SecurityException e) { @@ -190,14 +165,7 @@ public Subject authenticate(Object credentials) { try { loginContext.login(); final Subject subject = loginContext.getSubject(); - @SuppressWarnings("removal") - var dummy = AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - subject.setReadOnly(); - return null; - } - }); - + subject.setReadOnly(); return subject; } catch (LoginException le) { diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXSubjectDomainCombiner.java b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXSubjectDomainCombiner.java deleted file mode 100644 index a8f97fad1e4..00000000000 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXSubjectDomainCombiner.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2003, 2021, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package com.sun.jmx.remote.security; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.CodeSource; -import java.security.Permissions; -import java.security.ProtectionDomain; -import javax.security.auth.Subject; -import javax.security.auth.SubjectDomainCombiner; - -/** - *

      This class represents an extension to the {@link SubjectDomainCombiner} - * and is used to add a new {@link ProtectionDomain}, comprised of a null - * codesource/signers and an empty permission set, to the access control - * context with which this combiner is combined.

      - * - *

      When the {@link #combine} method is called the {@link ProtectionDomain} - * is augmented with the permissions granted to the set of principals present - * in the supplied {@link Subject}.

      - */ -@SuppressWarnings("removal") -public class JMXSubjectDomainCombiner extends SubjectDomainCombiner { - - public JMXSubjectDomainCombiner(Subject s) { - super(s); - } - - public ProtectionDomain[] combine(ProtectionDomain[] current, - ProtectionDomain[] assigned) { - // Add a new ProtectionDomain with the null codesource/signers, and - // the empty permission set, to the end of the array containing the - // 'current' protections domains, i.e. the ones that will be augmented - // with the permissions granted to the set of principals present in - // the supplied subject. - // - ProtectionDomain[] newCurrent; - if (current == null || current.length == 0) { - newCurrent = new ProtectionDomain[1]; - newCurrent[0] = pdNoPerms; - } else { - newCurrent = new ProtectionDomain[current.length + 1]; - for (int i = 0; i < current.length; i++) { - newCurrent[i] = current[i]; - } - newCurrent[current.length] = pdNoPerms; - } - return super.combine(newCurrent, assigned); - } - - /** - * A null CodeSource. - */ - private static final CodeSource nullCodeSource = - new CodeSource(null, (java.security.cert.Certificate[]) null); - - /** - * A ProtectionDomain with a null CodeSource and an empty permission set. - */ - private static final ProtectionDomain pdNoPerms = - new ProtectionDomain(nullCodeSource, new Permissions(), null, null); - - /** - * Get the current AccessControlContext combined with the supplied subject. - */ - public static AccessControlContext getContext(Subject subject) { - return new AccessControlContext(AccessController.getContext(), - new JMXSubjectDomainCombiner(subject)); - } - - /** - * Get the AccessControlContext of the domain combiner created with - * the supplied subject, i.e. an AccessControlContext with the domain - * combiner created with the supplied subject and where the caller's - * context has been removed. - */ - public static AccessControlContext - getDomainCombinerContext(Subject subject) { - return new AccessControlContext( - new AccessControlContext(new ProtectionDomain[0]), - new JMXSubjectDomainCombiner(subject)); - } -} diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerAccessController.java b/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerAccessController.java index c94750e99ff..7fd7fcb2dd2 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerAccessController.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerAccessController.java @@ -25,9 +25,7 @@ package com.sun.jmx.remote.security; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.ObjectInputStream; -import java.security.AccessController; import java.util.Set; import javax.management.Attribute; import javax.management.AttributeList; @@ -74,13 +72,8 @@ * be overridden, for instance if the default checking behavior is * inappropriate.

      * - *

      If there is no SecurityManager, then the access controller will refuse - * to create an MBean that is a ClassLoader. This prevents - * people from opening security holes unintentionally. Otherwise, it - * would not be obvious that granting write access grants the ability to - * download and execute arbitrary code in the target MBean server. Advanced - * users who do want an MBean which is a ClassLoader are presumably advanced enough - * to handle policy files and security managers.

      + *

      The access controller will refuse to create an MBean that is a ClassLoader. + *

      */ public abstract class MBeanServerAccessController implements MBeanServerForwarder { @@ -174,15 +167,9 @@ public ObjectInstance createMBean(String className, ObjectName name) MBeanException, NotCompliantMBeanException { checkCreate(className); - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - Object object = getMBeanServer().instantiate(className); - checkClassLoader(object); - return getMBeanServer().registerMBean(object, name); - } else { - return getMBeanServer().createMBean(className, name); - } + Object object = getMBeanServer().instantiate(className); + checkClassLoader(object); + return getMBeanServer().registerMBean(object, name); } /** @@ -198,18 +185,11 @@ public ObjectInstance createMBean(String className, ObjectName name, MBeanException, NotCompliantMBeanException { checkCreate(className); - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - Object object = getMBeanServer().instantiate(className, - params, - signature); - checkClassLoader(object); - return getMBeanServer().registerMBean(object, name); - } else { - return getMBeanServer().createMBean(className, name, - params, signature); - } + Object object = getMBeanServer().instantiate(className, + params, + signature); + checkClassLoader(object); + return getMBeanServer().registerMBean(object, name); } /** @@ -227,16 +207,10 @@ public ObjectInstance createMBean(String className, NotCompliantMBeanException, InstanceNotFoundException { checkCreate(className); - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - Object object = getMBeanServer().instantiate(className, - loaderName); - checkClassLoader(object); - return getMBeanServer().registerMBean(object, name); - } else { - return getMBeanServer().createMBean(className, name, loaderName); - } + Object object = getMBeanServer().instantiate(className, + loaderName); + checkClassLoader(object); + return getMBeanServer().registerMBean(object, name); } /** @@ -256,19 +230,12 @@ public ObjectInstance createMBean(String className, NotCompliantMBeanException, InstanceNotFoundException { checkCreate(className); - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - Object object = getMBeanServer().instantiate(className, - loaderName, - params, - signature); - checkClassLoader(object); - return getMBeanServer().registerMBean(object, name); - } else { - return getMBeanServer().createMBean(className, name, loaderName, - params, signature); - } + Object object = getMBeanServer().instantiate(className, + loaderName, + params, + signature); + checkClassLoader(object); + return getMBeanServer().registerMBean(object, name); } /** @@ -614,8 +581,7 @@ private void checkClassLoader(Object object) { if (object instanceof ClassLoader) throw new SecurityException("Access denied! Creating an " + "MBean that is a ClassLoader " + - "is forbidden unless a security " + - "manager is installed."); + "is forbidden."); } //------------------ diff --git a/src/java.management/share/classes/com/sun/jmx/remote/util/EnvHelp.java b/src/java.management/share/classes/com/sun/jmx/remote/util/EnvHelp.java index 3dcadb56325..a64db45c22f 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/util/EnvHelp.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/util/EnvHelp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -39,14 +39,11 @@ import java.util.TreeMap; import java.util.TreeSet; -import java.security.AccessController; - import javax.management.ObjectName; import javax.management.MBeanServer; import javax.management.InstanceNotFoundException; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServerFactory; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.remote.security.NotificationAccessController; public class EnvHelp { @@ -272,13 +269,11 @@ public static int getNotifBufferSize(Map env) { // the default value re-specified in the system try { - GetPropertyAction act = new GetPropertyAction(BUFFER_SIZE_PROPERTY); - String s = AccessController.doPrivileged(act); + String s = System.getProperty(BUFFER_SIZE_PROPERTY); if (s != null) { defaultQueueSize = Integer.parseInt(s); } else { // try the old one - act = new GetPropertyAction(oldP); - s = AccessController.doPrivileged(act); + s = System.getProperty(oldP); if (s != null) { defaultQueueSize = Integer.parseInt(s); } diff --git a/src/java.management/share/classes/com/sun/jmx/remote/util/OrderClassLoaders.java b/src/java.management/share/classes/com/sun/jmx/remote/util/OrderClassLoaders.java index cc22f207c29..b2449648292 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/util/OrderClassLoaders.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/util/OrderClassLoaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,8 +25,6 @@ package com.sun.jmx.remote.util; -import sun.reflect.misc.ReflectUtil; - public class OrderClassLoaders extends ClassLoader { public OrderClassLoaders(ClassLoader cl1, ClassLoader cl2) { super(cl1); @@ -35,7 +33,6 @@ public OrderClassLoaders(ClassLoader cl1, ClassLoader cl2) { } protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - ReflectUtil.checkPackageAccess(name); try { return super.loadClass(name, resolve); } catch (ClassNotFoundException cne) { diff --git a/src/java.management/share/classes/java/lang/management/ManagementFactory.java b/src/java.management/share/classes/java/lang/management/ManagementFactory.java index 03bbcead194..547d7da9692 100644 --- a/src/java.management/share/classes/java/lang/management/ManagementFactory.java +++ b/src/java.management/share/classes/java/lang/management/ManagementFactory.java @@ -25,24 +25,20 @@ package java.lang.management; -import java.io.FilePermission; import java.io.IOException; import javax.management.DynamicMBean; +import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MBeanServerFactory; -import javax.management.MBeanServerPermission; +import javax.management.NotCompliantMBeanException; import javax.management.NotificationEmitter; import javax.management.ObjectName; +import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.MalformedObjectNameException; import javax.management.StandardEmitterMBean; import javax.management.StandardMBean; -import java.security.AccessController; -import java.security.Permission; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -469,13 +465,6 @@ public static List getGarbageCollectorMXBeans() { * @see javax.management.MBeanServerFactory#createMBeanServer */ public static synchronized MBeanServer getPlatformMBeanServer() { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanServerPermission("createMBeanServer"); - sm.checkPermission(perm); - } - if (platformMBeanServer == null) { platformMBeanServer = MBeanServerFactory.createMBeanServer(); platformComponents() @@ -594,10 +583,7 @@ public static synchronized MBeanServer getPlatformMBeanServer() { // Only allow MXBean interfaces from the platform modules loaded by the // bootstrap or platform class loader final Class cls = mxbeanInterface; - @SuppressWarnings("removal") - ClassLoader loader = - AccessController.doPrivileged( - (PrivilegedAction) () -> cls.getClassLoader()); + ClassLoader loader = cls.getClassLoader(); if (!jdk.internal.misc.VM.isSystemDomainLoader(loader)) { throw new IllegalArgumentException(mxbeanName + " is not a platform MXBean"); @@ -882,29 +868,22 @@ private static Stream getProxyNames(PlatformComponent pc, private static final String NOTIF_EMITTER = "javax.management.NotificationEmitter"; - @SuppressWarnings("removal") private static void addMXBean(final MBeanServer mbs, String name, final Object pmo) { try { ObjectName oname = ObjectName.getInstance(name); // Make DynamicMBean out of MXBean by wrapping it with a StandardMBean - AccessController.doPrivileged((PrivilegedExceptionAction) () -> { - final DynamicMBean dmbean; - if (pmo instanceof DynamicMBean) { - dmbean = DynamicMBean.class.cast(pmo); - } else if (pmo instanceof NotificationEmitter) { - dmbean = new StandardEmitterMBean(pmo, null, true, (NotificationEmitter) pmo); - } else { - dmbean = new StandardMBean(pmo, null, true); - } - - mbs.registerMBean(dmbean, oname); - return null; - }); - } catch (MalformedObjectNameException mone) { - throw new IllegalArgumentException(mone); - } catch (PrivilegedActionException e) { - throw new RuntimeException(e.getException()); + final DynamicMBean dmbean; + if (pmo instanceof DynamicMBean) { + dmbean = DynamicMBean.class.cast(pmo); + } else if (pmo instanceof NotificationEmitter) { + dmbean = new StandardEmitterMBean(pmo, null, true, (NotificationEmitter) pmo); + } else { + dmbean = new StandardMBean(pmo, null, true); + } + mbs.registerMBean(dmbean, oname); + } catch (MalformedObjectNameException | InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) { + throw new IllegalArgumentException(e); } } @@ -918,19 +897,11 @@ private static class PlatformMBeanFinder { static { // get all providers - @SuppressWarnings("removal") - List providers = AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public List run() { - List all = new ArrayList<>(); - for (PlatformMBeanProvider provider : ServiceLoader.loadInstalled(PlatformMBeanProvider.class)) { - all.add(provider); - } - all.add(new DefaultPlatformMBeanProvider()); - return all; - } - }, null, new FilePermission("<>", "read")); + List providers = new ArrayList<>(); + for (PlatformMBeanProvider provider : ServiceLoader.loadInstalled(PlatformMBeanProvider.class)) { + providers.add(provider); + } + providers.add(new DefaultPlatformMBeanProvider()); // load all platform components into a map var map = new HashMap>(); @@ -1013,11 +984,8 @@ static PlatformComponent findSingleton(Class mbeanIntf) loadNativeLib(); } - @SuppressWarnings({"removal", "restricted"}) + @SuppressWarnings("restricted") private static void loadNativeLib() { - AccessController.doPrivileged((PrivilegedAction) () -> { - System.loadLibrary("management"); - return null; - }); + System.loadLibrary("management"); } } diff --git a/src/java.management/share/classes/java/lang/management/RuntimeMXBean.java b/src/java.management/share/classes/java/lang/management/RuntimeMXBean.java index 30a399ad6a1..d2aae5cda08 100644 --- a/src/java.management/share/classes/java/lang/management/RuntimeMXBean.java +++ b/src/java.management/share/classes/java/lang/management/RuntimeMXBean.java @@ -25,9 +25,6 @@ package java.lang.management; -import java.security.AccessController; -import java.security.PrivilegedAction; - /** * The management interface for the runtime system of * the Java virtual machine. @@ -76,8 +73,7 @@ public interface RuntimeMXBean extends PlatformManagedObject { */ @SuppressWarnings("removal") public default long getPid() { - return AccessController.doPrivileged((PrivilegedAction) - () -> ProcessHandle.current().pid()); + return ProcessHandle.current().pid(); } /** diff --git a/src/java.management/share/classes/javax/management/ClassAttributeValueExp.java b/src/java.management/share/classes/javax/management/ClassAttributeValueExp.java index f7c40691e39..43f3f428fab 100644 --- a/src/java.management/share/classes/javax/management/ClassAttributeValueExp.java +++ b/src/java.management/share/classes/javax/management/ClassAttributeValueExp.java @@ -25,10 +25,6 @@ package javax.management; -import java.security.AccessController; - -import com.sun.jmx.mbeanserver.GetPropertyAction; - /** * This class represents the name of the Java implementation class of * the MBean. It is used for performing queries based on the class of diff --git a/src/java.management/share/classes/javax/management/JMX.java b/src/java.management/share/classes/javax/management/JMX.java index f1dba270579..82b14c691b4 100644 --- a/src/java.management/share/classes/javax/management/JMX.java +++ b/src/java.management/share/classes/javax/management/JMX.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -29,7 +29,6 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import sun.reflect.misc.ReflectUtil; /** * Static methods from the JMX API. There are no instances of this class. diff --git a/src/java.management/share/classes/javax/management/MBeanAttributeInfo.java b/src/java.management/share/classes/javax/management/MBeanAttributeInfo.java index 6551e9ba81b..e236e511976 100644 --- a/src/java.management/share/classes/javax/management/MBeanAttributeInfo.java +++ b/src/java.management/share/classes/javax/management/MBeanAttributeInfo.java @@ -26,9 +26,7 @@ package javax.management; import java.lang.reflect.Method; -import java.security.AccessController; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Introspector; import java.util.Objects; diff --git a/src/java.management/share/classes/javax/management/MBeanInfo.java b/src/java.management/share/classes/javax/management/MBeanInfo.java index cfb2e6c0ded..df54b4228e2 100644 --- a/src/java.management/share/classes/javax/management/MBeanInfo.java +++ b/src/java.management/share/classes/javax/management/MBeanInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -34,8 +34,6 @@ import java.util.Arrays; import java.util.Map; import java.util.WeakHashMap; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Objects; import static javax.management.ImmutableDescriptor.nonNullDescriptor; @@ -551,10 +549,8 @@ static boolean arrayGettersSafe(Class subclass, Class immutableClass) { Boolean safe = arrayGettersSafeMap.get(subclass); if (safe == null) { try { - ArrayGettersSafeAction action = - new ArrayGettersSafeAction(subclass, immutableClass); - safe = AccessController.doPrivileged(action); - } catch (Exception e) { // e.g. SecurityException + safe = arrayGettersSafeHelper(subclass, immutableClass); + } catch (Exception e) { /* We don't know, so we assume it isn't. */ safe = false; } @@ -564,25 +560,7 @@ static boolean arrayGettersSafe(Class subclass, Class immutableClass) { } } - /* - * The PrivilegedAction stuff is probably overkill. We can be - * pretty sure the caller does have the required privileges -- a - * JMX user that can't do reflection can't even use Standard - * MBeans! But there's probably a performance gain by not having - * to check the whole call stack. - */ - private static class ArrayGettersSafeAction - implements PrivilegedAction { - - private final Class subclass; - private final Class immutableClass; - - ArrayGettersSafeAction(Class subclass, Class immutableClass) { - this.subclass = subclass; - this.immutableClass = immutableClass; - } - - public Boolean run() { + private static boolean arrayGettersSafeHelper(Class subclass, Class immutableClass) { Method[] methods = immutableClass.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; @@ -601,7 +579,6 @@ public Boolean run() { } } return true; - } } private static boolean isEqual(String s1, String s2) { diff --git a/src/java.management/share/classes/javax/management/MBeanServer.java b/src/java.management/share/classes/javax/management/MBeanServer.java index efecf0def02..036fea93eb7 100644 --- a/src/java.management/share/classes/javax/management/MBeanServer.java +++ b/src/java.management/share/classes/javax/management/MBeanServer.java @@ -68,13 +68,6 @@ * @since 1.5 */ -/* DELETED: - * - *
    1. For the {@link #isRegistered isRegistered} method, the - * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(null, null, name, "isRegistered")}.

      - */ public interface MBeanServer extends MBeanServerConnection { /** diff --git a/src/java.management/share/classes/javax/management/MBeanServerFactory.java b/src/java.management/share/classes/javax/management/MBeanServerFactory.java index 21f20e23991..3030d9e2f9a 100644 --- a/src/java.management/share/classes/javax/management/MBeanServerFactory.java +++ b/src/java.management/share/classes/javax/management/MBeanServerFactory.java @@ -28,14 +28,9 @@ import com.sun.jmx.defaults.JmxProperties; import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER; import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; -import java.security.AccessController; -import java.security.Permission; import java.util.ArrayList; import java.lang.System.Logger.Level; import javax.management.loading.ClassLoaderRepository; -import sun.reflect.misc.ReflectUtil; - /** *

      Provides MBean server references. There are no instances of @@ -100,38 +95,6 @@ private MBeanServerFactory() { **/ private static MBeanServerBuilder builder = null; - /** - * Provide a new {@link javax.management.MBeanServerBuilder}. - * @param builder The new MBeanServerBuilder that will be used to - * create {@link javax.management.MBeanServer}s. - * @exception IllegalArgumentException if the given builder is null. - * - * @exception SecurityException if there is a SecurityManager and - * the caller's permissions do not include or imply {@link - * MBeanServerPermission}("setMBeanServerBuilder"). - * - **/ - // public static synchronized void - // setMBeanServerBuilder(MBeanServerBuilder builder) { - // checkPermission("setMBeanServerBuilder"); - // MBeanServerFactory.builder = builder; - // } - - /** - * Get the current {@link javax.management.MBeanServerBuilder}. - * - * @return the current {@link javax.management.MBeanServerBuilder}. - * - * @exception SecurityException if there is a SecurityManager and - * the caller's permissions do not include or imply {@link - * MBeanServerPermission}("getMBeanServerBuilder"). - * - **/ - // public static synchronized MBeanServerBuilder getMBeanServerBuilder() { - // checkPermission("getMBeanServerBuilder"); - // return builder; - // } - /** * Remove internal MBeanServerFactory references to a created * MBeanServer. This allows the garbage collector to remove the @@ -146,8 +109,6 @@ private MBeanServerFactory() { * */ public static void releaseMBeanServer(MBeanServer mbeanServer) { - checkPermission("releaseMBeanServer"); - removeMBeanServer(mbeanServer); } @@ -215,8 +176,6 @@ public static MBeanServer createMBeanServer() { * MBeanServerBuilder}. */ public static MBeanServer createMBeanServer(String domain) { - checkPermission("createMBeanServer"); - final MBeanServer mBeanServer = newMBeanServer(domain); addMBeanServer(mBeanServer); return mBeanServer; @@ -290,8 +249,6 @@ public static MBeanServer newMBeanServer() { * MBeanServerBuilder}. */ public static MBeanServer newMBeanServer(String domain) { - checkPermission("newMBeanServer"); - // Get the builder. Creates a new one if necessary. // final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder(); @@ -335,8 +292,6 @@ public static MBeanServer newMBeanServer(String domain) { public static synchronized ArrayList findMBeanServer(String agentId) { - checkPermission("findMBeanServer"); - if (agentId == null) return new ArrayList<>(mBeanServerList); @@ -379,16 +334,6 @@ private static String mBeanServerId(MBeanServer mbs) { } } - private static void checkPermission(String action) - throws SecurityException { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - Permission perm = new MBeanServerPermission(action); - sm.checkPermission(perm); - } - } - private static synchronized void addMBeanServer(MBeanServer mbs) { mBeanServerList.add(mbs); } @@ -420,7 +365,7 @@ private static Class loadBuilderClass(String builderClassName) } // No context class loader? Try with Class.forName() - return ReflectUtil.forName(builderClassName); + return Class.forName(builderClassName); } /** @@ -450,10 +395,7 @@ private static MBeanServerBuilder newBuilder(Class builderClass) { **/ private static synchronized void checkMBeanServerBuilder() { try { - GetPropertyAction act = - new GetPropertyAction(JMX_INITIAL_BUILDER); - @SuppressWarnings("removal") - String builderClassName = AccessController.doPrivileged(act); + String builderClassName = System.getProperty(JMX_INITIAL_BUILDER); try { final Class newBuilderClass; @@ -499,9 +441,6 @@ private static synchronized void checkMBeanServerBuilder() { * * @return the new current {@link javax.management.MBeanServerBuilder}. * - * @exception SecurityException if there is a SecurityManager and - * the caller's permissions do not make it possible to instantiate - * a new builder. * @exception JMRuntimeException if the builder instantiation * fails with a checked exception - * {@link java.lang.ClassNotFoundException} etc... diff --git a/src/java.management/share/classes/javax/management/Notification.java b/src/java.management/share/classes/javax/management/Notification.java index 337628aeec8..a1db858bda6 100644 --- a/src/java.management/share/classes/javax/management/Notification.java +++ b/src/java.management/share/classes/javax/management/Notification.java @@ -31,10 +31,6 @@ import java.io.ObjectStreamField; import java.util.EventObject; -import java.security.AccessController; - -import com.sun.jmx.mbeanserver.GetPropertyAction; - /** *

      The Notification class represents a notification emitted by an * MBean. It contains a reference to the source MBean: if the diff --git a/src/java.management/share/classes/javax/management/NumericValueExp.java b/src/java.management/share/classes/javax/management/NumericValueExp.java index bae84019d2e..b748bb6e629 100644 --- a/src/java.management/share/classes/javax/management/NumericValueExp.java +++ b/src/java.management/share/classes/javax/management/NumericValueExp.java @@ -25,16 +25,11 @@ package javax.management; - -import com.sun.jmx.mbeanserver.GetPropertyAction; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; - /** * This class represents numbers that are arguments to relational constraints. * A NumericValueExp may be used anywhere a ValueExp is required. diff --git a/src/java.management/share/classes/javax/management/ObjectName.java b/src/java.management/share/classes/javax/management/ObjectName.java index f20750c9d8a..4bcda7f0252 100644 --- a/src/java.management/share/classes/javax/management/ObjectName.java +++ b/src/java.management/share/classes/javax/management/ObjectName.java @@ -25,14 +25,12 @@ package javax.management; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; diff --git a/src/java.management/share/classes/javax/management/StandardMBean.java b/src/java.management/share/classes/javax/management/StandardMBean.java index a410ffb3492..cf61ff5a6c6 100644 --- a/src/java.management/share/classes/javax/management/StandardMBean.java +++ b/src/java.management/share/classes/javax/management/StandardMBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -33,7 +33,6 @@ import com.sun.jmx.mbeanserver.StandardMBeanSupport; import com.sun.jmx.mbeanserver.Util; -import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; @@ -1163,9 +1162,7 @@ static boolean immutableInfo(Class subclass) { Boolean safe = mbeanInfoSafeMap.get(subclass); if (safe == null) { try { - MBeanInfoSafeAction action = - new MBeanInfoSafeAction(subclass); - safe = AccessController.doPrivileged(action); + safe = mBeanInfoSafe(subclass); } catch (Exception e) { // e.g. SecurityException /* We don't know, so we assume it isn't. */ safe = false; @@ -1189,16 +1186,8 @@ static boolean overrides(Class subclass, Class superclass, return false; } - private static class MBeanInfoSafeAction - implements PrivilegedAction { - private final Class subclass; - - MBeanInfoSafeAction(Class subclass) { - this.subclass = subclass; - } - - public Boolean run() { + private static boolean mBeanInfoSafe(Class subclass) { // Check for "void cacheMBeanInfo(MBeanInfo)" method. // if (overrides(subclass, StandardMBean.class, @@ -1230,6 +1219,5 @@ public Boolean run() { "getNotificationInfo", (Class[]) null)) return false; return true; - } } } diff --git a/src/java.management/share/classes/javax/management/modelmbean/DescriptorSupport.java b/src/java.management/share/classes/javax/management/modelmbean/DescriptorSupport.java index bea7ad92d0c..ebf08d6f1d9 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/DescriptorSupport.java +++ b/src/java.management/share/classes/javax/management/modelmbean/DescriptorSupport.java @@ -32,7 +32,6 @@ import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; import static com.sun.jmx.mbeanserver.Util.cast; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; import java.io.IOException; @@ -42,7 +41,6 @@ import java.lang.reflect.Constructor; -import java.security.AccessController; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; @@ -58,8 +56,6 @@ import javax.management.MBeanException; import javax.management.RuntimeOperationsException; -import sun.reflect.misc.ReflectUtil; - /** * This class represents the metadata set for a ModelMBean element. A * descriptor is part of the ModelMBeanInfo, @@ -1140,7 +1136,6 @@ private static Object parseQuotedFieldValue(String s) final Constructor constr; try { - ReflectUtil.checkPackageAccess(className); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); final Class c = diff --git a/src/java.management/share/classes/javax/management/modelmbean/InvalidTargetObjectTypeException.java b/src/java.management/share/classes/javax/management/modelmbean/InvalidTargetObjectTypeException.java index d7d3bed055c..ca94d88c794 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/InvalidTargetObjectTypeException.java +++ b/src/java.management/share/classes/javax/management/modelmbean/InvalidTargetObjectTypeException.java @@ -30,13 +30,10 @@ package javax.management.modelmbean; -import com.sun.jmx.mbeanserver.GetPropertyAction; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; /** * Exception thrown when an invalid target object type is specified. diff --git a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanAttributeInfo.java b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanAttributeInfo.java index d30bd368a63..674b5ccffbd 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanAttributeInfo.java +++ b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanAttributeInfo.java @@ -31,14 +31,12 @@ package javax.management.modelmbean; import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.lang.reflect.Method; -import java.security.AccessController; import java.lang.System.Logger.Level; import javax.management.Descriptor; diff --git a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanConstructorInfo.java b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanConstructorInfo.java index 0b8413083ac..2374b862bf3 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanConstructorInfo.java +++ b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanConstructorInfo.java @@ -31,14 +31,12 @@ package javax.management.modelmbean; import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.lang.reflect.Constructor; -import java.security.AccessController; import java.lang.System.Logger.Level; import javax.management.Descriptor; diff --git a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java index a209c2c7f42..a7d169041a8 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java +++ b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java @@ -31,13 +31,11 @@ package javax.management.modelmbean; import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; import java.lang.System.Logger.Level; import javax.management.Descriptor; diff --git a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java index c0bce84a5d9..8870217cf60 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java +++ b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanNotificationInfo.java @@ -31,13 +31,11 @@ package javax.management.modelmbean; import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; import java.lang.System.Logger.Level; import javax.management.Descriptor; diff --git a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanOperationInfo.java b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanOperationInfo.java index 4120bc2cd2d..0249d00eeb6 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanOperationInfo.java +++ b/src/java.management/share/classes/javax/management/modelmbean/ModelMBeanOperationInfo.java @@ -31,14 +31,12 @@ package javax.management.modelmbean; import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.lang.reflect.Method; -import java.security.AccessController; import java.lang.System.Logger.Level; import javax.management.Descriptor; diff --git a/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java b/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java index d213324f35c..f15a8bee2b3 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java +++ b/src/java.management/share/classes/javax/management/modelmbean/RequiredModelMBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -37,9 +37,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Date; import java.util.HashMap; @@ -78,11 +75,8 @@ import javax.management.RuntimeOperationsException; import javax.management.ServiceNotFoundException; import javax.management.loading.ClassLoaderRepository; -import jdk.internal.access.JavaSecurityAccess; -import jdk.internal.access.SharedSecrets; import sun.reflect.misc.MethodUtil; -import sun.reflect.misc.ReflectUtil; /** * This class is the implementation of a ModelMBean. An appropriate @@ -140,10 +134,6 @@ public class RequiredModelMBean private boolean registered = false; private transient MBeanServer server = null; - private static final JavaSecurityAccess javaSecurityAccess = SharedSecrets.getJavaSecurityAccess(); - @SuppressWarnings("removal") - private final AccessControlContext acc = AccessController.getContext(); - /*************************************/ /* constructors */ /*************************************/ @@ -964,32 +954,18 @@ public Object invoke(String opName, Object[] opArgs, String[] sig) } } - final Class targetClass; + Class targetClass = null; if (opClassName != null) { try { - @SuppressWarnings("removal") - AccessControlContext stack = AccessController.getContext(); - final Object obj = targetObject; - final String className = opClassName; final ClassNotFoundException[] caughtException = new ClassNotFoundException[1]; - targetClass = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<>() { - - @Override - public Class run() { - try { - ReflectUtil.checkPackageAccess(className); - final ClassLoader targetClassLoader = - obj.getClass().getClassLoader(); - return Class.forName(className, false, - targetClassLoader); - } catch (ClassNotFoundException e) { - caughtException[0] = e; - } - return null; - } - }, stack, acc); + final ClassLoader targetClassLoader = targetObject.getClass().getClassLoader(); + try { + targetClass = Class.forName(opClassName, false, targetClassLoader); + } catch (ClassNotFoundException e) { + caughtException[0] = e; + } if (caughtException[0] != null) { throw caughtException[0]; @@ -1039,16 +1015,10 @@ private Method resolveMethod(Class targetClass, if (sig == null) argClasses = null; else { - @SuppressWarnings("removal") - final AccessControlContext stack = AccessController.getContext(); final ReflectionException[] caughtException = new ReflectionException[1]; final ClassLoader targetClassLoader = targetClass.getClassLoader(); argClasses = new Class[sig.length]; - javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction() { - - @Override - public Void run() { for (int i = 0; i < sig.length; i++) { if (tracing) { MODELMBEAN_LOGGER.log(Level.TRACE, @@ -1057,7 +1027,6 @@ public Void run() { argClasses[i] = (Class) primitiveClassMap.get(sig[i]); if (argClasses[i] == null) { try { - ReflectUtil.checkPackageAccess(sig[i]); argClasses[i] = Class.forName(sig[i], false, targetClassLoader); } catch (ClassNotFoundException e) { @@ -1070,9 +1039,6 @@ public Void run() { } } } - return null; - } - }, stack, acc); if (caughtException[0] != null) { throw caughtException[0]; @@ -1123,31 +1089,20 @@ private Method findRMMBMethod(String opMethodName, if (targetObjectField != null) return null; final Class rmmbClass = RequiredModelMBean.class; - final Class targetClass; + Class targetClass = null; if (opClassName == null) targetClass = rmmbClass; else { - @SuppressWarnings("removal") - AccessControlContext stack = AccessController.getContext(); - final String className = opClassName; - targetClass = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<>() { - - @Override - public Class run() { - try { - ReflectUtil.checkPackageAccess(className); - final ClassLoader targetClassLoader = - rmmbClass.getClassLoader(); - Class clz = Class.forName(className, false, - targetClassLoader); - if (!rmmbClass.isAssignableFrom(clz)) - return null; - return clz; - } catch (ClassNotFoundException e) { - return null; - } + final ClassLoader targetClassLoader = rmmbClass.getClassLoader(); + try { + Class clz = Class.forName(opClassName, false, targetClassLoader); + if (!rmmbClass.isAssignableFrom(clz)) { + targetClass = null; + } else { + targetClass = clz; } - }, stack, acc); + } catch (ClassNotFoundException e) { + } } try { return targetClass != null ? resolveMethod(targetClass, opMethodName, sig) : null; @@ -1165,21 +1120,12 @@ private Object invokeMethod(String opName, final Method method, throws MBeanException, ReflectionException { try { final Throwable[] caughtException = new Throwable[1]; - @SuppressWarnings("removal") - AccessControlContext stack = AccessController.getContext(); - Object rslt = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<>() { - - @Override - public Object run() { - try { - ReflectUtil.checkPackageAccess(method.getDeclaringClass()); - return MethodUtil.invoke(method, targetObject, opArgs); - } catch (InvocationTargetException | IllegalAccessException e) { - caughtException[0] = e; - } - return null; - } - }, stack, acc); + Object rslt = null; + try { + rslt = MethodUtil.invoke(method, targetObject, opArgs); + } catch (InvocationTargetException | IllegalAccessException e) { + caughtException[0] = e; + } if (caughtException[0] != null) { if (caughtException[0] instanceof Exception) { throw (Exception)caughtException[0]; @@ -1577,24 +1523,13 @@ public Object getAttribute(String attrName) final Class respClass = response.getClass(); final Exception[] caughException = new Exception[1]; - @SuppressWarnings("removal") - AccessControlContext stack = AccessController.getContext(); - - Class c = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<>() { - - @Override - public Class run() { - try { - ReflectUtil.checkPackageAccess(respType); - ClassLoader cl = - respClass.getClassLoader(); - return Class.forName(respType, true, cl); - } catch (Exception e) { - caughException[0] = e; - } - return null; - } - }, stack, acc); + ClassLoader cl = respClass.getClassLoader(); + Class c = null; + try { + c = Class.forName(respType, true, cl); + } catch (Exception e) { + caughException[0] = e; + } if (caughException[0] != null) { throw caughException[0]; @@ -2660,31 +2595,20 @@ protected ClassLoaderRepository getClassLoaderRepository() { private Class loadClass(final String className) throws ClassNotFoundException { - @SuppressWarnings("removal") - AccessControlContext stack = AccessController.getContext(); final ClassNotFoundException[] caughtException = new ClassNotFoundException[1]; - Class c = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<>() { - - @Override - public Class run() { - try { - ReflectUtil.checkPackageAccess(className); - return Class.forName(className); - } catch (ClassNotFoundException e) { - final ClassLoaderRepository clr = - getClassLoaderRepository(); - try { - if (clr == null) throw new ClassNotFoundException(className); - return clr.loadClass(className); - } catch (ClassNotFoundException ex) { - caughtException[0] = ex; - } - } - return null; + Class c = null; + try { + c = Class.forName(className); + } catch (ClassNotFoundException e) { + final ClassLoaderRepository clr = getClassLoaderRepository(); + try { + if (clr == null) throw new ClassNotFoundException(className); + return clr.loadClass(className); + } catch (ClassNotFoundException ex) { + caughtException[0] = ex; } - }, stack, acc); - + } if (caughtException[0] != null) { throw caughtException[0]; } diff --git a/src/java.management/share/classes/javax/management/modelmbean/XMLParseException.java b/src/java.management/share/classes/javax/management/modelmbean/XMLParseException.java index daa1abf039e..7d4773a49ea 100644 --- a/src/java.management/share/classes/javax/management/modelmbean/XMLParseException.java +++ b/src/java.management/share/classes/javax/management/modelmbean/XMLParseException.java @@ -31,13 +31,10 @@ package javax.management.modelmbean; -import com.sun.jmx.mbeanserver.GetPropertyAction; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; /** * This exception is thrown when an XML formatted string is being parsed into ModelMBean objects diff --git a/src/java.management/share/classes/javax/management/monitor/Monitor.java b/src/java.management/share/classes/javax/management/monitor/Monitor.java index 151855c3495..6c0b73a41fe 100644 --- a/src/java.management/share/classes/javax/management/monitor/Monitor.java +++ b/src/java.management/share/classes/javax/management/monitor/Monitor.java @@ -26,13 +26,9 @@ package javax.management.monitor; import static com.sun.jmx.defaults.JmxProperties.MONITOR_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Introspector; import java.io.IOException; -import java.security.AccessControlContext; -import java.security.AccessController; import java.security.PrivilegedAction; -import java.security.ProtectionDomain; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @@ -170,15 +166,9 @@ public final synchronized void setDerivedGaugeTimeStamp( new CopyOnWriteArrayList<>(); /** - * Subject and possibly AccessControlContext of the Monitor.start() caller. + * Subject of the Monitor.start() caller. */ private volatile Subject subject; - @SuppressWarnings("removal") - private static final AccessControlContext noPermissionsACC = - new AccessControlContext( - new ProtectionDomain[] {new ProtectionDomain(null, null)}); - @SuppressWarnings("removal") - private volatile AccessControlContext acc = noPermissionsACC; /** * Scheduler Service. @@ -204,9 +194,7 @@ public final synchronized void setDerivedGaugeTimeStamp( private static final int maximumPoolSize; static { final String maximumPoolSizeSysProp = "jmx.x.monitor.maximum.pool.size"; - @SuppressWarnings("removal") - final String maximumPoolSizeStr = AccessController.doPrivileged( - new GetPropertyAction(maximumPoolSizeSysProp)); + final String maximumPoolSizeStr = System.getProperty(maximumPoolSizeSysProp); if (maximumPoolSizeStr == null || maximumPoolSizeStr.trim().length() == 0) { maximumPoolSize = 10; @@ -749,10 +737,9 @@ void doStop() { // cleanupFutures(); - // Reset the Subject and AccessControlContext. + // Reset the Subject. // subject = null; - acc = noPermissionsACC; // Reset the complex type attribute information // such that it is recalculated again. @@ -1469,14 +1456,10 @@ private class MonitorTask implements Runnable { public MonitorTask() { // Find out if there's already an existing executor for the calling // thread and reuse it. Otherwise, create a new one and store it in - // the executors map. If there is a SecurityManager, the group of - // System.getSecurityManager() is used, else the group of the thread + // the executors map. Use the Thread group of the thread // instantiating this MonitorTask, i.e. the group of the thread that // calls "Monitor.start()". - @SuppressWarnings("removal") - SecurityManager s = System.getSecurityManager(); - ThreadGroup group = (s != null) ? s.getThreadGroup() : - Thread.currentThread().getThreadGroup(); + ThreadGroup group = Thread.currentThread().getThreadGroup(); synchronized (executorsLock) { for (ThreadPoolExecutor e : executors.keySet()) { DaemonThreadFactory tf = @@ -1556,8 +1539,7 @@ public Void run() { * Daemon thread factory used by the monitor executors. *

      * This factory creates all new threads used by an Executor in - * the same ThreadGroup. If there is a SecurityManager, it uses - * the group of System.getSecurityManager(), else the group of + * the same ThreadGroup. Use the Thread group of * the thread instantiating this DaemonThreadFactory. Each new * thread is created as a daemon thread with priority * Thread.NORM_PRIORITY. New threads have names accessible via @@ -1572,10 +1554,7 @@ private static class DaemonThreadFactory implements ThreadFactory { static final String nameSuffix = "]"; public DaemonThreadFactory(String poolName) { - @SuppressWarnings("removal") - SecurityManager s = System.getSecurityManager(); - group = (s != null) ? s.getThreadGroup() : - Thread.currentThread().getThreadGroup(); + group = Thread.currentThread().getThreadGroup(); namePrefix = "JMX Monitor " + poolName + " Pool [Thread-"; } diff --git a/src/java.management/share/classes/javax/management/openmbean/OpenMBeanAttributeInfoSupport.java b/src/java.management/share/classes/javax/management/openmbean/OpenMBeanAttributeInfoSupport.java index c8a6ca2f07a..4f26719d4f3 100644 --- a/src/java.management/share/classes/javax/management/openmbean/OpenMBeanAttributeInfoSupport.java +++ b/src/java.management/share/classes/javax/management/openmbean/OpenMBeanAttributeInfoSupport.java @@ -43,7 +43,6 @@ import javax.management.ImmutableDescriptor; import javax.management.MBeanAttributeInfo; import sun.reflect.misc.MethodUtil; -import sun.reflect.misc.ReflectUtil; /** * Describes an attribute of an open MBean. @@ -693,7 +692,6 @@ private static T convertFromString(String s, OpenType openType) { Class c; try { String className = openType.safeGetClassName(); - ReflectUtil.checkPackageAccess(className); c = cast(Class.forName(className)); } catch (ClassNotFoundException e) { throw new NoClassDefFoundError(e.toString()); // can't happen @@ -702,8 +700,6 @@ private static T convertFromString(String s, OpenType openType) { // Look for: public static T valueOf(String) Method valueOf; try { - // It is safe to call this plain Class.getMethod because the class "c" - // was checked before by ReflectUtil.checkPackageAccess(openType.safeGetClassName()); valueOf = c.getMethod("valueOf", String.class); if (!Modifier.isStatic(valueOf.getModifiers()) || valueOf.getReturnType() != c) @@ -724,8 +720,6 @@ private static T convertFromString(String s, OpenType openType) { // Look for: public T(String) Constructor con; try { - // It is safe to call this plain Class.getConstructor because the class "c" - // was checked before by ReflectUtil.checkPackageAccess(openType.safeGetClassName()); con = c.getConstructor(String.class); } catch (NoSuchMethodException e) { con = null; @@ -764,9 +758,6 @@ private static T convertFromStringArray(Object x, try { String baseClassName = baseType.safeGetClassName(); - // check access to the provided base type class name and bail out early - ReflectUtil.checkPackageAccess(baseClassName); - stringArrayClass = Class.forName(squareBrackets + "Ljava.lang.String;"); targetArrayClass = diff --git a/src/java.management/share/classes/javax/management/openmbean/OpenType.java b/src/java.management/share/classes/javax/management/openmbean/OpenType.java index f2a004dab82..a81359a7fe9 100644 --- a/src/java.management/share/classes/javax/management/openmbean/OpenType.java +++ b/src/java.management/share/classes/javax/management/openmbean/OpenType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -25,13 +25,10 @@ package javax.management.openmbean; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -193,14 +190,11 @@ protected OpenType(String className, this.isArray = isArray; } - @SuppressWarnings("removal") private void checkClassNameOverride() throws SecurityException { if (this.getClass().getClassLoader() == null) return; // We trust bootstrap classes. if (overridesGetClassName(this.getClass())) { - final GetPropertyAction getExtendOpenTypes = - new GetPropertyAction("jmx.extend.open.types"); - if (AccessController.doPrivileged(getExtendOpenTypes) == null) { + if (System.getProperty("jmx.extend.open.types") == null) { throw new SecurityException("Cannot override getClassName() " + "unless -Djmx.extend.open.types"); } @@ -209,16 +203,11 @@ private void checkClassNameOverride() throws SecurityException { @SuppressWarnings("removal") private static boolean overridesGetClassName(final Class c) { - return AccessController.doPrivileged(new PrivilegedAction<>() { - public Boolean run() { - try { - return (c.getMethod("getClassName").getDeclaringClass() != - OpenType.class); - } catch (Exception e) { - return true; // fail safe - } - } - }); + try { + return (c.getMethod("getClassName").getDeclaringClass() != OpenType.class); + } catch (Exception e) { + return true; // fail safe + } } private static String validClassName(String className) throws OpenDataException { diff --git a/src/java.management/share/classes/javax/management/openmbean/TabularDataSupport.java b/src/java.management/share/classes/javax/management/openmbean/TabularDataSupport.java index 00f255f394b..94c804d9029 100644 --- a/src/java.management/share/classes/javax/management/openmbean/TabularDataSupport.java +++ b/src/java.management/share/classes/javax/management/openmbean/TabularDataSupport.java @@ -26,12 +26,10 @@ package javax.management.openmbean; -import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.security.AccessController; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -148,10 +146,7 @@ public TabularDataSupport(TabularType tabularType, int initialCapacity, float lo // Since LinkedHashMap was introduced in SE 1.4, it's conceivable even // if very unlikely that we might be the server of a 1.3 client. In // that case you'll need to set this property. See CR 6334663. - @SuppressWarnings("removal") - String useHashMapProp = AccessController.doPrivileged( - new GetPropertyAction("jmx.tabular.data.hash.map")); - boolean useHashMap = "true".equalsIgnoreCase(useHashMapProp); + boolean useHashMap = Boolean.getBoolean("jmx.tabular.data.hash.map"); // Construct the empty contents HashMap // diff --git a/src/java.management/share/classes/javax/management/relation/MBeanServerNotificationFilter.java b/src/java.management/share/classes/javax/management/relation/MBeanServerNotificationFilter.java index 7579e416e36..077fce5786e 100644 --- a/src/java.management/share/classes/javax/management/relation/MBeanServerNotificationFilter.java +++ b/src/java.management/share/classes/javax/management/relation/MBeanServerNotificationFilter.java @@ -27,13 +27,11 @@ import static com.sun.jmx.mbeanserver.Util.cast; import static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; import java.util.List; import java.util.Vector; diff --git a/src/java.management/share/classes/javax/management/relation/RelationNotification.java b/src/java.management/share/classes/javax/management/relation/RelationNotification.java index 16d660bc07f..d1849795eb7 100644 --- a/src/java.management/share/classes/javax/management/relation/RelationNotification.java +++ b/src/java.management/share/classes/javax/management/relation/RelationNotification.java @@ -34,8 +34,6 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -43,7 +41,6 @@ import java.util.List; import java.util.Set; -import com.sun.jmx.mbeanserver.GetPropertyAction; import static com.sun.jmx.mbeanserver.Util.cast; /** diff --git a/src/java.management/share/classes/javax/management/relation/RelationTypeSupport.java b/src/java.management/share/classes/javax/management/relation/RelationTypeSupport.java index 766c42ca780..e4b221b1d68 100644 --- a/src/java.management/share/classes/javax/management/relation/RelationTypeSupport.java +++ b/src/java.management/share/classes/javax/management/relation/RelationTypeSupport.java @@ -27,15 +27,12 @@ import static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER; import static com.sun.jmx.mbeanserver.Util.cast; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; -import java.security.AccessController; - import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; diff --git a/src/java.management/share/classes/javax/management/relation/Role.java b/src/java.management/share/classes/javax/management/relation/Role.java index 431092a3b48..7fd90f69b7d 100644 --- a/src/java.management/share/classes/javax/management/relation/Role.java +++ b/src/java.management/share/classes/javax/management/relation/Role.java @@ -26,7 +26,6 @@ package javax.management.relation; import static com.sun.jmx.mbeanserver.Util.cast; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; @@ -34,8 +33,6 @@ import java.io.ObjectStreamField; import java.io.Serializable; -import java.security.AccessController; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; diff --git a/src/java.management/share/classes/javax/management/relation/RoleInfo.java b/src/java.management/share/classes/javax/management/relation/RoleInfo.java index 6f2926005e9..9c75cda273f 100644 --- a/src/java.management/share/classes/javax/management/relation/RoleInfo.java +++ b/src/java.management/share/classes/javax/management/relation/RoleInfo.java @@ -25,15 +25,11 @@ package javax.management.relation; - -import com.sun.jmx.mbeanserver.GetPropertyAction; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.io.Serializable; -import java.security.AccessController; import javax.management.MBeanServer; diff --git a/src/java.management/share/classes/javax/management/relation/RoleResult.java b/src/java.management/share/classes/javax/management/relation/RoleResult.java index 7f98a7f7a24..c372f47cd77 100644 --- a/src/java.management/share/classes/javax/management/relation/RoleResult.java +++ b/src/java.management/share/classes/javax/management/relation/RoleResult.java @@ -25,17 +25,12 @@ package javax.management.relation; - -import com.sun.jmx.mbeanserver.GetPropertyAction; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.io.Serializable; -import java.security.AccessController; - /** * Represents the result of a multiple access to several roles of a relation * (either for reading or writing). diff --git a/src/java.management/share/classes/javax/management/relation/RoleUnresolved.java b/src/java.management/share/classes/javax/management/relation/RoleUnresolved.java index 36b6dff9d71..435f68011c2 100644 --- a/src/java.management/share/classes/javax/management/relation/RoleUnresolved.java +++ b/src/java.management/share/classes/javax/management/relation/RoleUnresolved.java @@ -26,7 +26,6 @@ package javax.management.relation; import static com.sun.jmx.mbeanserver.Util.cast; -import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; @@ -34,8 +33,6 @@ import java.io.ObjectStreamField; import java.io.Serializable; -import java.security.AccessController; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; diff --git a/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java b/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java index e5c0907a7b4..459a00f88cf 100644 --- a/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java +++ b/src/java.management/share/classes/javax/management/remote/JMXConnectorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -37,13 +37,9 @@ import java.util.StringTokenizer; import java.util.function.Predicate; import java.util.stream.Stream; -import java.security.AccessController; -import java.security.PrivilegedAction; import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; -import sun.reflect.misc.ReflectUtil; - /** *

      Factory to create JMX API connector clients. There @@ -388,12 +384,7 @@ private static String resolvePkgs(Map env) pkgsObject = env.get(PROTOCOL_PROVIDER_PACKAGES); if (pkgsObject == null) - pkgsObject = - AccessController.doPrivileged(new PrivilegedAction() { - public String run() { - return System.getProperty(PROTOCOL_PROVIDER_PACKAGES); - } - }); + pkgsObject = System.getProperty(PROTOCOL_PROVIDER_PACKAGES); if (pkgsObject == null) return null; @@ -447,20 +438,12 @@ static T getProvider(JMXServiceURL serviceURL, return instance; } - @SuppressWarnings("removal") private static ClassLoader wrap(final ClassLoader parent) { - return parent != null ? AccessController.doPrivileged(new PrivilegedAction<>() { - @Override - public ClassLoader run() { - return new ClassLoader(parent) { - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - ReflectUtil.checkPackageAccess(name); - return super.loadClass(name, resolve); - } - }; - } - }) : null; + return parent != null ? new ClassLoader(parent) { + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException + { return super.loadClass(name, resolve); } + } + : null; } /** diff --git a/src/java.management/share/classes/sun/management/ClassLoadingImpl.java b/src/java.management/share/classes/sun/management/ClassLoadingImpl.java index 9c3c62158b7..855b3e97575 100644 --- a/src/java.management/share/classes/sun/management/ClassLoadingImpl.java +++ b/src/java.management/share/classes/sun/management/ClassLoadingImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -64,8 +64,6 @@ public boolean isVerbose() { } public void setVerbose(boolean value) { - Util.checkControlAccess(); - setVerboseClass(value); } static native void setVerboseClass(boolean value); diff --git a/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java b/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java index 3746cd311b2..a0291045654 100644 --- a/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java +++ b/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -36,9 +36,6 @@ import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.RuntimeOperationsException; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import jdk.internal.misc.VM; import jdk.internal.misc.VM.BufferPool; @@ -47,7 +44,6 @@ import java.util.List; import java.lang.reflect.UndeclaredThrowableException; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -191,18 +187,13 @@ static boolean isAvailable() { return LOG_MANAGER_CLASS != null; } - @SuppressWarnings("removal") private static Class loadLoggingClass(String className) { - return AccessController.doPrivileged(new PrivilegedAction<>() { - @Override - public Class run() { - Optional logging = ModuleLayer.boot().findModule("java.logging"); - if (logging.isPresent()) { - return Class.forName(logging.get(), className); - } - return null; - } - }); + Optional logging = ModuleLayer.boot().findModule("java.logging"); + if (logging.isPresent()) { + return Class.forName(logging.get(), className); + } else { + return null; + } } private Map initMethodMap(Object impl) { @@ -460,29 +451,15 @@ public static synchronized HotspotCompilationMBean getHotspotCompilationMBean() * Registers a given MBean if not registered in the MBeanServer; * otherwise, just return. */ - @SuppressWarnings("removal") private static void addMBean(MBeanServer mbs, Object mbean, String mbeanName) { + final ObjectName objName = Util.newObjectName(mbeanName); + try { - final ObjectName objName = Util.newObjectName(mbeanName); - - // inner class requires these fields to be final - final MBeanServer mbs0 = mbs; - final Object mbean0 = mbean; - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Void run() throws MBeanRegistrationException, - NotCompliantMBeanException { - try { - mbs0.registerMBean(mbean0, objName); - return null; - } catch (InstanceAlreadyExistsException e) { - // if an instance with the object name exists in - // the MBeanServer ignore the exception - } - return null; - } - }); - } catch (PrivilegedActionException e) { - throw new RuntimeException(e.getException()); + mbs.registerMBean(mbean, objName); + } catch (InstanceAlreadyExistsException iaee) { + // if an instance with the object name exists in the MBeanServer, ignore the exception + } catch (Exception e) { + throw new RuntimeException(e); } } @@ -521,26 +498,15 @@ static void registerInternalMBeans(MBeanServer mbs) { } } - @SuppressWarnings("removal") private static void unregisterMBean(MBeanServer mbs, String mbeanName) { + final ObjectName objName = Util.newObjectName(mbeanName); + try { - final ObjectName objName = Util.newObjectName(mbeanName); - - // inner class requires these fields to be final - final MBeanServer mbs0 = mbs; - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Void run() throws MBeanRegistrationException, - RuntimeOperationsException { - try { - mbs0.unregisterMBean(objName); - } catch (InstanceNotFoundException e) { - // ignore exception if not found - } - return null; - } - }); - } catch (PrivilegedActionException e) { - throw new RuntimeException(e.getException()); + mbs.unregisterMBean(objName); + } catch (InstanceNotFoundException infe) { + // ignore exception if not found + } catch (Exception e) { + throw new RuntimeException(e); } } diff --git a/src/java.management/share/classes/sun/management/MappedMXBeanType.java b/src/java.management/share/classes/sun/management/MappedMXBeanType.java index 25ae6321b31..3e83ea35545 100644 --- a/src/java.management/share/classes/sun/management/MappedMXBeanType.java +++ b/src/java.management/share/classes/sun/management/MappedMXBeanType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -35,10 +35,6 @@ import java.util.Map; import java.util.*; import java.io.InvalidObjectException; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import javax.management.openmbean.*; import static javax.management.openmbean.SimpleType.*; @@ -610,19 +606,14 @@ static class CompositeDataMXBeanType extends MappedMXBeanType { Method fromMethod = null; Method toMethod = null; - @SuppressWarnings("removal") CompositeDataMXBeanType(Class c) throws OpenDataException { this.javaClass = c; this.mappedTypeClass = COMPOSITE_DATA_CLASS; // check if a static from method exists try { - fromMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<>() { - public Method run() throws NoSuchMethodException { - return javaClass.getMethod("from", COMPOSITE_DATA_CLASS); - } - }); - } catch (PrivilegedActionException e) { + fromMethod = javaClass.getMethod("from", COMPOSITE_DATA_CLASS); + } catch (NoSuchMethodException e) { // ignore NoSuchMethodException since we allow classes // that has no from method to be embedded in another class. } @@ -637,12 +628,7 @@ public Method run() throws NoSuchMethodException { this.isCompositeData = false; // Make a CompositeData containing all the getters - final Method[] methods = - AccessController.doPrivileged(new PrivilegedAction<>() { - public Method[] run() { - return javaClass.getMethods(); - } - }); + final Method[] methods = javaClass.getMethods(); final List names = new ArrayList<>(); final List> types = new ArrayList<>(); diff --git a/src/java.management/share/classes/sun/management/MemoryImpl.java b/src/java.management/share/classes/sun/management/MemoryImpl.java index 5bdbe7b6349..e1f68ce3711 100644 --- a/src/java.management/share/classes/sun/management/MemoryImpl.java +++ b/src/java.management/share/classes/sun/management/MemoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -81,8 +81,6 @@ public boolean isVerbose() { } public void setVerbose(boolean value) { - Util.checkControlAccess(); - setVerboseGC(value); } diff --git a/src/java.management/share/classes/sun/management/MemoryPoolImpl.java b/src/java.management/share/classes/sun/management/MemoryPoolImpl.java index 3b8197bb8f1..defcecf7b21 100644 --- a/src/java.management/share/classes/sun/management/MemoryPoolImpl.java +++ b/src/java.management/share/classes/sun/management/MemoryPoolImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -112,8 +112,6 @@ public void setUsageThreshold(long newThreshold) { "Usage threshold is not supported"); } - Util.checkControlAccess(); - MemoryUsage usage = getUsage0(); if (newThreshold < 0) { throw new IllegalArgumentException( @@ -157,8 +155,6 @@ public String[] getMemoryManagerNames() { } public void resetPeakUsage() { - Util.checkControlAccess(); - synchronized (this) { // synchronized since getPeakUsage may be called concurrently resetPeakUsage0(); @@ -209,8 +205,6 @@ public void setCollectionUsageThreshold(long newThreshold) { "CollectionUsage threshold is not supported"); } - Util.checkControlAccess(); - MemoryUsage usage = getUsage0(); if (newThreshold < 0) { throw new IllegalArgumentException( diff --git a/src/java.management/share/classes/sun/management/RuntimeImpl.java b/src/java.management/share/classes/sun/management/RuntimeImpl.java index c919c0eeddf..a7550650f94 100644 --- a/src/java.management/share/classes/sun/management/RuntimeImpl.java +++ b/src/java.management/share/classes/sun/management/RuntimeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -101,7 +101,6 @@ public String getBootClassPath() { } public List getInputArguments() { - Util.checkMonitorAccess(); return jvm.getVmArguments(); } diff --git a/src/java.management/share/classes/sun/management/ThreadImpl.java b/src/java.management/share/classes/sun/management/ThreadImpl.java index 1f37b2da567..be54ced066d 100644 --- a/src/java.management/share/classes/sun/management/ThreadImpl.java +++ b/src/java.management/share/classes/sun/management/ThreadImpl.java @@ -129,7 +129,6 @@ protected boolean isThreadAllocatedMemoryEnabled() { @Override public long[] getAllThreadIds() { - Util.checkMonitorAccess(); Thread[] threads = getThreads(); return threadIds(threads); } @@ -178,8 +177,6 @@ public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) { // an empty array of ids should return an empty array of ThreadInfos if (ids.length == 0) return new ThreadInfo[0]; - Util.checkMonitorAccess(); - ThreadInfo[] infos = new ThreadInfo[ids.length]; // nulls if (maxDepth == Integer.MAX_VALUE) { getThreadInfo1(ids, -1, infos); @@ -196,8 +193,6 @@ public void setThreadContentionMonitoringEnabled(boolean enable) { "Thread contention monitoring is not supported"); } - Util.checkControlAccess(); - synchronized (this) { if (contentionMonitoringEnabled != enable) { if (enable) { @@ -332,7 +327,6 @@ public void setThreadCpuTimeEnabled(boolean enable) { "Thread CPU time measurement is not supported"); } - Util.checkControlAccess(); synchronized (this) { if (cpuTimeEnabled != enable) { // notify VM of the state change @@ -401,7 +395,6 @@ protected long[] getThreadAllocatedBytes(long[] ids) { protected void setThreadAllocatedMemoryEnabled(boolean enable) { ensureThreadAllocatedMemorySupported(); - Util.checkControlAccess(); synchronized (this) { if (allocatedMemoryEnabled != enable) { // notify VM of the state change @@ -426,7 +419,6 @@ private long[] threadsToIds(Thread[] threads) { @Override public long[] findMonitorDeadlockedThreads() { - Util.checkMonitorAccess(); Thread[] threads = findMonitorDeadlockedThreads0(); return threadsToIds(threads); } @@ -438,15 +430,12 @@ public long[] findDeadlockedThreads() { "Monitoring of Synchronizer Usage is not supported."); } - Util.checkMonitorAccess(); - Thread[] threads = findDeadlockedThreads0(); return threadsToIds(threads); } @Override public void resetPeakThreadCount() { - Util.checkControlAccess(); resetPeakThreadCount0(); } @@ -471,8 +460,6 @@ private void verifyDumpThreads(boolean lockedMonitors, throw new UnsupportedOperationException( "Monitoring of Synchronizer Usage is not supported."); } - - Util.checkMonitorAccess(); } @Override diff --git a/src/java.management/share/classes/sun/management/Util.java b/src/java.management/share/classes/sun/management/Util.java index e5a25792263..cad5a2e3892 100644 --- a/src/java.management/share/classes/sun/management/Util.java +++ b/src/java.management/share/classes/sun/management/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -50,34 +50,15 @@ public static ObjectName newObjectName(String name) { } } - private static ManagementPermission monitorPermission = - new ManagementPermission("monitor"); - private static ManagementPermission controlPermission = - new ManagementPermission("control"); - - /** - * Check that the current context is trusted to perform monitoring - * or management. - *

      - * If the check fails we throw a SecurityException, otherwise - * we return normally. - * - * @exception SecurityException if a security manager exists and if - * the caller does not have ManagementPermission("control"). - */ - static void checkAccess(ManagementPermission p) - throws SecurityException { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(p); - } + // Methods retained temporarily due to usage by jdk.management. + static void checkAccess(ManagementPermission p) { + // no-op } static void checkMonitorAccess() throws SecurityException { - checkAccess(monitorPermission); + // no-op } public static void checkControlAccess() throws SecurityException { - checkAccess(controlPermission); + // no-op } } diff --git a/src/java.management/share/classes/sun/management/VMManagementImpl.java b/src/java.management/share/classes/sun/management/VMManagementImpl.java index 7e1e870acb4..041f09547d2 100644 --- a/src/java.management/share/classes/sun/management/VMManagementImpl.java +++ b/src/java.management/share/classes/sun/management/VMManagementImpl.java @@ -200,7 +200,7 @@ public synchronized List getVmArguments() { public native int getAvailableProcessors(); // Compilation Subsystem - public String getCompilerName() { + public String getCompilerName() { return System.getProperty("sun.management.compiler"); } public native long getTotalCompileTime(); From 6c1ed0bb6912d8085b98f50b7bdbb467369f3992 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Wed, 4 Dec 2024 14:32:47 +0000 Subject: [PATCH 122/171] 8345474: Translation for instanceof is not triggered when patterns are not used in the compilation unit Reviewed-by: mcimadamore --- .../sun/tools/javac/main/JavaCompiler.java | 8 +++ .../tools/javac/patterns/T8345474.java | 49 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/langtools/tools/javac/patterns/T8345474.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 9600e4f6e4f..274ac029b92 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -97,6 +97,7 @@ import static com.sun.tools.javac.code.TypeTag.CLASS; import static com.sun.tools.javac.main.Option.*; import com.sun.tools.javac.tree.JCTree.JCBindingPattern; +import com.sun.tools.javac.tree.JCTree.JCInstanceOf; import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; import static javax.tools.StandardLocation.CLASS_OUTPUT; @@ -1550,6 +1551,13 @@ public void visitBindingPattern(JCBindingPattern tree) { super.visitBindingPattern(tree); } @Override + public void visitTypeTest(JCInstanceOf tree) { + if (tree.pattern.type.isPrimitive()) { + hasPatterns = true; + } + super.visitTypeTest(tree); + } + @Override public void visitRecordPattern(JCRecordPattern that) { hasPatterns = true; super.visitRecordPattern(that); diff --git a/test/langtools/tools/javac/patterns/T8345474.java b/test/langtools/tools/javac/patterns/T8345474.java new file mode 100644 index 00000000000..68a0c88a501 --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8345474.java @@ -0,0 +1,49 @@ +/* + * 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 8345474 + * @summary Translation for instanceof is not triggered when patterns are not used in the compilation unit + * @enablePreview + * @compile T8345474.java + * @run main T8345474 + */ +import java.util.List; + +public class T8345474 { + public static void main(String[] args) { + erasureInstanceofTypeComparisonOperator(); + } + + public static void erasureInstanceofTypeComparisonOperator() { + List ls = List.of((short) 42); + + assertTrue(ls.get(0) instanceof int); + } + + static void assertTrue(boolean actual) { + if (!actual) { + throw new AssertionError("Expected: true, but got false"); + } + } +} From 6f307623568efe4d90942cd22ec9a26b2e1ca1b1 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 4 Dec 2024 15:34:11 +0000 Subject: [PATCH 123/171] 8345468: test/jdk/javax/swing/JScrollBar/4865918/bug4865918.java fails in ubuntu22.04 Reviewed-by: abhiscxk --- .../swing/JScrollBar/4865918/bug4865918.java | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/test/jdk/javax/swing/JScrollBar/4865918/bug4865918.java b/test/jdk/javax/swing/JScrollBar/4865918/bug4865918.java index eba58ef0649..ea20840d862 100644 --- a/test/jdk/javax/swing/JScrollBar/4865918/bug4865918.java +++ b/test/jdk/javax/swing/JScrollBar/4865918/bug4865918.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -26,14 +26,17 @@ * @key headful * @bug 4865918 * @summary REGRESSION:JCK1.4a-runtime api/javax_swing/interactive/JScrollBarTests.html#JScrollBar - * @author Andrey Pikalev * @run main bug4865918 */ -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import java.util.*; +import java.awt.Dimension; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JScrollBar; +import javax.swing.SwingUtilities; +import java.awt.event.MouseEvent; + +import java.util.Date; public class bug4865918 { @@ -43,29 +46,18 @@ public class bug4865918 { public static void main(String[] argv) throws Exception { try { Robot robot = new Robot(); - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - createAndShowGUI(); - } - }); + SwingUtilities.invokeAndWait(() -> createAndShowGUI()); robot.waitForIdle(); + robot.delay(1000); - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - sbar.pressMouse(); - } - }); + SwingUtilities.invokeAndWait(() -> sbar.pressMouse()); robot.waitForIdle(); + robot.delay(200); - int value = getValue(); - - if (value != 9) { - throw new Error("The scrollbar block increment is incorect"); + if (getValue() != 9) { + throw new RuntimeException("The scrollbar block increment is incorrect"); } } finally { if (frame != null) SwingUtilities.invokeAndWait(() -> frame.dispose()); @@ -75,11 +67,8 @@ public void run() { private static int getValue() throws Exception { final int[] result = new int[1]; - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - result[0] = sbar.getValue(); - } + SwingUtilities.invokeAndWait(() -> { + result[0] = sbar.getValue(); }); return result[0]; @@ -95,8 +84,9 @@ private static void createAndShowGUI() { frame.getContentPane().add(sbar); frame.pack(); + frame.setLocationRelativeTo(null); frame.setVisible(true); - + frame.toFront(); } static class TestScrollBar extends JScrollBar { @@ -111,7 +101,7 @@ public void pressMouse() { MouseEvent me = new MouseEvent(sbar, MouseEvent.MOUSE_PRESSED, (new Date()).getTime(), - MouseEvent.BUTTON1_MASK, + MouseEvent.BUTTON1_DOWN_MASK, 3 * getWidth() / 4, getHeight() / 2, 1, true); processMouseEvent(me); From 923321cfb1a9c66ca0e8f843ff029fd161a19b5b Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 4 Dec 2024 15:35:27 +0000 Subject: [PATCH 124/171] 8345447: test/jdk/javax/swing/JToolBar/4529206/bug4529206.java fails in ubuntu22.04 Reviewed-by: abhiscxk --- .../javax/swing/JToolBar/4529206/bug4529206.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java b/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java index ed4f062b24c..56f4a224e3b 100644 --- a/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java +++ b/test/jdk/javax/swing/JToolBar/4529206/bug4529206.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -30,6 +30,7 @@ import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicToolBarUI; /* * @test @@ -45,7 +46,7 @@ public class bug4529206 { static JButton jButton1; private static void test() { - frame = new JFrame(); + frame = new JFrame("bug4529206"); JPanel jPanFrame = (JPanel) frame.getContentPane(); jPanFrame.setLayout(new BorderLayout()); frame.setSize(new Dimension(200, 100)); @@ -64,7 +65,7 @@ private static void test() { } private static void makeToolbarFloat() { - javax.swing.plaf.basic.BasicToolBarUI ui = (javax.swing.plaf.basic.BasicToolBarUI) jToolBar1.getUI(); + BasicToolBarUI ui = (BasicToolBarUI) jToolBar1.getUI(); if (!ui.isFloating()) { ui.setFloatingLocation(100, 100); ui.setFloating(true, jToolBar1.getLocation()); @@ -79,16 +80,17 @@ public static void main(String[] args) throws Exception { try { SwingUtilities.invokeAndWait(() -> test()); Robot robot = new Robot(); - robot.setAutoWaitForIdle(true); + robot.waitForIdle(); robot.delay(1000); SwingUtilities.invokeAndWait(() -> makeToolbarFloat()); + robot.waitForIdle(); robot.delay(300); SwingUtilities.invokeAndWait(() -> { if (frame.isFocused()) { - throw - new RuntimeException("setFloating does not work correctly"); + throw new RuntimeException( + "setFloating does not work correctly"); } }); } finally { From 16ef6e2a187181f49b7b2d601c660bbd25ab3845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 4 Dec 2024 15:38:17 +0000 Subject: [PATCH 125/171] 8344229: Revisit SecurityManager usage in jdk.httpserver after JEP 486 integration Reviewed-by: dfuchs --- .../httpserver/spi/HttpServerProvider.java | 48 ++--- .../sun/net/httpserver/AuthFilter.java | 8 +- .../sun/net/httpserver/HttpServerImpl.java | 9 +- .../sun/net/httpserver/HttpsServerImpl.java | 9 +- .../sun/net/httpserver/ServerConfig.java | 183 +++++++----------- .../sun/net/httpserver/ServerImpl.java | 12 +- .../simpleserver/FileServerHandler.java | 12 -- .../httpserver/simpleserver/JWebServer.java | 14 +- .../sun/net/httpserver/FileServerHandler.java | 8 +- .../HttpsParametersClientAuthTest.java | 26 +-- .../jdk/com/sun/net/httpserver/LogFilter.java | 10 +- .../com/sun/net/httpserver/SelCacheTest.java | 5 +- .../sun/net/httpserver/SimpleFileServer.java | 6 +- test/jdk/com/sun/net/httpserver/Test14.java | 7 +- test/jdk/com/sun/net/httpserver/Test2.java | 7 +- test/jdk/com/sun/net/httpserver/Test3.java | 6 +- test/jdk/com/sun/net/httpserver/Test4.java | 5 +- test/jdk/com/sun/net/httpserver/Test5.java | 5 +- test/jdk/com/sun/net/httpserver/Test6.java | 7 +- test/jdk/com/sun/net/httpserver/Test7.java | 7 +- test/jdk/com/sun/net/httpserver/Test8.java | 7 +- .../com/sun/net/httpserver/TestLogging.java | 7 +- .../com/sun/net/httpserver/bugs/B6339483.java | 7 +- .../com/sun/net/httpserver/bugs/B6341616.java | 7 +- .../com/sun/net/httpserver/bugs/B6526158.java | 7 +- .../com/sun/net/httpserver/bugs/B6526913.java | 7 +- .../com/sun/net/httpserver/bugs/B6529200.java | 6 +- .../com/sun/net/httpserver/bugs/B6744329.java | 7 +- .../simpleserver/RootDirPermissionsTest.java | 3 +- 29 files changed, 135 insertions(+), 317 deletions(-) diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java index e86f1944cd3..d025e90cf87 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/spi/HttpServerProvider.java @@ -31,8 +31,6 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Iterator; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; @@ -83,12 +81,7 @@ public abstract HttpsServer createHttpsServer(InetSocketAddress addr, /** * Initializes a new instance of this class. */ - protected HttpServerProvider() { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) - sm.checkPermission(new RuntimePermission("httpServerProvider")); - } + protected HttpServerProvider() {} private static boolean loadProviderFromProperty() { String cn = System.getProperty("com.sun.net.httpserver.HttpServerProvider"); @@ -107,8 +100,7 @@ private static boolean loadProviderFromProperty() { NoSuchMethodException | ClassNotFoundException | IllegalAccessException | - InstantiationException | - SecurityException x) { + InstantiationException x) { throw new ServiceConfigurationError(null, x); } } @@ -118,20 +110,10 @@ private static boolean loadProviderAsService() { ServiceLoader.load(HttpServerProvider.class, ClassLoader.getSystemClassLoader()) .iterator(); - for (;;) { - try { - if (!i.hasNext()) - return false; - provider = i.next(); - return true; - } catch (ServiceConfigurationError sce) { - if (sce.getCause() instanceof SecurityException) { - // Ignore the security exception, try the next provider - continue; - } - throw sce; - } - } + if (!i.hasNext()) + return false; + provider = i.next(); + return true; } /** @@ -170,22 +152,16 @@ private static boolean loadProviderAsService() { * * @return The system-wide default HttpServerProvider */ - @SuppressWarnings("removal") public static HttpServerProvider provider () { synchronized (lock) { if (provider != null) return provider; - return (HttpServerProvider)AccessController - .doPrivileged(new PrivilegedAction() { - public Object run() { - if (loadProviderFromProperty()) - return provider; - if (loadProviderAsService()) - return provider; - provider = new sun.net.httpserver.DefaultHttpServerProvider(); - return provider; - } - }); + if (loadProviderFromProperty()) + return provider; + if (loadProviderAsService()) + return provider; + provider = new sun.net.httpserver.DefaultHttpServerProvider(); + return provider; } } diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/AuthFilter.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/AuthFilter.java index d56ae8aa94d..a6f03fdc542 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/AuthFilter.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/AuthFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -27,12 +27,6 @@ import com.sun.net.httpserver.*; import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.*; -import javax.security.auth.*; -import javax.security.auth.callback.*; -import javax.security.auth.login.*; public class AuthFilter extends Filter { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpServerImpl.java index e6e0ad85759..dd09957c0c6 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpServerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -27,14 +27,9 @@ import java.net.*; import java.io.*; -import java.nio.*; -import java.security.*; -import java.nio.channels.*; -import java.util.*; import java.util.concurrent.*; -import javax.net.ssl.*; + import com.sun.net.httpserver.*; -import com.sun.net.httpserver.spi.*; public class HttpServerImpl extends HttpServer { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpsServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpsServerImpl.java index f6b94db7c0e..97312ec29e6 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpsServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/HttpsServerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -27,14 +27,9 @@ import java.net.*; import java.io.*; -import java.nio.*; -import java.security.*; -import java.nio.channels.*; -import java.util.*; import java.util.concurrent.*; -import javax.net.ssl.*; + import com.sun.net.httpserver.*; -import com.sun.net.httpserver.spi.*; public class HttpsServerImpl extends HttpsServer { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java index 9186dd4c168..3eda0eb1a48 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerConfig.java @@ -27,14 +27,11 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; -import java.security.PrivilegedAction; /** * Parameters that users will not likely need to set * but are useful for debugging */ - -@SuppressWarnings("removal") class ServerConfig { private static final int DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS = 10000 ; // 10 sec. @@ -52,94 +49,73 @@ class ServerConfig { private static final int DEFAULT_MAX_REQ_HEADER_SIZE = 380 * 1024; private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024; - private static long idleTimerScheduleMillis; - private static long idleIntervalMillis; + private static final long idleTimerScheduleMillis; + private static final long idleIntervalMillis; // The maximum number of bytes to drain from an inputstream - private static long drainAmount; + private static final long drainAmount; // the maximum number of connections that the server will allow to be open // after which it will no longer "accept()" any new connections, till the // current connection count goes down due to completion of processing the requests - private static int maxConnections; - private static int maxIdleConnections; + private static final int maxConnections; + private static final int maxIdleConnections; // The maximum number of request headers allowable - private static int maxReqHeaders; + private static final int maxReqHeaders; // a maximum value for the header list size. This is the // names size + values size + 32 bytes per field line - private static int maxReqHeadersSize; + private static final int maxReqHeadersSize; // max time a request or response is allowed to take - private static long maxReqTime; - private static long maxRspTime; - private static long reqRspTimerScheduleMillis; - private static boolean debug; + private static final long maxReqTime; + private static final long maxRspTime; + private static final long reqRspTimerScheduleMillis; + private static final boolean debug; // the value of the TCP_NODELAY socket-level option - private static boolean noDelay; + private static final boolean noDelay; static { - java.security.AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Void run () { - idleIntervalMillis = Long.getLong("sun.net.httpserver.idleInterval", - DEFAULT_IDLE_INTERVAL_IN_SECS) * 1000; - if (idleIntervalMillis <= 0) { - idleIntervalMillis = DEFAULT_IDLE_INTERVAL_IN_SECS * 1000; - } - - idleTimerScheduleMillis = Long.getLong("sun.net.httpserver.clockTick", - DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS); - if (idleTimerScheduleMillis <= 0) { - // ignore zero or negative value and use the default schedule - idleTimerScheduleMillis = DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS; - } - - maxConnections = Integer.getInteger( - "jdk.httpserver.maxConnections", - DEFAULT_MAX_CONNECTIONS); - - maxIdleConnections = Integer.getInteger( - "sun.net.httpserver.maxIdleConnections", - DEFAULT_MAX_IDLE_CONNECTIONS); - - drainAmount = Long.getLong("sun.net.httpserver.drainAmount", - DEFAULT_DRAIN_AMOUNT); - - maxReqHeaders = Integer.getInteger( - "sun.net.httpserver.maxReqHeaders", - DEFAULT_MAX_REQ_HEADERS); - if (maxReqHeaders <= 0) { - maxReqHeaders = DEFAULT_MAX_REQ_HEADERS; - } - - // a value <= 0 means unlimited - maxReqHeadersSize = Integer.getInteger( - "sun.net.httpserver.maxReqHeaderSize", - DEFAULT_MAX_REQ_HEADER_SIZE); - if (maxReqHeadersSize <= 0) { - maxReqHeadersSize = 0; - } - - maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime", - DEFAULT_MAX_REQ_TIME); - - maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime", - DEFAULT_MAX_RSP_TIME); - - reqRspTimerScheduleMillis = Long.getLong("sun.net.httpserver.timerMillis", - DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS); - if (reqRspTimerScheduleMillis <= 0) { - // ignore any negative or zero value for this configuration and reset - // to default schedule - reqRspTimerScheduleMillis = DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS; - } - - debug = Boolean.getBoolean("sun.net.httpserver.debug"); - - noDelay = Boolean.getBoolean("sun.net.httpserver.nodelay"); - - return null; - } - }); + + long providedIdleIntervalMillis = + Long.getLong("sun.net.httpserver.idleInterval", DEFAULT_IDLE_INTERVAL_IN_SECS) * 1000; + idleIntervalMillis = providedIdleIntervalMillis > 0 + ? providedIdleIntervalMillis + : Math.multiplyExact(DEFAULT_IDLE_INTERVAL_IN_SECS, 1000); + + long providedIdleTimerScheduleMillis = + Long.getLong("sun.net.httpserver.clockTick", DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS); + // Ignore zero or negative value and use the default schedule + idleTimerScheduleMillis = providedIdleTimerScheduleMillis > 0 + ? providedIdleTimerScheduleMillis + : DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS; + + maxConnections = Integer.getInteger("jdk.httpserver.maxConnections", DEFAULT_MAX_CONNECTIONS); + + maxIdleConnections = Integer.getInteger("sun.net.httpserver.maxIdleConnections", DEFAULT_MAX_IDLE_CONNECTIONS); + + drainAmount = Long.getLong("sun.net.httpserver.drainAmount", DEFAULT_DRAIN_AMOUNT); + + int providedMaxReqHeaders = Integer.getInteger("sun.net.httpserver.maxReqHeaders", DEFAULT_MAX_REQ_HEADERS); + maxReqHeaders = providedMaxReqHeaders > 0 ? providedMaxReqHeaders : DEFAULT_MAX_REQ_HEADERS; + + // A value <= 0 means unlimited + maxReqHeadersSize = Math.max( + Integer.getInteger("sun.net.httpserver.maxReqHeaderSize", DEFAULT_MAX_REQ_HEADER_SIZE), + 0); + + maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime", DEFAULT_MAX_REQ_TIME); + + maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime", DEFAULT_MAX_RSP_TIME); + + long providedReqRspTimerScheduleMillis = Long.getLong( + "sun.net.httpserver.timerMillis", + DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS); + // Ignore any negative or zero value for this configuration and reset to default schedule + reqRspTimerScheduleMillis = providedReqRspTimerScheduleMillis > 0 + ? providedReqRspTimerScheduleMillis + : DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS; + + debug = Boolean.getBoolean("sun.net.httpserver.debug"); + + noDelay = Boolean.getBoolean("sun.net.httpserver.nodelay"); } @@ -148,39 +124,22 @@ static void checkLegacyProperties(final Logger logger) { // legacy properties that are no longer used // print a warning to logger if they are set. - java.security.AccessController.doPrivileged( - new PrivilegedAction() { - public Void run () { - if (System.getProperty("sun.net.httpserver.readTimeout") - !=null) - { - logger.log (Level.WARNING, - "sun.net.httpserver.readTimeout "+ - "property is no longer used. "+ - "Use sun.net.httpserver.maxReqTime instead." - ); - } - if (System.getProperty("sun.net.httpserver.writeTimeout") - !=null) - { - logger.log (Level.WARNING, - "sun.net.httpserver.writeTimeout "+ - "property is no longer used. Use "+ - "sun.net.httpserver.maxRspTime instead." - ); - } - if (System.getProperty("sun.net.httpserver.selCacheTimeout") - !=null) - { - logger.log (Level.WARNING, - "sun.net.httpserver.selCacheTimeout "+ - "property is no longer used." - ); - } - return null; - } - } - ); + if (System.getProperty("sun.net.httpserver.readTimeout") != null) { + logger.log( + Level.WARNING, + "sun.net.httpserver.readTimeout property is no longer used. " + + "Use sun.net.httpserver.maxReqTime instead."); + } + if (System.getProperty("sun.net.httpserver.writeTimeout") != null) { + logger.log( + Level.WARNING, + "sun.net.httpserver.writeTimeout property is no longer used. " + + "Use sun.net.httpserver.maxRspTime instead."); + } + if (System.getProperty("sun.net.httpserver.selCacheTimeout") != null) { + logger.log(Level.WARNING, "sun.net.httpserver.selCacheTimeout property is no longer used."); + } + } static boolean debugEnabled() { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java index 49377475719..cb27911c647 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -53,8 +53,6 @@ import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -304,16 +302,8 @@ public synchronized void removeContext (HttpContext context) throws IllegalArgum logger.log (Level.DEBUG, "context removed: " + context.getPath()); } - @SuppressWarnings("removal") public InetSocketAddress getAddress() { - return AccessController.doPrivileged( - new PrivilegedAction() { - public InetSocketAddress run() { - return - (InetSocketAddress)schan.socket() - .getLocalSocketAddress(); - } - }); + return (InetSocketAddress) schan.socket().getLocalSocketAddress(); } void addEvent (Event r) { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java index 81ddb58eecf..6e2683737e6 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/FileServerHandler.java @@ -25,7 +25,6 @@ package sun.net.httpserver.simpleserver; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -67,12 +66,6 @@ public final class FileServerHandler implements HttpHandler { private FileServerHandler(Path root, UnaryOperator mimeTable) { root = root.normalize(); - - @SuppressWarnings("removal") - var securityManager = System.getSecurityManager(); - if (securityManager != null) - securityManager.checkRead(pathForSecurityCheck(root.toString())); - if (!Files.exists(root)) throw new IllegalArgumentException("Path does not exist: " + root); if (!root.isAbsolute()) @@ -86,11 +79,6 @@ private FileServerHandler(Path root, UnaryOperator mimeTable) { this.logger = System.getLogger("com.sun.net.httpserver"); } - private static String pathForSecurityCheck(String path) { - var separator = String.valueOf(File.separatorChar); - return path.endsWith(separator) ? (path + "-") : (path + separator + "-"); - } - private static final HttpHandler NOT_IMPLEMENTED_HANDLER = HttpHandlers.of(501, Headers.of(), ""); diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/JWebServer.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/JWebServer.java index baa35a76883..a7daa46bdff 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/JWebServer.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/simpleserver/JWebServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -26,8 +26,6 @@ package sun.net.httpserver.simpleserver; import java.io.PrintWriter; -import java.security.AccessController; -import java.security.PrivilegedAction; import static java.nio.charset.StandardCharsets.UTF_8; @@ -84,15 +82,9 @@ private static void setMaxReqTime() { } } - @SuppressWarnings("removal") static void setMaxConnectionsIfNotSet() { - AccessController.doPrivileged((PrivilegedAction) () -> { - if (System.getProperty(SYS_PROP_MAX_CONNECTIONS) != null) { - // an explicit value has already been set, so we don't override it - return null; - } + if (System.getProperty(SYS_PROP_MAX_CONNECTIONS) == null) { System.setProperty(SYS_PROP_MAX_CONNECTIONS, DEFAULT_JWEBSERVER_MAX_CONNECTIONS); - return null; - }); + } } } diff --git a/test/jdk/com/sun/net/httpserver/FileServerHandler.java b/test/jdk/com/sun/net/httpserver/FileServerHandler.java index bff78443839..849b5fd06de 100644 --- a/test/jdk/com/sun/net/httpserver/FileServerHandler.java +++ b/test/jdk/com/sun/net/httpserver/FileServerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -21,13 +21,9 @@ * questions. */ -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.net.ssl.*; + import com.sun.net.httpserver.*; /** diff --git a/test/jdk/com/sun/net/httpserver/HttpsParametersClientAuthTest.java b/test/jdk/com/sun/net/httpserver/HttpsParametersClientAuthTest.java index 291b74f2669..dc1f573c4e4 100644 --- a/test/jdk/com/sun/net/httpserver/HttpsParametersClientAuthTest.java +++ b/test/jdk/com/sun/net/httpserver/HttpsParametersClientAuthTest.java @@ -31,9 +31,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; -import java.security.AccessController; import java.security.KeyStore; -import java.security.PrivilegedExceptionAction; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -359,21 +357,15 @@ private static SSLContext onlyTrustStoreContext() throws Exception { } private static KeyStore loadTestKeyStore() throws Exception { - return AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public KeyStore run() throws Exception { - final String testKeys = System.getProperty("test.src") - + "/" - + "../../../../../../test/lib/jdk/test/lib/net/testkeys"; - try (final FileInputStream fis = new FileInputStream(testKeys)) { - final char[] passphrase = "passphrase".toCharArray(); - final KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(fis, passphrase); - return ks; - } - } - }); + final String testKeys = System.getProperty("test.src") + + "/" + + "../../../../../../test/lib/jdk/test/lib/net/testkeys"; + try (final FileInputStream fis = new FileInputStream(testKeys)) { + final char[] passphrase = "passphrase".toCharArray(); + final KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(fis, passphrase); + return ks; + } } // no-op implementations of the abstract methods of HttpsParameters diff --git a/test/jdk/com/sun/net/httpserver/LogFilter.java b/test/jdk/com/sun/net/httpserver/LogFilter.java index 702db801a3f..43d93ab02c2 100644 --- a/test/jdk/com/sun/net/httpserver/LogFilter.java +++ b/test/jdk/com/sun/net/httpserver/LogFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -21,17 +21,11 @@ * questions. */ -import java.net.*; import java.util.*; import java.text.*; import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.*; + import com.sun.net.httpserver.*; -import javax.security.auth.*; -import javax.security.auth.callback.*; -import javax.security.auth.login.*; class LogFilter extends Filter { diff --git a/test/jdk/com/sun/net/httpserver/SelCacheTest.java b/test/jdk/com/sun/net/httpserver/SelCacheTest.java index 644e64419e0..1ac4d21ee3e 100644 --- a/test/jdk/com/sun/net/httpserver/SelCacheTest.java +++ b/test/jdk/com/sun/net/httpserver/SelCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -36,12 +36,9 @@ import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; import javax.net.ssl.*; /* basic http/s connectivity test diff --git a/test/jdk/com/sun/net/httpserver/SimpleFileServer.java b/test/jdk/com/sun/net/httpserver/SimpleFileServer.java index 0d32f37bda3..bff415c0b98 100644 --- a/test/jdk/com/sun/net/httpserver/SimpleFileServer.java +++ b/test/jdk/com/sun/net/httpserver/SimpleFileServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -21,13 +21,11 @@ * questions. */ -import java.util.*; import java.util.concurrent.*; import java.util.logging.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.net.ssl.*; + import com.sun.net.httpserver.*; /** diff --git a/test/jdk/com/sun/net/httpserver/Test14.java b/test/jdk/com/sun/net/httpserver/Test14.java index 532078e8215..9b10a87642a 100644 --- a/test/jdk/com/sun/net/httpserver/Test14.java +++ b/test/jdk/com/sun/net/httpserver/Test14.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.security.auth.callback.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; /** diff --git a/test/jdk/com/sun/net/httpserver/Test2.java b/test/jdk/com/sun/net/httpserver/Test2.java index dd675c8933f..1e7710162fd 100644 --- a/test/jdk/com/sun/net/httpserver/Test2.java +++ b/test/jdk/com/sun/net/httpserver/Test2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.security.auth.callback.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; /** diff --git a/test/jdk/com/sun/net/httpserver/Test3.java b/test/jdk/com/sun/net/httpserver/Test3.java index a9dab54a388..ed5b46781fd 100644 --- a/test/jdk/com/sun/net/httpserver/Test3.java +++ b/test/jdk/com/sun/net/httpserver/Test3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,14 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.util.regex.*; -import java.util.regex.Pattern.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.net.ssl.*; /** * Test pipe-lining over http diff --git a/test/jdk/com/sun/net/httpserver/Test4.java b/test/jdk/com/sun/net/httpserver/Test4.java index 996614c0a4c..a3502dec22e 100644 --- a/test/jdk/com/sun/net/httpserver/Test4.java +++ b/test/jdk/com/sun/net/httpserver/Test4.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.util.regex.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.net.ssl.*; /** * Test pipe-lining (block after read) diff --git a/test/jdk/com/sun/net/httpserver/Test5.java b/test/jdk/com/sun/net/httpserver/Test5.java index b57778a5226..68556bbb888 100644 --- a/test/jdk/com/sun/net/httpserver/Test5.java +++ b/test/jdk/com/sun/net/httpserver/Test5.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.util.regex.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.net.ssl.*; /** * Test pipe-lining (no block) diff --git a/test/jdk/com/sun/net/httpserver/Test6.java b/test/jdk/com/sun/net/httpserver/Test6.java index cf49cead89b..b69bc57b640 100644 --- a/test/jdk/com/sun/net/httpserver/Test6.java +++ b/test/jdk/com/sun/net/httpserver/Test6.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.security.auth.callback.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; /** diff --git a/test/jdk/com/sun/net/httpserver/Test7.java b/test/jdk/com/sun/net/httpserver/Test7.java index 2c204a8a60b..4b997411316 100644 --- a/test/jdk/com/sun/net/httpserver/Test7.java +++ b/test/jdk/com/sun/net/httpserver/Test7.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.security.auth.callback.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; /** diff --git a/test/jdk/com/sun/net/httpserver/Test8.java b/test/jdk/com/sun/net/httpserver/Test8.java index 0dfc35e50f4..58f3dc2b3b4 100644 --- a/test/jdk/com/sun/net/httpserver/Test8.java +++ b/test/jdk/com/sun/net/httpserver/Test8.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import javax.security.auth.callback.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; /** diff --git a/test/jdk/com/sun/net/httpserver/TestLogging.java b/test/jdk/com/sun/net/httpserver/TestLogging.java index 0dd1fe00dc5..d108d2cce5d 100644 --- a/test/jdk/com/sun/net/httpserver/TestLogging.java +++ b/test/jdk/com/sun/net/httpserver/TestLogging.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -32,14 +32,11 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.util.logging.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; public class TestLogging extends Test { diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6339483.java b/test/jdk/com/sun/net/httpserver/bugs/B6339483.java index 2bfa8ad3efc..1bcf51509e0 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6339483.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6339483.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; public class B6339483 { diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6341616.java b/test/jdk/com/sun/net/httpserver/bugs/B6341616.java index 82222faf08b..8a5cb98f298 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6341616.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6341616.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; public class B6341616 { diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6526158.java b/test/jdk/com/sun/net/httpserver/bugs/B6526158.java index c38f12b49c3..5c25911d87a 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6526158.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6526158.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; public class B6526158 { diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6526913.java b/test/jdk/com/sun/net/httpserver/bugs/B6526913.java index 4fb2efd67f8..de2bd398e33 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6526913.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6526913.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -33,13 +33,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; public class B6526913 { diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6529200.java b/test/jdk/com/sun/net/httpserver/bugs/B6529200.java index 3ce792ffcd7..774eeb93d0d 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6529200.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6529200.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -31,13 +31,9 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; public class B6529200 { diff --git a/test/jdk/com/sun/net/httpserver/bugs/B6744329.java b/test/jdk/com/sun/net/httpserver/bugs/B6744329.java index 9a782fd6c94..8a6290441ac 100644 --- a/test/jdk/com/sun/net/httpserver/bugs/B6744329.java +++ b/test/jdk/com/sun/net/httpserver/bugs/B6744329.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -32,13 +32,10 @@ import com.sun.net.httpserver.*; -import java.util.*; import java.util.concurrent.*; import java.io.*; import java.net.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; + import jdk.test.lib.net.URIBuilder; public class B6744329 { diff --git a/test/jdk/com/sun/net/httpserver/simpleserver/RootDirPermissionsTest.java b/test/jdk/com/sun/net/httpserver/simpleserver/RootDirPermissionsTest.java index 79477d23782..16a5f619c4a 100644 --- a/test/jdk/com/sun/net/httpserver/simpleserver/RootDirPermissionsTest.java +++ b/test/jdk/com/sun/net/httpserver/simpleserver/RootDirPermissionsTest.java @@ -23,7 +23,7 @@ /* * @test - * @summary Tests for FileServerHandler with SecurityManager + * @summary Tests file permission checks during the creation of a `FileServerHandler` * @library /test/lib * @build jdk.test.lib.net.URIBuilder * @run main/othervm -ea RootDirPermissionsTest true @@ -245,7 +245,6 @@ private static void testFileGET() throws Exception { } } - @SuppressWarnings("removal") private static void testCreateHandler(){ try { SimpleFileServer.createFileServer(LOOPBACK_ADDR, TEST_DIR, OutputLevel.NONE); From f3b4350e0f14d3b0c551e0d24563788f379111d6 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 4 Dec 2024 16:37:38 +0000 Subject: [PATCH 126/171] 8345219: C2: x86_64 should not go to interpreter stubs for NaNs handling Reviewed-by: epeter, kvn --- src/hotspot/share/compiler/compileBroker.cpp | 4 +- .../compiler/c2/irTests/TestFPConversion.java | 127 ++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 20 +++ .../bench/java/lang/DoubleBitConversion.java | 103 ++++++++++++++ .../bench/java/lang/FloatBitConversion.java | 103 ++++++++++++++ 5 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/TestFPConversion.java create mode 100644 test/micro/org/openjdk/bench/java/lang/DoubleBitConversion.java create mode 100644 test/micro/org/openjdk/bench/java/lang/FloatBitConversion.java diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index a7b0627c0a4..30956b6c793 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -1448,7 +1448,7 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci, // do the compilation if (method->is_native()) { if (!PreferInterpreterNativeStubs || method->is_method_handle_intrinsic()) { -#if defined(X86) && !defined(ZERO) +#if defined(IA32) && !defined(ZERO) // The following native methods: // // java.lang.Float.intBitsToFloat @@ -1470,7 +1470,7 @@ nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci, method->intrinsic_id() == vmIntrinsics::_doubleToRawLongBits))) { return nullptr; } -#endif // X86 && !ZERO +#endif // IA32 && !ZERO // To properly handle the appendix argument for out-of-line calls we are using a small trampoline that // pops off the appendix argument and jumps to the target (see gen_special_dispatch in SharedRuntime). diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestFPConversion.java b/test/hotspot/jtreg/compiler/c2/irTests/TestFPConversion.java new file mode 100644 index 00000000000..d9eb8c806d8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestFPConversion.java @@ -0,0 +1,127 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package compiler.c2.irTests; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8345219 + * @summary Test that code generation for FP conversion works as intended + * @library /test/lib / + * @requires os.arch != "x86" & os.arch != "i386" + * @run driver compiler.c2.irTests.TestFPConversion + */ +public class TestFPConversion { + static final double[] DOUBLES = new double[] { + Double.NEGATIVE_INFINITY, + -Double.MAX_VALUE, + -1.0, + -Double.MIN_VALUE, + -0.0, + 0.0, + Double.MIN_VALUE, + 1.0, + Double.MAX_VALUE, + Double.POSITIVE_INFINITY, + Double.NaN, + }; + + static final float[] FLOATS = new float[] { + Float.NEGATIVE_INFINITY, + -Float.MAX_VALUE, + -1.0F, + -Float.MIN_VALUE, + -0.0F, + 0.0F, + Float.MIN_VALUE, + 1.0F, + Float.MAX_VALUE, + Float.POSITIVE_INFINITY, + Float.NaN, + }; + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(counts = {IRNode.MOV_D2L, "1"}) + public long doubleToRawLongBits(double x) { + return Double.doubleToRawLongBits(x); + } + + @Test + @IR(counts = {IRNode.MOV_D2L, "1"}) + public long doubleToLongBits(double x) { + return Double.doubleToLongBits(x); + } + + @Test + @IR(counts = {IRNode.MOV_L2D, "1"}) + public double longBitsToDouble(long x) { + return Double.longBitsToDouble(x); + } + + @Test + @IR(counts = {IRNode.MOV_F2I, "1"}) + public int floatToRawIntBits(float x) { + return Float.floatToRawIntBits(x); + } + + @Test + @IR(counts = {IRNode.MOV_F2I, "1"}) + public int floatToIntBits(float x) { + return Float.floatToIntBits(x); + } + + @Test + @IR(counts = {IRNode.MOV_I2F, "1"}) + public float intBitsToFloat(int x) { + return Float.intBitsToFloat(x); + } + + @Run(test = {"doubleToRawLongBits", "doubleToLongBits", "longBitsToDouble", + "floatToRawIntBits", "floatToIntBits", "intBitsToFloat"}) + public void runTests() { + for (int i = 0; i < DOUBLES.length; i++) { + double d = DOUBLES[i]; + long l1 = doubleToRawLongBits(d); + long l2 = doubleToLongBits(d); + double d1 = longBitsToDouble(l1); + double d2 = longBitsToDouble(l2); + Asserts.assertEquals(d, d1); + Asserts.assertEquals(d, d2); + } + for (int i = 0; i < FLOATS.length; i++) { + float f = FLOATS[i]; + int i1 = floatToRawIntBits(f); + int i2 = floatToIntBits(f); + float f1 = intBitsToFloat(i1); + float f2 = intBitsToFloat(i2); + Asserts.assertEquals(f, f1); + Asserts.assertEquals(f, f2); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 1eb6251b2bb..35e10c7ae9f 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1169,6 +1169,26 @@ public class IRNode { beforeMatchingNameRegex(MOD_L, "ModL"); } + public static final String MOV_F2I = PREFIX + "MOV_F2I" + POSTFIX; + static { + beforeMatchingNameRegex(MOV_F2I, "MoveF2I"); + } + + public static final String MOV_I2F = PREFIX + "MOV_I2F" + POSTFIX; + static { + beforeMatchingNameRegex(MOV_I2F, "MoveI2F"); + } + + public static final String MOV_D2L = PREFIX + "MOV_D2L" + POSTFIX; + static { + beforeMatchingNameRegex(MOV_D2L, "MoveD2L"); + } + + public static final String MOV_L2D = PREFIX + "MOD_L2D" + POSTFIX; + static { + beforeMatchingNameRegex(MOV_L2D, "MoveL2D"); + } + public static final String MUL = PREFIX + "MUL" + POSTFIX; static { beforeMatchingNameRegex(MUL, "Mul(I|L|F|D)"); diff --git a/test/micro/org/openjdk/bench/java/lang/DoubleBitConversion.java b/test/micro/org/openjdk/bench/java/lang/DoubleBitConversion.java new file mode 100644 index 00000000000..7ef4bf9cc75 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/DoubleBitConversion.java @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.BitSet; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 3, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(3) +public class DoubleBitConversion { + + double doubleZero = 0; + double doubleOne = 1; + double doubleNan = Double.NaN; + + long longDoubleZero = Double.doubleToLongBits(0); + long longDoubleOne = Double.doubleToLongBits(1); + long longDoubleNaN = Double.doubleToLongBits(Double.NaN); + + @Benchmark + public long doubleToRawLongBits_zero() { + return Double.doubleToRawLongBits(doubleZero); + } + + @Benchmark + public long doubleToRawLongBits_one() { + return Double.doubleToRawLongBits(doubleOne); + } + + @Benchmark + public long doubleToRawLongBits_NaN() { + return Double.doubleToRawLongBits(doubleNan); + } + + @Benchmark + public long doubleToLongBits_zero() { + return Double.doubleToLongBits(doubleZero); + } + + @Benchmark + public long doubleToLongBits_one() { + return Double.doubleToLongBits(doubleOne); + } + + @Benchmark + public long doubleToLongBits_NaN() { + return Double.doubleToLongBits(doubleNan); + } + + @Benchmark + public double longBitsToDouble_zero() { + return Double.longBitsToDouble(longDoubleZero); + } + + @Benchmark + public double longBitsToDouble_one() { + return Double.longBitsToDouble(longDoubleOne); + } + + @Benchmark + public double longBitsToDouble_NaN() { + return Double.longBitsToDouble(longDoubleNaN); + } + +} + diff --git a/test/micro/org/openjdk/bench/java/lang/FloatBitConversion.java b/test/micro/org/openjdk/bench/java/lang/FloatBitConversion.java new file mode 100644 index 00000000000..965e7b88cbb --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/FloatBitConversion.java @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.BitSet; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 3, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(3) +public class FloatBitConversion { + + float floatZero = 0; + float floatOne = 1; + float floatNan = Float.NaN; + + int intFloatZero = Float.floatToIntBits(0); + int intFloatOne = Float.floatToIntBits(1); + int intFloatNaN = Float.floatToIntBits(Float.NaN); + + @Benchmark + public int floatToRawIntBits_zero() { + return Float.floatToRawIntBits(floatZero); + } + + @Benchmark + public int floatToRawIntBits_one() { + return Float.floatToRawIntBits(floatOne); + } + + @Benchmark + public int floatToRawIntBits_NaN() { + return Float.floatToRawIntBits(floatNan); + } + + @Benchmark + public int floatToIntBits_zero() { + return Float.floatToIntBits(floatZero); + } + + @Benchmark + public int floatToIntBits_one() { + return Float.floatToIntBits(floatOne); + } + + @Benchmark + public int floatToIntBits_NaN() { + return Float.floatToIntBits(floatNan); + } + + @Benchmark + public float intBitsToFloat_zero() { + return Float.intBitsToFloat(intFloatZero); + } + + @Benchmark + public float intBitsToFloat_one() { + return Float.intBitsToFloat(intFloatOne); + } + + @Benchmark + public float intBitsToFloat_NaN() { + return Float.intBitsToFloat(intFloatNaN); + } + +} + From 79eb77b782bd0c3cecee6c66b86f6f3e17054498 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Wed, 4 Dec 2024 17:30:01 +0000 Subject: [PATCH 127/171] 8345486: Reevaluate the classes in java.lang.classfile.components package Reviewed-by: mcimadamore, asotona --- make/test/BuildMicrobenchmark.gmk | 1 + .../lang/classfile/ClassFileTransform.java | 7 +- .../snippet-files/PackageSnippets.java | 84 ------------------- .../classfile/components/ClassPrinter.java | 14 +--- .../classfile/components/ClassRemapper.java | 4 +- .../components/CodeLocalsShifter.java | 4 +- .../classfile/components/CodeRelabeler.java | 4 +- .../components/CodeStackTracker.java | 4 +- .../classfile/components/package-info.java | 4 +- .../snippet-files/PackageSnippets.java | 8 +- .../classfile/impl/ClassPrinterImpl.java | 10 +-- .../classfile/impl/ClassRemapperImpl.java | 2 +- .../classfile/impl/CodeLocalsShifterImpl.java | 2 +- .../classfile/impl/CodeRelabelerImpl.java | 2 +- .../classfile/impl/CodeStackTrackerImpl.java | 2 +- .../jdk/internal/classfile/impl/Util.java | 2 +- .../classfile/impl/verifier/VerifierImpl.java | 2 +- src/java.base/share/classes/module-info.java | 3 +- .../jdk/jfr/internal/util/Bytecode.java | 2 +- .../AdvancedTransformationsTest.java | 8 +- test/jdk/jdk/classfile/ClassBuildingTest.java | 2 +- test/jdk/jdk/classfile/ClassPrinterTest.java | 2 +- test/jdk/jdk/classfile/SnippetsTest.java | 16 ++-- test/jdk/jdk/classfile/StackMapsTest.java | 2 +- test/jdk/jdk/classfile/StackTrackerTest.java | 2 +- test/jdk/jdk/classfile/TEST.properties | 1 + test/jdk/jdk/classfile/VerifierSelfTest.java | 2 +- .../examples/AnnotationsExamples.java | 2 +- .../helpers/RebuildingTransformation.java | 2 +- .../jdk/jdk/classfile/helpers/Transforms.java | 3 +- .../classfile/AbstractCorpusBenchmark.java | 1 + .../jdk/classfile/RepeatedModelTraversal.java | 3 +- .../bench/jdk/classfile/Transforms.java | 5 +- 33 files changed, 54 insertions(+), 158 deletions(-) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/ClassPrinter.java (97%) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/ClassRemapper.java (98%) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/CodeLocalsShifter.java (97%) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/CodeRelabeler.java (98%) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/CodeStackTracker.java (98%) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/package-info.java (98%) rename src/java.base/share/classes/{java/lang => jdk/internal}/classfile/components/snippet-files/PackageSnippets.java (97%) diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index 0a4c2d9a48d..da9ff2029e8 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -89,6 +89,7 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \ SRC := $(MICROBENCHMARK_SRC), \ BIN := $(MICROBENCHMARK_CLASSES), \ JAVAC_FLAGS := \ + --add-exports java.base/jdk.internal.classfile.components=ALL-UNNAMED \ --add-exports java.base/jdk.internal.classfile.impl=ALL-UNNAMED \ --add-exports java.base/jdk.internal.event=ALL-UNNAMED \ --add-exports java.base/jdk.internal.foreign=ALL-UNNAMED \ diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java b/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java index 9fdafdf4331..7d9385eed68 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFileTransform.java @@ -60,12 +60,7 @@ * its state must be reset for each traversal; this will happen automatically if * the transform is created with {@link ClassTransform#ofStateful(Supplier)} (or * corresponding methods for other classfile locations.) - *

      - * Class transformation sample where code transformation is stateful: - * {@snippet lang="java" class="PackageSnippets" region="codeRelabeling"} - *

      - * Complex class instrumentation sample chaining multiple transformations: - * {@snippet lang="java" class="PackageSnippets" region="classInstrumentation"} + * * @param the transform type * @param the element type * @param the builder type diff --git a/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java b/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java index c6956d6d23a..7a58da7f6ce 100644 --- a/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java +++ b/src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java @@ -25,21 +25,13 @@ package java.lang.classfile.snippets; import java.lang.classfile.*; -import java.lang.classfile.components.ClassRemapper; -import java.lang.classfile.components.CodeLocalsShifter; -import java.lang.classfile.components.CodeRelabeler; import java.lang.classfile.instruction.*; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandles; -import java.lang.reflect.AccessFlag; -import java.util.ArrayDeque; import java.util.HashSet; -import java.util.Map; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.stream.Collectors.toSet; @@ -326,82 +318,6 @@ void fooToBarUnrolled(ClassModel classModel) { // @end } - void codeRelabeling(ClassModel classModel) { - // @start region="codeRelabeling" - byte[] newBytes = ClassFile.of().transformClass(classModel, - ClassTransform.transformingMethodBodies( - CodeTransform.ofStateful(CodeRelabeler::of))); - // @end - } - - // @start region="classInstrumentation" - byte[] classInstrumentation(ClassModel target, ClassModel instrumentor, Predicate instrumentedMethodsFilter) { - var instrumentorCodeMap = instrumentor.methods().stream() - .filter(instrumentedMethodsFilter) - .collect(Collectors.toMap(mm -> mm.methodName().stringValue() + mm.methodType().stringValue(), mm -> mm.code().orElseThrow())); - var targetFieldNames = target.fields().stream().map(f -> f.fieldName().stringValue()).collect(Collectors.toSet()); - var targetMethods = target.methods().stream().map(m -> m.methodName().stringValue() + m.methodType().stringValue()).collect(Collectors.toSet()); - var instrumentorClassRemapper = ClassRemapper.of(Map.of(instrumentor.thisClass().asSymbol(), target.thisClass().asSymbol())); - return ClassFile.of().transformClass(target, - ClassTransform.transformingMethods( - instrumentedMethodsFilter, - (mb, me) -> { - if (me instanceof CodeModel targetCodeModel) { - var mm = targetCodeModel.parent().get(); - //instrumented methods code is taken from instrumentor - mb.transformCode(instrumentorCodeMap.get(mm.methodName().stringValue() + mm.methodType().stringValue()), - //all references to the instrumentor class are remapped to target class - instrumentorClassRemapper.asCodeTransform() - .andThen((codeBuilder, instrumentorCodeElement) -> { - //all invocations of target methods from instrumentor are inlined - if (instrumentorCodeElement instanceof InvokeInstruction inv - && target.thisClass().asInternalName().equals(inv.owner().asInternalName()) - && mm.methodName().stringValue().equals(inv.name().stringValue()) - && mm.methodType().stringValue().equals(inv.type().stringValue())) { - - //store stacked method parameters into locals - var storeStack = new ArrayDeque(); - int slot = 0; - if (!mm.flags().has(AccessFlag.STATIC)) - storeStack.push(StoreInstruction.of(TypeKind.REFERENCE, slot++)); - for (var pt : mm.methodTypeSymbol().parameterList()) { - var tk = TypeKind.from(pt); - storeStack.push(StoreInstruction.of(tk, slot)); - slot += tk.slotSize(); - } - storeStack.forEach(codeBuilder::with); - - //inlined target locals must be shifted based on the actual instrumentor locals - codeBuilder.block(inlinedBlockBuilder -> inlinedBlockBuilder - .transform(targetCodeModel, CodeLocalsShifter.of(mm.flags(), mm.methodTypeSymbol()) - .andThen(CodeRelabeler.of()) - .andThen((innerBuilder, shiftedTargetCode) -> { - //returns must be replaced with jump to the end of the inlined method - if (shiftedTargetCode instanceof ReturnInstruction) - innerBuilder.goto_(inlinedBlockBuilder.breakLabel()); - else - innerBuilder.with(shiftedTargetCode); - }))); - } else - codeBuilder.with(instrumentorCodeElement); - })); - } else - mb.with(me); - }) - .andThen(ClassTransform.endHandler(clb -> - //remaining instrumentor fields and methods are injected at the end - clb.transform(instrumentor, - ClassTransform.dropping(cle -> - !(cle instanceof FieldModel fm - && !targetFieldNames.contains(fm.fieldName().stringValue())) - && !(cle instanceof MethodModel mm - && !ConstantDescs.INIT_NAME.equals(mm.methodName().stringValue()) - && !targetMethods.contains(mm.methodName().stringValue() + mm.methodType().stringValue()))) - //and instrumentor class references remapped to target class - .andThen(instrumentorClassRemapper))))); - } - // @end - void resolverExample() { // @start region="lookup-class-hierarchy-resolver" MethodHandles.Lookup lookup = MethodHandles.lookup(); // @replace regex="MethodHandles\.lookup\(\)" replacement="..." diff --git a/src/java.base/share/classes/java/lang/classfile/components/ClassPrinter.java b/src/java.base/share/classes/jdk/internal/classfile/components/ClassPrinter.java similarity index 97% rename from src/java.base/share/classes/java/lang/classfile/components/ClassPrinter.java rename to src/java.base/share/classes/jdk/internal/classfile/components/ClassPrinter.java index 7237dc54580..cc5a3824936 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/ClassPrinter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/ClassPrinter.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.classfile.components; +package jdk.internal.classfile.components; import java.lang.classfile.ClassModel; import java.lang.classfile.CodeModel; @@ -58,8 +58,6 @@ *

      * Another use case for {@link ClassPrinter} is to simplify writing of automated tests: * {@snippet lang="java" class="PackageSnippets" region="printNodesInTest"} - * - * @since 24 */ public final class ClassPrinter { @@ -68,8 +66,6 @@ private ClassPrinter() { /** * Level of detail to print or export. - * - * @since 24 */ public enum Verbosity { @@ -102,8 +98,6 @@ public enum Verbosity { /** * Named, traversable, and printable node parent. - * - * @since 24 */ public sealed interface Node { @@ -146,8 +140,6 @@ default void toYaml(Consumer out) { /** * A leaf node holding single printable value. - * - * @since 24 */ public sealed interface LeafNode extends Node permits ClassPrinterImpl.LeafNodeImpl { @@ -161,8 +153,6 @@ public sealed interface LeafNode extends Node /** * A tree node holding {@link List} of nested nodes. - * - * @since 24 */ public sealed interface ListNode extends Node, List permits ClassPrinterImpl.ListNodeImpl { @@ -172,8 +162,6 @@ public sealed interface ListNode extends Node, List * A tree node holding {@link Map} of nested nodes. *

      * Each {@link Map.Entry#getKey()} == {@link Map.Entry#getValue()}.{@link #name()}. - * - * @since 24 */ public sealed interface MapNode extends Node, Map permits ClassPrinterImpl.MapNodeImpl { diff --git a/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java b/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java similarity index 98% rename from src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java rename to src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java index 4a2808ad4f0..ad116cd9dc4 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/ClassRemapper.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.classfile.components; +package jdk.internal.classfile.components; import java.lang.classfile.ClassFile; import java.lang.classfile.ClassModel; @@ -53,8 +53,6 @@ *

      * Arrays of reference types are always decomposed, mapped as the base reference * types and composed back to arrays. - * - * @since 24 */ public sealed interface ClassRemapper extends ClassTransform permits ClassRemapperImpl { diff --git a/src/java.base/share/classes/java/lang/classfile/components/CodeLocalsShifter.java b/src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java similarity index 97% rename from src/java.base/share/classes/java/lang/classfile/components/CodeLocalsShifter.java rename to src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java index 9db85f54a45..f06b4694953 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/CodeLocalsShifter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/CodeLocalsShifter.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.classfile.components; +package jdk.internal.classfile.components; import java.lang.classfile.AccessFlags; import java.lang.classfile.CodeTransform; @@ -37,8 +37,6 @@ * newly allocated positions to avoid conflicts during code injection. * Locals pointing to the receiver or to method arguments slots are never shifted. * All locals pointing beyond the method arguments are re-indexed in order of appearance. - * - * @since 24 */ public sealed interface CodeLocalsShifter extends CodeTransform permits CodeLocalsShifterImpl { diff --git a/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java b/src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java similarity index 98% rename from src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java rename to src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java index 4ef82adfdb9..6a16b6a00dd 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/CodeRelabeler.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.classfile.components; +package jdk.internal.classfile.components; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeTransform; @@ -44,8 +44,6 @@ * Primary purpose of CodeRelabeler is for repeated injections of the same code blocks. * Repeated injection of the same code block must be relabeled, so each instance of * {@link java.lang.classfile.Label} is bound in the target bytecode exactly once. - * - * @since 24 */ public sealed interface CodeRelabeler extends CodeTransform permits CodeRelabelerImpl { diff --git a/src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java b/src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java similarity index 98% rename from src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java rename to src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java index 3761e53ff19..eb7f58702eb 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/CodeStackTracker.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/CodeStackTracker.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.classfile.components; +package jdk.internal.classfile.components; import java.lang.classfile.CodeTransform; import java.lang.classfile.Label; @@ -49,8 +49,6 @@ * int maxStack = stackTracker.maxStackSize().get(); * }); * } - * - * @since 24 */ public sealed interface CodeStackTracker extends CodeTransform permits CodeStackTrackerImpl { diff --git a/src/java.base/share/classes/java/lang/classfile/components/package-info.java b/src/java.base/share/classes/jdk/internal/classfile/components/package-info.java similarity index 98% rename from src/java.base/share/classes/java/lang/classfile/components/package-info.java rename to src/java.base/share/classes/jdk/internal/classfile/components/package-info.java index be650f4c77c..30fb5fc07e5 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/package-info.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/package-info.java @@ -110,8 +110,6 @@ * CodeLocalsShifter} and {@link CodeRelabeler} into fully functional class * instrumenting transformation: * {@snippet lang="java" class="PackageSnippets" region="classInstrumentation"} - * - * @since 24 */ -package java.lang.classfile.components; +package jdk.internal.classfile.components; diff --git a/src/java.base/share/classes/java/lang/classfile/components/snippet-files/PackageSnippets.java b/src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java similarity index 97% rename from src/java.base/share/classes/java/lang/classfile/components/snippet-files/PackageSnippets.java rename to src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java index 87c2146a7ac..452e0188115 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/snippet-files/PackageSnippets.java +++ b/src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java @@ -25,10 +25,10 @@ package java.lang.classfile.components.snippets; import java.lang.classfile.*; -import java.lang.classfile.components.ClassPrinter; -import java.lang.classfile.components.ClassRemapper; -import java.lang.classfile.components.CodeLocalsShifter; -import java.lang.classfile.components.CodeRelabeler; +import jdk.internal.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassRemapper; +import jdk.internal.classfile.components.CodeLocalsShifter; +import jdk.internal.classfile.components.CodeRelabeler; import java.lang.classfile.instruction.InvokeInstruction; import java.lang.classfile.instruction.ReturnInstruction; import java.lang.classfile.instruction.StoreInstruction; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java index 183ee323b52..8a44890fe66 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassPrinterImpl.java @@ -31,11 +31,11 @@ import java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo; import java.lang.classfile.attribute.StackMapFrameInfo.UninitializedVerificationTypeInfo; import java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo; -import java.lang.classfile.components.ClassPrinter.LeafNode; -import java.lang.classfile.components.ClassPrinter.ListNode; -import java.lang.classfile.components.ClassPrinter.MapNode; -import java.lang.classfile.components.ClassPrinter.Node; -import java.lang.classfile.components.ClassPrinter.Verbosity; +import jdk.internal.classfile.components.ClassPrinter.LeafNode; +import jdk.internal.classfile.components.ClassPrinter.ListNode; +import jdk.internal.classfile.components.ClassPrinter.MapNode; +import jdk.internal.classfile.components.ClassPrinter.Node; +import jdk.internal.classfile.components.ClassPrinter.Verbosity; import java.lang.classfile.constantpool.*; import java.lang.classfile.instruction.*; import java.lang.constant.ConstantDesc; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java index cd9faaf2f9a..6dbde898ee5 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java @@ -26,7 +26,7 @@ import java.lang.classfile.*; import java.lang.classfile.attribute.*; -import java.lang.classfile.components.ClassRemapper; +import jdk.internal.classfile.components.ClassRemapper; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.classfile.instruction.ConstantInstruction.LoadConstantInstruction; import java.lang.classfile.instruction.*; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java index 7b7d04a8f1d..eac66b7cb5e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java @@ -28,7 +28,7 @@ import java.lang.classfile.CodeElement; import java.lang.classfile.Signature; import java.lang.classfile.TypeKind; -import java.lang.classfile.components.CodeLocalsShifter; +import jdk.internal.classfile.components.CodeLocalsShifter; import java.lang.classfile.instruction.IncrementInstruction; import java.lang.classfile.instruction.LoadInstruction; import java.lang.classfile.instruction.LocalVariable; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java index af0a59978d7..1e7e5262af4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java @@ -27,7 +27,7 @@ import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; import java.lang.classfile.Label; -import java.lang.classfile.components.CodeRelabeler; +import jdk.internal.classfile.components.CodeRelabeler; import java.lang.classfile.instruction.*; import java.util.function.BiFunction; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java index 2746caae004..5441f78518e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeStackTrackerImpl.java @@ -29,7 +29,7 @@ import java.lang.classfile.Label; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; -import java.lang.classfile.components.CodeStackTracker; +import jdk.internal.classfile.components.CodeStackTracker; import java.lang.classfile.instruction.*; import java.util.*; import java.util.function.Consumer; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index fc6099f3e5b..b0234fb88cc 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -26,7 +26,7 @@ import java.lang.classfile.*; import java.lang.classfile.attribute.CodeAttribute; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ModuleEntry; import java.lang.classfile.constantpool.PoolEntry; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index ed93ff1fc5d..4c2104c5a78 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -26,7 +26,7 @@ import java.lang.classfile.ClassHierarchyResolver; import java.lang.classfile.ClassModel; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index c3bcbed4e3b..187c97a6ad3 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -81,7 +81,6 @@ exports java.lang.annotation; exports java.lang.classfile; exports java.lang.classfile.attribute; - exports java.lang.classfile.components; exports java.lang.classfile.constantpool; exports java.lang.classfile.instruction; exports java.lang.constant; @@ -177,6 +176,8 @@ jdk.net, jdk.sctp, jdk.crypto.cryptoki; + exports jdk.internal.classfile.components to + jdk.jfr; exports jdk.internal.foreign to jdk.incubator.vector; exports jdk.internal.event to diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java index a027d756508..c51c3afe331 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Bytecode.java @@ -35,7 +35,7 @@ import java.lang.classfile.CodeBuilder; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; /** * Helper class when working with bytecode. diff --git a/test/jdk/jdk/classfile/AdvancedTransformationsTest.java b/test/jdk/jdk/classfile/AdvancedTransformationsTest.java index 450f1279810..557384e602e 100644 --- a/test/jdk/jdk/classfile/AdvancedTransformationsTest.java +++ b/test/jdk/jdk/classfile/AdvancedTransformationsTest.java @@ -37,8 +37,8 @@ import java.lang.classfile.MethodModel; import java.lang.classfile.TypeKind; import jdk.internal.classfile.impl.StackMapGenerator; -import java.lang.classfile.components.ClassRemapper; -import java.lang.classfile.components.CodeLocalsShifter; +import jdk.internal.classfile.components.ClassRemapper; +import jdk.internal.classfile.components.CodeLocalsShifter; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import static helpers.TestUtil.assertEmpty; @@ -59,9 +59,9 @@ import java.lang.classfile.instruction.ReturnInstruction; import java.lang.classfile.instruction.StoreInstruction; import java.lang.reflect.AccessFlag; -import java.lang.classfile.components.CodeRelabeler; +import jdk.internal.classfile.components.CodeRelabeler; import java.lang.constant.ModuleDesc; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import static java.lang.annotation.ElementType.*; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/test/jdk/jdk/classfile/ClassBuildingTest.java b/test/jdk/jdk/classfile/ClassBuildingTest.java index bf6380ae8fe..ba58f20f7d7 100644 --- a/test/jdk/jdk/classfile/ClassBuildingTest.java +++ b/test/jdk/jdk/classfile/ClassBuildingTest.java @@ -35,7 +35,7 @@ import java.lang.classfile.MethodTransform; import java.lang.classfile.attribute.MethodParametersAttribute; import java.lang.classfile.attribute.SignatureAttribute; -import java.lang.classfile.components.ClassRemapper; +import jdk.internal.classfile.components.ClassRemapper; import org.junit.jupiter.api.Test; import java.lang.constant.ClassDesc; diff --git a/test/jdk/jdk/classfile/ClassPrinterTest.java b/test/jdk/jdk/classfile/ClassPrinterTest.java index 7ea69f58889..5dc136f59c3 100644 --- a/test/jdk/jdk/classfile/ClassPrinterTest.java +++ b/test/jdk/jdk/classfile/ClassPrinterTest.java @@ -35,7 +35,7 @@ import java.util.Optional; import java.lang.classfile.*; import java.lang.classfile.attribute.*; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicCallSiteDesc; import java.lang.constant.MethodHandleDesc; diff --git a/test/jdk/jdk/classfile/SnippetsTest.java b/test/jdk/jdk/classfile/SnippetsTest.java index 6999ef2a0ae..51a73b93d67 100644 --- a/test/jdk/jdk/classfile/SnippetsTest.java +++ b/test/jdk/jdk/classfile/SnippetsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -28,6 +28,7 @@ */ import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import javax.tools.StandardLocation; import javax.tools.ToolProvider; @@ -41,7 +42,7 @@ public class SnippetsTest { @ParameterizedTest @ValueSource(strings = { "src/java.base/share/classes/java/lang/classfile/snippet-files/PackageSnippets.java", - "src/java.base/share/classes/java/lang/classfile/components/snippet-files/PackageSnippets.java"}) + "src/java.base/share/classes/jdk/internal/classfile/components/snippet-files/PackageSnippets.java"}) void testSnippet(String source) throws Exception { var p = Paths.get(System.getProperty("test.src", ".")).toAbsolutePath(); while ((p = p.getParent()) != null) { @@ -52,9 +53,14 @@ void testSnippet(String source) throws Exception { var compilationUnits = fileManager.getJavaFileObjectsFromFiles(List.of(src)); fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(Paths.get(System.getProperty("test.classes", ".")).toFile())); - var task = compiler.getTask(null, fileManager, null, List.of( - "--enable-preview", - "--source", String.valueOf(Runtime.version().feature())), + List flags = List.of( + "--source", String.valueOf(Runtime.version().feature())); + if (source.contains("jdk/internal/classfile/components")) { + flags = new ArrayList<>(flags); + flags.add("--add-exports"); + flags.add("java.base/jdk.internal.classfile.components=ALL-UNNAMED"); + } + var task = compiler.getTask(null, fileManager, null, flags, null, compilationUnits); if (task.call()) return; throw new RuntimeException("Error compiling " + source); diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 857299f4ecd..b2f94596622 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -33,7 +33,7 @@ import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.StackMapFrameInfo; import java.lang.classfile.attribute.StackMapTableAttribute; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; diff --git a/test/jdk/jdk/classfile/StackTrackerTest.java b/test/jdk/jdk/classfile/StackTrackerTest.java index b4e82ab0c4b..7d25c9e2ab9 100644 --- a/test/jdk/jdk/classfile/StackTrackerTest.java +++ b/test/jdk/jdk/classfile/StackTrackerTest.java @@ -31,7 +31,7 @@ import java.lang.constant.MethodTypeDesc; import java.lang.constant.ConstantDescs; import java.lang.classfile.*; -import java.lang.classfile.components.CodeStackTracker; +import jdk.internal.classfile.components.CodeStackTracker; import static java.lang.classfile.TypeKind.*; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --git a/test/jdk/jdk/classfile/TEST.properties b/test/jdk/jdk/classfile/TEST.properties index 756e08b7b93..296312b258f 100644 --- a/test/jdk/jdk/classfile/TEST.properties +++ b/test/jdk/jdk/classfile/TEST.properties @@ -1,6 +1,7 @@ maxOutputSize = 2500000 enablePreview = true modules = \ + java.base/jdk.internal.classfile.components \ java.base/jdk.internal.classfile.impl \ java.base/jdk.internal.classfile.impl.verifier \ java.base/jdk.internal.org.objectweb.asm \ diff --git a/test/jdk/jdk/classfile/VerifierSelfTest.java b/test/jdk/jdk/classfile/VerifierSelfTest.java index 1f9f199a33b..bc4ad5ab4ef 100644 --- a/test/jdk/jdk/classfile/VerifierSelfTest.java +++ b/test/jdk/jdk/classfile/VerifierSelfTest.java @@ -48,7 +48,7 @@ import java.util.stream.Stream; import java.lang.classfile.*; import java.lang.classfile.attribute.*; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.constant.ModuleDesc; diff --git a/test/jdk/jdk/classfile/examples/AnnotationsExamples.java b/test/jdk/jdk/classfile/examples/AnnotationsExamples.java index a43ab72556e..dfec144bbab 100644 --- a/test/jdk/jdk/classfile/examples/AnnotationsExamples.java +++ b/test/jdk/jdk/classfile/examples/AnnotationsExamples.java @@ -39,7 +39,7 @@ import java.lang.classfile.ClassFile; import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; import java.lang.classfile.constantpool.ConstantPoolBuilder; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; public class AnnotationsExamples { diff --git a/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java b/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java index b0e1d6c312d..895c04704cc 100644 --- a/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java +++ b/test/jdk/jdk/classfile/helpers/RebuildingTransformation.java @@ -32,7 +32,7 @@ import java.lang.classfile.instruction.*; import java.lang.constant.ModuleDesc; import java.lang.constant.PackageDesc; -import java.lang.classfile.components.CodeStackTracker; +import jdk.internal.classfile.components.CodeStackTracker; class RebuildingTransformation { diff --git a/test/jdk/jdk/classfile/helpers/Transforms.java b/test/jdk/jdk/classfile/helpers/Transforms.java index c385fe61663..b0e88bd8212 100644 --- a/test/jdk/jdk/classfile/helpers/Transforms.java +++ b/test/jdk/jdk/classfile/helpers/Transforms.java @@ -22,7 +22,6 @@ */ package helpers; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; import java.util.Map; @@ -43,7 +42,7 @@ import java.lang.classfile.CodeTransform; import java.lang.classfile.MethodModel; import java.lang.classfile.MethodTransform; -import java.lang.classfile.components.ClassRemapper; +import jdk.internal.classfile.components.ClassRemapper; import jdk.internal.org.objectweb.asm.AnnotationVisitor; import jdk.internal.org.objectweb.asm.Attribute; import jdk.internal.org.objectweb.asm.ClassReader; diff --git a/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java b/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java index 5f64e114877..685f73e64ce 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/AbstractCorpusBenchmark.java @@ -47,6 +47,7 @@ "--add-exports", "java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED", "--add-exports", "java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED", "--enable-preview", + "--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED", "--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"}) @State(Scope.Benchmark) public class AbstractCorpusBenchmark { diff --git a/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java b/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java index b9c9082625c..f8f29036545 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/RepeatedModelTraversal.java @@ -31,12 +31,13 @@ import java.util.List; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; -import java.lang.classfile.components.ClassPrinter; +import jdk.internal.classfile.components.ClassPrinter; import org.openjdk.jmh.annotations.*; @BenchmarkMode(Mode.Throughput) @State(Scope.Benchmark) @Fork(value = 1, jvmArgs = { + "--add-exports", "java.base/jdk.internal.classfile.components=ALL-UNNAMED", "--enable-preview"}) @Warmup(iterations = 3) @Measurement(iterations = 4) diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java b/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java index e4d553c268f..8af03a8b874 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Transforms.java @@ -22,11 +22,8 @@ */ package org.openjdk.bench.jdk.classfile; -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Consumer; import java.util.function.UnaryOperator; @@ -43,7 +40,7 @@ import java.lang.classfile.CodeTransform; import java.lang.classfile.MethodModel; import java.lang.classfile.MethodTransform; -import java.lang.classfile.components.ClassRemapper; +import jdk.internal.classfile.components.ClassRemapper; import jdk.internal.org.objectweb.asm.AnnotationVisitor; import jdk.internal.org.objectweb.asm.Attribute; import jdk.internal.org.objectweb.asm.ClassReader; From baa6957b9bf9c050c44ef6e36d21566dd3636fa0 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie Date: Wed, 4 Dec 2024 17:51:04 +0000 Subject: [PATCH 128/171] 8345507: Fix build of static launcher Reviewed-by: erikj --- make/ModuleWrapper.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/ModuleWrapper.gmk b/make/ModuleWrapper.gmk index e99f34cdd71..51208432ea0 100644 --- a/make/ModuleWrapper.gmk +++ b/make/ModuleWrapper.gmk @@ -51,7 +51,7 @@ ifeq ($(MAKEFILE_PREFIX), Lib) # which static library files to include. The variable $(MODULE)_INCLUDED_LIBS is # added to for each call to SetupJdkLibrary. The file module-included-libs.txt is then # read in StaticLibs.gmk. - ifneq ($($(MODULE)_JDK_LIBS), ) + ifneq ($($(MODULE)_INCLUDED_LIBS), ) LIBLIST := $(SUPPORT_OUTPUTDIR)/modules_static-libs/$(MODULE)/module-included-libs.txt $(LIBLIST): $(TARGETS) From e1695f6c40dbf27538c6c450eb1cf64a05e0ee9a Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 4 Dec 2024 18:27:37 +0000 Subject: [PATCH 129/171] 8345472: Fix incorrect format instruction for floating point max/min patterns Reviewed-by: kvn --- src/hotspot/cpu/x86/x86_64.ad | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 550c8047034..4667922505c 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -4424,21 +4424,21 @@ instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MaxF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "maxF $dst, $a, $b \t! using tmp, atmp and btmp as TEMP" %} + format %{ "maxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} ins_encode %{ __ vminmax_fp(Op_MaxV, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); %} ins_pipe( pipe_slow ); %} -instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xmmt, rRegI tmp, rFlagsReg cr) %{ +instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MaxF a b)); - effect(USE a, USE b, TEMP xmmt, TEMP tmp, KILL cr); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "$dst = max($a, $b)\t# intrinsic (float)" %} + format %{ "maxF_reduction $dst, $a, $b \t!using $xtmp and $rtmp as TEMP" %} ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xmmt$$XMMRegister, $tmp$$Register, + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, false /*min*/, true /*single*/); %} ins_pipe( pipe_slow ); @@ -4449,21 +4449,21 @@ instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MaxD a b)); effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); - format %{ "maxD $dst, $a, $b \t! using tmp, atmp and btmp as TEMP" %} + format %{ "maxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} ins_encode %{ __ vminmax_fp(Op_MaxV, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); %} ins_pipe( pipe_slow ); %} -instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xmmt, rRegL tmp, rFlagsReg cr) %{ +instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MaxD a b)); - effect(USE a, USE b, TEMP xmmt, TEMP tmp, KILL cr); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "$dst = max($a, $b)\t# intrinsic (double)" %} + format %{ "maxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xmmt$$XMMRegister, $tmp$$Register, + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, false /*min*/, false /*single*/); %} ins_pipe( pipe_slow ); @@ -4474,21 +4474,21 @@ instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "minF $dst, $a, $b \t! using tmp, atmp and btmp as TEMP" %} + format %{ "minF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} ins_encode %{ __ vminmax_fp(Op_MinV, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); %} ins_pipe( pipe_slow ); %} -instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xmmt, rRegI tmp, rFlagsReg cr) %{ +instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MinF a b)); - effect(USE a, USE b, TEMP xmmt, TEMP tmp, KILL cr); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "$dst = min($a, $b)\t# intrinsic (float)" %} + format %{ "minF_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xmmt$$XMMRegister, $tmp$$Register, + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, true /*min*/, true /*single*/); %} ins_pipe( pipe_slow ); @@ -4499,21 +4499,21 @@ instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MinD a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "minD $dst, $a, $b \t! using tmp, atmp and btmp as TEMP" %} + format %{ "minD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} ins_encode %{ __ vminmax_fp(Op_MinV, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); %} ins_pipe( pipe_slow ); %} -instruct minD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xmmt, rRegL tmp, rFlagsReg cr) %{ +instruct minD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MinD a b)); - effect(USE a, USE b, TEMP xmmt, TEMP tmp, KILL cr); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "$dst = min($a, $b)\t# intrinsic (double)" %} + format %{ "maxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xmmt$$XMMRegister, $tmp$$Register, + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, true /*min*/, false /*single*/); %} ins_pipe( pipe_slow ); From 6aa7667e9d05c1c3edce5ae2f29864e7a876ebbe Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 4 Dec 2024 18:45:35 +0000 Subject: [PATCH 130/171] 8339535: JVM crashes with -Xshare:dump -XX:+SegmentedCodeCache Reviewed-by: ccheung, dholmes, kvn --- src/hotspot/share/runtime/arguments.cpp | 47 ++++++++++--------- src/hotspot/share/runtime/arguments.hpp | 8 ++-- .../cds/appcds/CommandLineFlagCombo.java | 3 +- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 8df81ca0f94..11481954e10 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1775,14 +1775,20 @@ bool Arguments::sun_java_launcher_is_altjvm() { //=========================================================================================================== // Parsing of main arguments -unsigned int addreads_count = 0; -unsigned int addexports_count = 0; -unsigned int addopens_count = 0; -unsigned int patch_mod_count = 0; -unsigned int enable_native_access_count = 0; +static unsigned int addreads_count = 0; +static unsigned int addexports_count = 0; +static unsigned int addopens_count = 0; +static unsigned int patch_mod_count = 0; +static unsigned int enable_native_access_count = 0; +static bool patch_mod_javabase = false; // Check the consistency of vm_init_args bool Arguments::check_vm_args_consistency() { + // This may modify compiler flags. Must be called before CompilerConfig::check_args_consistency() + if (!CDSConfig::check_vm_args_consistency(patch_mod_javabase, mode_flag_cmd_line)) { + return false; + } + // Method for adding checks for flag consistency. // The intent is to warn the user of all possible conflicts, // before returning an error. @@ -1953,8 +1959,6 @@ jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args, const JavaVMInitArgs *java_tool_options_args, const JavaVMInitArgs *java_options_args, const JavaVMInitArgs *cmd_line_args) { - bool patch_mod_javabase = false; - // Save default settings for some mode flags Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods; Arguments::_UseOnStackReplacement = UseOnStackReplacement; @@ -1968,27 +1972,27 @@ jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args, set_mode_flags(_mixed); // Parse args structure generated from java.base vm options resource - jint result = parse_each_vm_init_arg(vm_options_args, &patch_mod_javabase, JVMFlagOrigin::JIMAGE_RESOURCE); + jint result = parse_each_vm_init_arg(vm_options_args, JVMFlagOrigin::JIMAGE_RESOURCE); if (result != JNI_OK) { return result; } // Parse args structure generated from JAVA_TOOL_OPTIONS environment // variable (if present). - result = parse_each_vm_init_arg(java_tool_options_args, &patch_mod_javabase, JVMFlagOrigin::ENVIRON_VAR); + result = parse_each_vm_init_arg(java_tool_options_args, JVMFlagOrigin::ENVIRON_VAR); if (result != JNI_OK) { return result; } // Parse args structure generated from the command line flags. - result = parse_each_vm_init_arg(cmd_line_args, &patch_mod_javabase, JVMFlagOrigin::COMMAND_LINE); + result = parse_each_vm_init_arg(cmd_line_args, JVMFlagOrigin::COMMAND_LINE); if (result != JNI_OK) { return result; } // Parse args structure generated from the _JAVA_OPTIONS environment // variable (if present) (mimics classic VM) - result = parse_each_vm_init_arg(java_options_args, &patch_mod_javabase, JVMFlagOrigin::ENVIRON_VAR); + result = parse_each_vm_init_arg(java_options_args, JVMFlagOrigin::ENVIRON_VAR); if (result != JNI_OK) { return result; } @@ -2009,7 +2013,7 @@ jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args, SystemMemoryBarrier::initialize(); // Do final processing now that all arguments have been parsed - result = finalize_vm_init_args(patch_mod_javabase); + result = finalize_vm_init_args(); if (result != JNI_OK) { return result; } @@ -2064,7 +2068,7 @@ static bool valid_jdwp_agent(char *name, bool is_path) { } #endif -int Arguments::process_patch_mod_option(const char* patch_mod_tail, bool* patch_mod_javabase) { +int Arguments::process_patch_mod_option(const char* patch_mod_tail) { // --patch-module==()* assert(patch_mod_tail != nullptr, "Unexpected null patch-module value"); // Find the equal sign between the module name and the path specification @@ -2080,7 +2084,7 @@ int Arguments::process_patch_mod_option(const char* patch_mod_tail, bool* patch_ memcpy(module_name, patch_mod_tail, module_len); *(module_name + module_len) = '\0'; // The path piece begins one past the module_equal sign - add_patch_mod_prefix(module_name, module_equal + 1, patch_mod_javabase); + add_patch_mod_prefix(module_name, module_equal + 1); FREE_C_HEAP_ARRAY(char, module_name); if (!create_numbered_module_property("jdk.module.patch", patch_mod_tail, patch_mod_count++)) { return JNI_ENOMEM; @@ -2146,7 +2150,7 @@ jint Arguments::parse_xss(const JavaVMOption* option, const char* tail, intx* ou return JNI_OK; } -jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, JVMFlagOrigin origin) { +jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin origin) { // For match_option to return remaining or value part of option string const char* tail; @@ -2273,7 +2277,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m } } else if (match_option(option, "--patch-module=", &tail)) { // --patch-module==()* - int res = process_patch_mod_option(tail, patch_mod_javabase); + int res = process_patch_mod_option(tail); if (res != JNI_OK) { return res; } @@ -2822,16 +2826,16 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m return JNI_OK; } -void Arguments::add_patch_mod_prefix(const char* module_name, const char* path, bool* patch_mod_javabase) { +void Arguments::add_patch_mod_prefix(const char* module_name, const char* path) { // For java.base check for duplicate --patch-module options being specified on the command line. // This check is only required for java.base, all other duplicate module specifications // will be checked during module system initialization. The module system initialization // will throw an ExceptionInInitializerError if this situation occurs. if (strcmp(module_name, JAVA_BASE_NAME) == 0) { - if (*patch_mod_javabase) { + if (patch_mod_javabase) { vm_exit_during_initialization("Cannot specify " JAVA_BASE_NAME " more than once to --patch-module"); } else { - *patch_mod_javabase = true; + patch_mod_javabase = true; } } @@ -2883,7 +2887,7 @@ void Arguments::fix_appclasspath() { } } -jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { +jint Arguments::finalize_vm_init_args() { // check if the default lib/endorsed directory exists; if so, error char path[JVM_MAXPATHLEN]; const char* fileSep = os::file_separator(); @@ -2957,9 +2961,6 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { return JNI_ERR; } - if (!CDSConfig::check_vm_args_consistency(patch_mod_javabase, mode_flag_cmd_line)) { - return JNI_ERR; - } #ifndef CAN_SHOW_REGISTERS_ON_ASSERT UNSUPPORTED_OPTION(ShowRegistersOnAssert); diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index cac0296b327..f18e44bbb55 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -290,7 +290,7 @@ class Arguments : AllStatic { static bool create_module_property(const char* prop_name, const char* prop_value, PropertyInternal internal); static bool create_numbered_module_property(const char* prop_base_name, const char* prop_value, unsigned int count); - static int process_patch_mod_option(const char* patch_mod_tail, bool* patch_mod_javabase); + static int process_patch_mod_option(const char* patch_mod_tail); // Aggressive optimization flags. static jint set_aggressive_opts_flags(); @@ -325,8 +325,8 @@ class Arguments : AllStatic { const JavaVMInitArgs *java_tool_options_args, const JavaVMInitArgs *java_options_args, const JavaVMInitArgs *cmd_line_args); - static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, JVMFlagOrigin origin); - static jint finalize_vm_init_args(bool patch_mod_javabase); + static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, JVMFlagOrigin origin); + static jint finalize_vm_init_args(); static bool is_bad_option(const JavaVMOption* option, jboolean ignore, const char* option_type); static bool is_bad_option(const JavaVMOption* option, jboolean ignore) { @@ -474,7 +474,7 @@ class Arguments : AllStatic { static void set_ext_dirs(char *value) { _ext_dirs = os::strdup_check_oom(value); } // Set up the underlying pieces of the boot class path - static void add_patch_mod_prefix(const char *module_name, const char *path, bool* patch_mod_javabase); + static void add_patch_mod_prefix(const char *module_name, const char *path); static void set_boot_class_path(const char *value, bool has_jimage) { // During start up, set by os::set_boot_path() assert(get_boot_class_path() == nullptr, "Boot class path previously set"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java index 8e33bf0bb0c..6ab9b18c1e9 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -63,6 +63,7 @@ public class CommandLineFlagCombo { "-Xint", "-Xmixed", "-Xcomp", + "-XX:+SegmentedCodeCache", }; public static void main(String[] args) throws Exception { From faa109e8d40dbe8c6beb26bd8f9e5f6f63e4cab1 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Wed, 4 Dec 2024 18:52:07 +0000 Subject: [PATCH 131/171] 8340580: Characters in Document diagrams not in the middle Move text in Document-{insert,remove}.svg by 0.5 of pixel Reviewed-by: azvegint, kizune --- .../swing/text/doc-files/Document-insert.svg | 38 +++++++++--------- .../swing/text/doc-files/Document-remove.svg | 40 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.svg b/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.svg index 09a74210d41..8bb51a43055 100644 --- a/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.svg +++ b/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-insert.svg @@ -97,49 +97,49 @@ - T + T - h + h - e + e - + - q + q - u + u - i + i - c + c - k + k - + - b + b - r + r - o + o - w + w - n + n - + - f + f - o + o - x + x diff --git a/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-remove.svg b/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-remove.svg index 2c8e91923f5..e47a5f750d0 100644 --- a/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-remove.svg +++ b/src/java.desktop/share/classes/javax/swing/text/doc-files/Document-remove.svg @@ -1,7 +1,7 @@