diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index a5113f994df..c30234aae38 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2944,6 +2944,23 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Big-endian 128-bit + 64-bit -> 128-bit addition. + // Inputs: 128-bits. in is preserved. + // The least-significant 64-bit word is in the upper dword of each vector. + // inc (the 64-bit increment) is preserved. Its lower dword must be zero. + // Output: result + void be_add_128_64(FloatRegister result, FloatRegister in, + FloatRegister inc, FloatRegister tmp) { + assert_different_registers(result, tmp, inc); + + __ addv(result, __ T2D, in, inc); // Add inc to the least-significant dword of + // input + __ cm(__ HI, tmp, __ T2D, inc, result);// Check for result overflowing + __ ext(tmp, __ T16B, tmp, tmp, 0x08); // Swap LSD of comparison result to MSD and + // MSD == 0 (must be!) to LSD + __ subv(result, __ T2D, result, tmp); // Subtract -1 from MSD if there was an overflow + } + // CTR AES crypt. // Arguments: // @@ -3053,13 +3070,16 @@ class StubGenerator: public StubCodeGenerator { // Setup the counter __ movi(v4, __ T4S, 0); __ movi(v5, __ T4S, 1); - __ ins(v4, __ S, v5, 3, 3); // v4 contains { 0, 0, 0, 1 } + __ ins(v4, __ S, v5, 2, 2); // v4 contains { 0, 1 } - __ ld1(v0, __ T16B, counter); // Load the counter into v0 - __ rev32(v16, __ T16B, v0); - __ addv(v16, __ T4S, v16, v4); - __ rev32(v16, __ T16B, v16); - __ st1(v16, __ T16B, counter); // Save the incremented counter back + // 128-bit big-endian increment + __ ld1(v0, __ T16B, counter); + __ rev64(v16, __ T16B, v0); + be_add_128_64(v16, v16, v4, /*tmp*/v5); + __ rev64(v16, __ T16B, v16); + __ st1(v16, __ T16B, counter); + // Previous counter value is in v0 + // v4 contains { 0, 1 } { // We have fewer than bulk_width blocks of data left. Encrypt @@ -3091,9 +3111,9 @@ class StubGenerator: public StubCodeGenerator { // Increment the counter, store it back __ orr(v0, __ T16B, v16, v16); - __ rev32(v16, __ T16B, v16); - __ addv(v16, __ T4S, v16, v4); - __ rev32(v16, __ T16B, v16); + __ rev64(v16, __ T16B, v16); + be_add_128_64(v16, v16, v4, /*tmp*/v5); + __ rev64(v16, __ T16B, v16); __ st1(v16, __ T16B, counter); // Save the incremented counter back __ b(inner_loop); @@ -3141,7 +3161,7 @@ class StubGenerator: public StubCodeGenerator { // Keys should already be loaded into the correct registers __ ld1(v0, __ T16B, counter); // v0 contains the first counter - __ rev32(v16, __ T16B, v0); // v16 contains byte-reversed counter + __ rev64(v16, __ T16B, v0); // v16 contains byte-reversed counter // AES/CTR loop { @@ -3151,12 +3171,12 @@ class StubGenerator: public StubCodeGenerator { // Setup the counters __ movi(v8, __ T4S, 0); __ movi(v9, __ T4S, 1); - __ ins(v8, __ S, v9, 3, 3); // v8 contains { 0, 0, 0, 1 } + __ ins(v8, __ S, v9, 2, 2); // v8 contains { 0, 1 } for (int i = 0; i < bulk_width; i++) { FloatRegister v0_ofs = as_FloatRegister(v0->encoding() + i); - __ rev32(v0_ofs, __ T16B, v16); - __ addv(v16, __ T4S, v16, v8); + __ rev64(v0_ofs, __ T16B, v16); + be_add_128_64(v16, v16, v8, /*tmp*/v9); } __ ld1(v8, v9, v10, v11, __ T16B, __ post(in, 4 * 16)); @@ -3186,7 +3206,7 @@ class StubGenerator: public StubCodeGenerator { } // Save the counter back where it goes - __ rev32(v16, __ T16B, v16); + __ rev64(v16, __ T16B, v16); __ st1(v16, __ T16B, counter); __ pop(saved_regs, sp); @@ -7221,7 +7241,6 @@ class StubGenerator: public StubCodeGenerator { // The handle is dereferenced through a load barrier. static void jfr_epilogue(MacroAssembler* _masm) { __ reset_last_Java_frame(true); - __ resolve_global_jobject(r0, rscratch1, rscratch2); } // For c2: c_rarg0 is junk, call to runtime to write a checkpoint. @@ -7250,6 +7269,7 @@ class StubGenerator: public StubCodeGenerator { jfr_prologue(the_pc, _masm, rthread); __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::write_checkpoint), 1); jfr_epilogue(_masm); + __ resolve_global_jobject(r0, rscratch1, rscratch2); __ leave(); __ ret(lr); @@ -7263,6 +7283,44 @@ class StubGenerator: public StubCodeGenerator { return stub; } + // For c2: call to return a leased buffer. + static RuntimeStub* generate_jfr_return_lease() { + enum layout { + rbp_off, + rbpH_off, + return_off, + return_off2, + framesize // inclusive of return address + }; + + int insts_size = 1024; + int locs_size = 64; + CodeBuffer code("jfr_return_lease", insts_size, locs_size); + OopMapSet* oop_maps = new OopMapSet(); + MacroAssembler* masm = new MacroAssembler(&code); + MacroAssembler* _masm = masm; + + address start = __ pc(); + __ enter(); + int frame_complete = __ pc() - start; + address the_pc = __ pc(); + jfr_prologue(the_pc, _masm, rthread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), 1); + jfr_epilogue(_masm); + + __ leave(); + __ ret(lr); + + OopMap* map = new OopMap(framesize, 1); // rfp + oop_maps->add_gc_map(the_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub("jfr_return_lease", &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + return stub; + } + #endif // INCLUDE_JFR // Continuation point for throwing of implicit exceptions that are @@ -8261,10 +8319,18 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) } +#if INCLUDE_JFR + void generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); + } +#endif // INCLUDE_JFR + void generate_final_stubs() { // support for verify_oop (must happen after universe_init) if (VerifyOops) { diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp index f6e7b46b030..fe01d5f3fa5 100644 --- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp @@ -3072,6 +3072,46 @@ class StubGenerator: public StubCodeGenerator { return stub; } + // For c2: call to return a leased buffer. + static RuntimeStub* generate_jfr_return_lease() { + enum layout { + r1_off, + r2_off, + return_off, + framesize // inclusive of return address + }; + + CodeBuffer code("jfr_return_lease", 512, 64); + MacroAssembler* masm = new MacroAssembler(&code); + + address start = __ pc(); + __ raw_push(R1, R2, LR); + address the_pc = __ pc(); + + int frame_complete = the_pc - start; + + __ set_last_Java_frame(SP, FP, true, Rtemp); + __ mov(c_rarg0, Rthread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), c_rarg0); + __ reset_last_Java_frame(Rtemp); + + __ raw_pop(R1, R2, LR); + __ ret(); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 1); + oop_maps->add_gc_map(frame_complete, map); + + RuntimeStub* stub = + RuntimeStub::new_runtime_stub(code.name(), + &code, + frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, + false); + return stub; + } + #endif // INCLUDE_JFR //--------------------------------------------------------------------------- @@ -3116,10 +3156,18 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) } +#if INCLUDE_JFR + void generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); + } +#endif // INCLUDE_JFR + void generate_final_stubs() { // Generates all stubs and initializes the entry points diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 2e9e18bd8db..c06f22e0fbb 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -4680,6 +4680,41 @@ class StubGenerator: public StubCodeGenerator { return stub; } + // For c2: call to return a leased buffer. + RuntimeStub* generate_jfr_return_lease() { + CodeBuffer code("jfr_return_lease", 512, 64); + MacroAssembler* _masm = new MacroAssembler(&code); + + Register tmp1 = R10_ARG8; + Register tmp2 = R9_ARG7; + + int framesize = frame::native_abi_reg_args_size / VMRegImpl::stack_slot_size; + address start = __ pc(); + __ mflr(tmp1); + __ std(tmp1, _abi0(lr), R1_SP); // save return pc + __ push_frame_reg_args(0, tmp1); + int frame_complete = __ pc() - start; + __ set_last_Java_frame(R1_SP, noreg); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), R16_thread); + address calls_return_pc = __ last_calls_return_pc(); + __ reset_last_Java_frame(); + __ pop_frame(); + __ ld(tmp1, _abi0(lr), R1_SP); + __ mtlr(tmp1); + __ blr(); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 0); + oop_maps->add_gc_map(calls_return_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub(code.name(), + &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + return stub; + } + #endif // INCLUDE_JFR @@ -4728,10 +4763,18 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) } +#if INCLUDE_JFR + void generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); + } +#endif // INCLUDE_JFR + void generate_final_stubs() { // Generates all stubs and initializes the entry points diff --git a/src/hotspot/cpu/riscv/icache_riscv.cpp b/src/hotspot/cpu/riscv/icache_riscv.cpp index a6a5f42b47a..d615dcfb9e9 100644 --- a/src/hotspot/cpu/riscv/icache_riscv.cpp +++ b/src/hotspot/cpu/riscv/icache_riscv.cpp @@ -1,6 +1,7 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2023, 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 @@ -25,6 +26,8 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "riscv_flush_icache.hpp" +#include "runtime/java.hpp" #include "runtime/icache.hpp" #define __ _masm-> @@ -33,16 +36,22 @@ static int icache_flush(address addr, int lines, int magic) { // To make a store to instruction memory visible to all RISC-V harts, // the writing hart has to execute a data FENCE before requesting that // all remote RISC-V harts execute a FENCE.I. - // - // No sush assurance is defined at the interface level of the builtin - // method, and so we should make sure it works. + + // We need to make sure stores happens before the I/D cache synchronization. __asm__ volatile("fence rw, rw" : : : "memory"); - __builtin___clear_cache(addr, addr + (lines << ICache::log2_line_size)); + RiscvFlushIcache::flush((uintptr_t)addr, ((uintptr_t)lines) << ICache::log2_line_size); + return magic; } void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) { + // Only riscv_flush_icache is supported as I-cache synchronization. + // We must make sure the VM can execute such without error. + if (!RiscvFlushIcache::test()) { + vm_exit_during_initialization("Unable to synchronize I-cache"); + } + address start = (address)icache_flush; *flush_icache_stub = (ICache::flush_icache_stub_t)start; diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 0522c8070fb..60c1fc8c3d5 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -3959,7 +3959,6 @@ class StubGenerator: public StubCodeGenerator { static void jfr_epilogue(MacroAssembler* _masm) { __ reset_last_Java_frame(true); - __ resolve_global_jobject(x10, t0, t1); } // For c2: c_rarg0 is junk, call to runtime to write a checkpoint. // It returns a jobject handle to the event writer. @@ -3988,6 +3987,7 @@ class StubGenerator: public StubCodeGenerator { __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::write_checkpoint), 1); jfr_epilogue(_masm); + __ resolve_global_jobject(x10, t0, t1); __ leave(); __ ret(); @@ -4001,6 +4001,44 @@ class StubGenerator: public StubCodeGenerator { return stub; } + // For c2: call to return a leased buffer. + static RuntimeStub* generate_jfr_return_lease() { + enum layout { + fp_off, + fp_off2, + return_off, + return_off2, + framesize // inclusive of return address + }; + + int insts_size = 1024; + int locs_size = 64; + CodeBuffer code("jfr_return_lease", insts_size, locs_size); + OopMapSet* oop_maps = new OopMapSet(); + MacroAssembler* masm = new MacroAssembler(&code); + MacroAssembler* _masm = masm; + + address start = __ pc(); + __ enter(); + int frame_complete = __ pc() - start; + address the_pc = __ pc(); + jfr_prologue(the_pc, _masm, xthread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), 1); + + jfr_epilogue(_masm); + __ leave(); + __ ret(); + + OopMap* map = new OopMap(framesize, 1); + oop_maps->add_gc_map(the_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub("jfr_return_lease", &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + return stub; + } + #endif // INCLUDE_JFR #undef __ @@ -4044,10 +4082,18 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) } +#if INCLUDE_JFR + void generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); + } +#endif // INCLUDE_JFR + void generate_final_stubs() { // support for verify_oop (must happen after universe_init) if (VerifyOops) { diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index d5465343131..a26bcd04de2 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3085,7 +3085,14 @@ class StubGenerator: public StubCodeGenerator { Unimplemented(); return nullptr; } - #endif // INCLUD_JFR + + RuntimeStub* generate_jfr_return_lease() { + if (!Continuations::enabled()) return nullptr; + Unimplemented(); + return nullptr; + } + + #endif // INCLUDE_JFR void generate_initial_stubs() { // Generates all stubs and initializes the entry points. @@ -3133,9 +3140,17 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) + } + +#if INCLUDE_JFR + void generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); } +#endif // INCLUDE_JFR void generate_final_stubs() { // Generates all stubs and initializes the entry points. diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 2902abfc661..d619ccaf251 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -4408,6 +4408,14 @@ void Assembler::evpcmpuw(KRegister kdst, XMMRegister nds, XMMRegister src, Compa emit_int24(0x3E, (0xC0 | encode), vcc); } +void Assembler::evpcmpuq(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len) { + assert(VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x1E, (0xC0 | encode), vcc); +} + void Assembler::evpcmpuw(KRegister kdst, XMMRegister nds, Address src, ComparisonPredicate vcc, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); InstructionMark im(this); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 5102e2c3849..a228bd28db7 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1795,6 +1795,8 @@ class Assembler : public AbstractAssembler { void evpcmpuw(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len); void evpcmpuw(KRegister kdst, XMMRegister nds, Address src, ComparisonPredicate vcc, int vector_len); + void evpcmpuq(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len); + void pcmpeqw(XMMRegister dst, XMMRegister src); void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqw(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 66f3ebe7b28..da3bc94f304 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -9270,6 +9270,17 @@ void MacroAssembler::evpandq(XMMRegister dst, XMMRegister nds, AddressLiteral sr } } +void MacroAssembler::evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, AddressLiteral src, bool merge, int vector_len, Register rscratch) { + assert(rscratch != noreg || always_reachable(src), "missing"); + + if (reachable(src)) { + Assembler::evpaddq(dst, mask, nds, as_Address(src), merge, vector_len); + } else { + lea(rscratch, src); + Assembler::evpaddq(dst, mask, nds, Address(rscratch, 0), merge, vector_len); + } +} + void MacroAssembler::evporq(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register rscratch) { assert(rscratch != noreg || always_reachable(src), "missing"); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index e4c4b0f10b6..61db66ae00f 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -1788,6 +1788,9 @@ class MacroAssembler: public Assembler { using Assembler::evpandq; void evpandq(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register rscratch = noreg); + using Assembler::evpaddq; + void evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, AddressLiteral src, bool merge, int vector_len, Register rscratch = noreg); + using Assembler::evporq; void evporq(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len, Register rscratch = noreg); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp index ed3e75ee1d3..41294a4f207 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp @@ -4011,7 +4011,6 @@ class StubGenerator: public StubCodeGenerator { Register java_thread = rdi; __ get_thread(java_thread); __ reset_last_Java_frame(java_thread, true); - __ resolve_global_jobject(rax, java_thread, rdx); } // For c2: c_rarg0 is junk, call to runtime to write a checkpoint. @@ -4044,6 +4043,7 @@ class StubGenerator: public StubCodeGenerator { jfr_prologue(the_pc, _masm); __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::write_checkpoint), 1); jfr_epilogue(_masm); + __ resolve_global_jobject(rax, rdi, rdx); __ leave(); __ ret(0); @@ -4057,6 +4057,47 @@ class StubGenerator: public StubCodeGenerator { return stub; } + // For c2: call to return a leased buffer. + static RuntimeStub* generate_jfr_return_lease() { + enum layout { + FPUState_off = 0, + rbp_off = FPUStateSizeInWords, + rdi_off, + rsi_off, + rcx_off, + rbx_off, + saved_argument_off, + saved_argument_off2, // 2nd half of double + framesize + }; + + int insts_size = 1024; + int locs_size = 64; + CodeBuffer code("jfr_return_lease", insts_size, locs_size); + OopMapSet* oop_maps = new OopMapSet(); + MacroAssembler* masm = new MacroAssembler(&code); + MacroAssembler* _masm = masm; + + address start = __ pc(); + __ enter(); + int frame_complete = __ pc() - start; + address the_pc = __ pc(); + jfr_prologue(the_pc, _masm); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), 1); + jfr_epilogue(_masm); + __ leave(); + __ ret(0); + + OopMap* map = new OopMap(framesize, 1); // rbp + oop_maps->add_gc_map(the_pc - start, map); + + RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) + RuntimeStub::new_runtime_stub("jfr_return_lease", &code, frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, false); + return stub; + } + #endif // INCLUDE_JFR //--------------------------------------------------------------------------- @@ -4148,10 +4189,18 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) } +#if INCLUDE_JFR + void generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); + } +#endif // INCLUDE_JFR + void generate_final_stubs() { // Generates all stubs and initializes the entry points diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 6cd17651514..014737322d8 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -3750,6 +3750,47 @@ RuntimeStub* StubGenerator::generate_jfr_write_checkpoint() { return stub; } +// For c2: call to return a leased buffer. +RuntimeStub* StubGenerator::generate_jfr_return_lease() { + enum layout { + rbp_off, + rbpH_off, + return_off, + return_off2, + framesize // inclusive of return address + }; + + CodeBuffer code("jfr_return_lease", 1024, 64); + MacroAssembler* _masm = new MacroAssembler(&code); + address start = __ pc(); + + __ enter(); + address the_pc = __ pc(); + + int frame_complete = the_pc - start; + + __ set_last_Java_frame(rsp, rbp, the_pc, rscratch2); + __ movptr(c_rarg0, r15_thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, JfrIntrinsicSupport::return_lease), 1); + __ reset_last_Java_frame(true); + + __ leave(); + __ ret(0); + + OopMapSet* oop_maps = new OopMapSet(); + OopMap* map = new OopMap(framesize, 1); + oop_maps->add_gc_map(frame_complete, map); + + RuntimeStub* stub = + RuntimeStub::new_runtime_stub(code.name(), + &code, + frame_complete, + (framesize >> (LogBytesPerWord - LogBytesPerInt)), + oop_maps, + false); + return stub; +} + #endif // INCLUDE_JFR // Continuation point for throwing of implicit exceptions that are @@ -3945,10 +3986,18 @@ void StubGenerator::generate_continuation_stubs() { StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); - JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();) - JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();) + JFR_ONLY(generate_jfr_stubs();) } +#if INCLUDE_JFR +void StubGenerator::generate_jfr_stubs() { + StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint(); + StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point(); + StubRoutines::_jfr_return_lease_stub = generate_jfr_return_lease(); + StubRoutines::_jfr_return_lease = StubRoutines::_jfr_return_lease_stub->entry_point(); +} +#endif + void StubGenerator::generate_final_stubs() { // Generates the rest of stubs and initializes the entry points diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 86e3b169554..94545965fc9 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -364,7 +364,8 @@ class StubGenerator: public StubCodeGenerator { // Utility routine for increase 128bit counter (iv in CTR mode) void inc_counter(Register reg, XMMRegister xmmdst, int inc_delta, Label& next_block); - + void ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2, + int vector_len, KRegister ktmp, Register rscratch = noreg); void generate_aes_stubs(); @@ -519,12 +520,13 @@ class StubGenerator: public StubCodeGenerator { address generate_cont_returnBarrier_exception(); #if INCLUDE_JFR - + void generate_jfr_stubs(); // For c2: c_rarg0 is junk, call to runtime to write a checkpoint. // It returns a jobject handle to the event writer. // The handle is dereferenced and the return value is the event writer oop. RuntimeStub* generate_jfr_write_checkpoint(); - + // For c2: call to runtime to return a buffer lease. + RuntimeStub* generate_jfr_return_lease(); #endif // INCLUDE_JFR // Continuation point for throwing of implicit exceptions that are diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 3e1439f2a02..7b1fb54853e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -121,6 +121,16 @@ static address counter_mask_linc32_addr() { return (address)COUNTER_MASK_LINC32; } +ATTRIBUTE_ALIGNED(64) uint64_t COUNTER_MASK_ONES[] = { + 0x0000000000000000UL, 0x0000000000000001UL, + 0x0000000000000000UL, 0x0000000000000001UL, + 0x0000000000000000UL, 0x0000000000000001UL, + 0x0000000000000000UL, 0x0000000000000001UL, +}; +static address counter_mask_ones_addr() { + return (address)COUNTER_MASK_ONES; +} + ATTRIBUTE_ALIGNED(64) static const uint64_t GHASH_POLYNOMIAL_REDUCTION[] = { 0x00000001C2000000UL, 0xC200000000000000UL, 0x00000001C2000000UL, 0xC200000000000000UL, @@ -1623,6 +1633,17 @@ void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, Re __ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit); } +// Add 128-bit integers in xmmsrc1 to xmmsrc2, then place the result in xmmdst. +// Clobber ktmp and rscratch. +// Used by aesctr_encrypt. +void StubGenerator::ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2, + int vector_len, KRegister ktmp, Register rscratch) { + __ vpaddq(xmmdst, xmmsrc1, xmmsrc2, vector_len); + __ evpcmpuq(ktmp, xmmdst, xmmsrc2, __ lt, vector_len); + __ kshiftlbl(ktmp, ktmp, 1); + __ evpaddq(xmmdst, ktmp, xmmdst, ExternalAddress(counter_mask_ones_addr()), /*merge*/true, + vector_len, rscratch); +} // AES-ECB Encrypt Operation void StubGenerator::aesecb_encrypt(Register src_addr, Register dest_addr, Register key, Register len) { @@ -2046,7 +2067,6 @@ void StubGenerator::aesecb_decrypt(Register src_addr, Register dest_addr, Regist } - // AES Counter Mode using VAES instructions void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Register key, Register counter, Register len_reg, Register used, Register used_addr, Register saved_encCounter_start) { @@ -2104,14 +2124,17 @@ void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Regist // The counter is incremented after each block i.e. 16 bytes is processed; // each zmm register has 4 counter values as its MSB // the counters are incremented in parallel - __ vpaddd(xmm8, xmm8, ExternalAddress(counter_mask_linc0_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm9, xmm8, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm10, xmm9, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm11, xmm10, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm12, xmm11, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm13, xmm12, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm14, xmm13, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); - __ vpaddd(xmm15, xmm14, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); + + __ evmovdquq(xmm19, ExternalAddress(counter_mask_linc0_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + __ evmovdquq(xmm19, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); + ev_add128(xmm9, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm10, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm11, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm12, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm13, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm14, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm15, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // load linc32 mask in zmm register.linc32 increments counter by 32 __ evmovdquq(xmm19, ExternalAddress(counter_mask_linc32_addr()), Assembler::AVX_512bit, r15 /*rscratch*/); @@ -2159,21 +2182,21 @@ void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Regist // This is followed by incrementing counter values in zmm8-zmm15. // Since we will be processing 32 blocks at a time, the counter is incremented by 32. roundEnc(xmm21, 7); - __ vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm22, 7); - __ vpaddq(xmm9, xmm9, xmm19, Assembler::AVX_512bit); + ev_add128(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm23, 7); - __ vpaddq(xmm10, xmm10, xmm19, Assembler::AVX_512bit); + ev_add128(xmm10, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm24, 7); - __ vpaddq(xmm11, xmm11, xmm19, Assembler::AVX_512bit); + ev_add128(xmm11, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm25, 7); - __ vpaddq(xmm12, xmm12, xmm19, Assembler::AVX_512bit); + ev_add128(xmm12, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm26, 7); - __ vpaddq(xmm13, xmm13, xmm19, Assembler::AVX_512bit); + ev_add128(xmm13, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm27, 7); - __ vpaddq(xmm14, xmm14, xmm19, Assembler::AVX_512bit); + ev_add128(xmm14, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm28, 7); - __ vpaddq(xmm15, xmm15, xmm19, Assembler::AVX_512bit); + ev_add128(xmm15, xmm15, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm29, 7); __ cmpl(rounds, 52); @@ -2251,8 +2274,8 @@ void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Regist __ vpshufb(xmm3, xmm11, xmm16, Assembler::AVX_512bit); __ evpxorq(xmm3, xmm3, xmm20, Assembler::AVX_512bit); // Increment counter values by 16 - __ vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); - __ vpaddq(xmm9, xmm9, xmm19, Assembler::AVX_512bit); + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // AES encode rounds roundEnc(xmm21, 3); roundEnc(xmm22, 3); @@ -2319,7 +2342,7 @@ void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Regist __ vpshufb(xmm1, xmm9, xmm16, Assembler::AVX_512bit); __ evpxorq(xmm1, xmm1, xmm20, Assembler::AVX_512bit); // increment counter by 8 - __ vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // AES encode roundEnc(xmm21, 1); roundEnc(xmm22, 1); @@ -2376,8 +2399,9 @@ void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Regist // XOR counter with first roundkey __ vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit); __ evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit); + // Increment counter - __ vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); __ vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_512bit); __ vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_512bit); __ vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_512bit); @@ -2427,7 +2451,7 @@ void StubGenerator::aesctr_encrypt(Register src_addr, Register dest_addr, Regist __ evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_128bit); __ vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_128bit); // Increment counter by 1 - __ vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_128bit); + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_128bit, /*ktmp*/k1, r15 /*rscratch*/); __ vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_128bit); __ vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_128bit); __ vaesenc(xmm0, xmm0, xmm24, Assembler::AVX_128bit); diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.cpp new file mode 100644 index 00000000000..89ca0188791 --- /dev/null +++ b/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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" +#include "logging/log.hpp" +#include "riscv_flush_icache.hpp" +#include "runtime/os.hpp" +#include "runtime/vm_version.hpp" +#include "utilities/debug.hpp" + +#include +#include + +#define check_with_errno(check_type, cond, msg) \ + do { \ + int err = errno; \ + check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err), \ + os::errno_name(err)); \ +} while (false) + +#define assert_with_errno(cond, msg) check_with_errno(assert, cond, msg) +#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg) + +#ifndef NR_riscv_flush_icache +#ifndef NR_arch_specific_syscall +#define NR_arch_specific_syscall 244 +#endif +#define NR_riscv_flush_icache (NR_arch_specific_syscall + 15) +#endif + +#define SYS_RISCV_FLUSH_ICACHE_LOCAL 1UL +#define SYS_RISCV_FLUSH_ICACHE_ALL 0UL + +static long sys_flush_icache(uintptr_t start, uintptr_t end , uintptr_t flags) { + return syscall(NR_riscv_flush_icache, start, end, flags); +} + +bool RiscvFlushIcache::test() { + alignas(64) char memory[64]; + long ret = sys_flush_icache((uintptr_t)&memory[0], + (uintptr_t)&memory[sizeof(memory) - 1], + SYS_RISCV_FLUSH_ICACHE_ALL); + if (ret == 0) { + return true; + } + int err = errno; \ + log_error(os)("Syscall: RISCV_FLUSH_ICACHE not available; error='%s' (errno=%s)", + os::strerror(err), os::errno_name(err)); + return false; +} + +void RiscvFlushIcache::flush(uintptr_t start, uintptr_t end) { + long ret = sys_flush_icache(start, end, SYS_RISCV_FLUSH_ICACHE_ALL); + guarantee_with_errno(ret == 0, "riscv_flush_icache failed"); +} diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp b/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp new file mode 100644 index 00000000000..34742c173ab --- /dev/null +++ b/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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. + * + */ + +#ifndef OS_LINUX_RISCV_FLUSH_ICACHE_LINUX_HPP +#define OS_LINUX_RISCV_FLUSH_ICACHE_LINUX_HPP + +#include "memory/allStatic.hpp" +#include "runtime/vm_version.hpp" +#include "utilities/growableArray.hpp" + +class RiscvFlushIcache: public AllStatic { + public: + static bool test(); + static void flush(uintptr_t start, uintptr_t end); +}; + +#endif // OS_LINUX_RISCV_FLUSH_ICACHE_LINUX_HPP diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index bcd89beb7d5..20101fd02dd 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -469,10 +469,6 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { SwitchRangeArray* create_lookup_ranges(LookupSwitch* x); void do_SwitchRanges(SwitchRangeArray* x, LIR_Opr value, BlockBegin* default_sux); -#ifdef JFR_HAVE_INTRINSICS - void do_getEventWriter(Intrinsic* x); -#endif - void do_RuntimeCall(address routine, Intrinsic* x); ciKlass* profile_type(ciMethodData* md, int md_first_offset, int md_offset, intptr_t profiled_k, diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp index e5b77f58258..22bf4bc2cff 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp @@ -228,24 +228,23 @@ void RangeCheckEliminator::Visitor::do_ArithmeticOp(ArithmeticOp *ao) { Bound* y_bound = _rce->get_bound(y); if (x_bound->lower() >= 0 && x_bound->lower_instr() == nullptr && y->as_ArrayLength() != nullptr) { _bound = new Bound(0, nullptr, -1, y); - } else if (y->type()->as_IntConstant() && y->type()->as_IntConstant()->value() != 0) { + } else if (x_bound->has_lower() && x_bound->lower() >= 0 && y->type()->as_IntConstant() && + y->type()->as_IntConstant()->value() != 0 && y->type()->as_IntConstant()->value() != min_jint) { // The binary % operator is said to yield the remainder of its operands from an implied division; the // left-hand operand is the dividend and the right-hand operand is the divisor. // - // % operator follows from this rule that the result of the remainder operation can be negative only + // It follows from this rule that the result of the remainder operation can be negative only // if the dividend is negative, and can be positive only if the dividend is positive. Moreover, the - // magnitude of the result is always less than the magnitude of the divisor(See JLS 15.17.3). + // magnitude of the result is always less than the magnitude of the divisor (see JLS 15.17.3). // // So if y is a constant integer and not equal to 0, then we can deduce the bound of remainder operation: // x % -y ==> [0, y - 1] Apply RCE // x % y ==> [0, y - 1] Apply RCE // -x % y ==> [-y + 1, 0] // -x % -y ==> [-y + 1, 0] - if (x_bound->has_lower() && x_bound->lower() >= 0) { - _bound = new Bound(0, nullptr, y->type()->as_IntConstant()->value() - 1, nullptr); - } else { - _bound = new Bound(); - } + // + // Use the absolute value of y as an upper bound. Skip min_jint because abs(min_jint) is undefined. + _bound = new Bound(0, nullptr, abs(y->type()->as_IntConstant()->value()) - 1, nullptr); } else { _bound = new Bound(); } @@ -270,18 +269,16 @@ void RangeCheckEliminator::Visitor::do_ArithmeticOp(ArithmeticOp *ao) { Bound * bound = _rce->get_bound(y); if (bound->has_upper() && bound->has_lower()) { - // TODO: consider using __builtin_add_overflow - jlong new_lowerl = ((jlong)bound->lower()) + const_value; - jint new_lower = low(new_lowerl); - jlong new_upperl = ((jlong)bound->upper()) + const_value; - jint new_upper = low(new_upperl); - - if (((jlong)new_lower) == new_lowerl && ((jlong)new_upper == new_upperl)) { - Bound *newBound = new Bound(new_lower, bound->lower_instr(), new_upper, bound->upper_instr()); - _bound = newBound; - } else { - // overflow + jint t_lo = bound->lower(); + jint t_hi = bound->upper(); + jint new_lower = java_add(t_lo, const_value); + jint new_upper = java_add(t_hi, const_value); + bool overflow = ((const_value < 0 && (new_lower > t_lo)) || + (const_value > 0 && (new_upper < t_hi))); + if (overflow) { _bound = new Bound(); + } else { + _bound = new Bound(new_lower, bound->lower_instr(), new_upper, bound->upper_instr()); } } else { _bound = new Bound(); @@ -1559,7 +1556,6 @@ void RangeCheckEliminator::Bound::add_assertion(Instruction *instruction, Instru NOT_PRODUCT(ao->set_printable_bci(position->printable_bci())); result = result->insert_after(ao); compare_with = ao; - // TODO: Check that add operation does not overflow! } } assert(compare_with != nullptr, "You have to compare with something!"); diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 4f427d60876..546c7f1feed 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -88,10 +88,10 @@ product(ccstr, ExtraSharedClassListFile, nullptr, \ "Extra classlist for building the CDS archive file") \ \ - product(int, ArchiveRelocationMode, 0, DIAGNOSTIC, \ + product(int, ArchiveRelocationMode, 1, DIAGNOSTIC, \ "(0) first map at preferred address, and if " \ - "unsuccessful, map at alternative address (default); " \ - "(1) always map at alternative address; " \ + "unsuccessful, map at alternative address; " \ + "(1) always map at alternative address (default); " \ "(2) always map at preferred address, and if unsuccessful, " \ "do not map the archive") \ range(0, 2) \ diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index 75da90615b6..c037e8bd525 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -302,9 +302,13 @@ JVM_ENTRY_NO_ENV(jobject, jfr_new_event_writer(JNIEnv* env, jclass cls)) return JfrJavaEventWriter::new_event_writer(thread); JVM_END -JVM_ENTRY_NO_ENV(jboolean, jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size)) - return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread); -JVM_END +NO_TRANSITION(void, jfr_event_writer_flush(JNIEnv* env, jclass jvm, jobject writer, jint used_size, jint requested_size)) + JfrJavaEventWriter::flush(writer, used_size, requested_size, JavaThread::current()); +NO_TRANSITION_END + +NO_TRANSITION(jlong, jfr_commit(JNIEnv* env, jclass jvm, jlong next_position)) + return JfrJavaEventWriter::commit(next_position); +NO_TRANSITION_END JVM_ENTRY_NO_ENV(void, jfr_flush(JNIEnv* env, jobject jvm)) JfrRepository::flush(thread); @@ -401,3 +405,7 @@ JVM_ENTRY_NO_ENV(jlong, jfr_host_total_memory(JNIEnv* env, jobject jvm)) return os::physical_memory(); #endif JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) + EventDataLoss::commit(bytes, min_jlong); +JVM_END diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index eda89c3bce4..c7efe1c6a64 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -121,8 +121,11 @@ jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls); jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size); -void JNICALL jfr_flush(JNIEnv* env, jobject jvm); -void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg); +jlong JNICALL jfr_commit(JNIEnv* env, jclass cls, jlong next_position); + +void JNICALL jfr_flush(JNIEnv* env, jclass jvm); + +void JNICALL jfr_abort(JNIEnv* env, jclass jvm, jstring errorMsg); jboolean JNICALL jfr_add_string_constant(JNIEnv* env, jclass jvm, jlong id, jstring string); @@ -162,6 +165,8 @@ jboolean JNICALL jfr_is_containerized(JNIEnv* env, jobject jvm); jlong JNICALL jfr_host_total_memory(JNIEnv* env, jobject jvm); +void JNICALL jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes); + #ifdef __cplusplus } #endif diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 4cbe85f49b8..7c571abbd69 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -71,7 +71,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"getTypeId", (char*)"(Ljava/lang/Class;)J", (void*)jfr_type_id, (char*)"getEventWriter", (char*)"()Ljdk/jfr/internal/event/EventWriter;", (void*)jfr_get_event_writer, (char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/event/EventWriter;", (void*)jfr_new_event_writer, - (char*)"flush", (char*)"(Ljdk/jfr/internal/event/EventWriter;II)Z", (void*)jfr_event_writer_flush, + (char*)"flush", (char*)"(Ljdk/jfr/internal/event/EventWriter;II)V", (void*)jfr_event_writer_flush, + (char*)"commit", (char*)"(J)J", (void*)jfr_commit, (char*)"flush", (char*)"()V", (void*)jfr_flush, (char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location, (char*)"setDumpPath", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_dump_path, @@ -95,7 +96,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"isExcluded", (char*)"(Ljava/lang/Class;)Z", (void*)jfr_is_class_excluded, (char*)"isInstrumented", (char*)"(Ljava/lang/Class;)Z", (void*) jfr_is_class_instrumented, (char*)"isContainerized", (char*)"()Z", (void*) jfr_is_containerized, - (char*)"hostTotalMemory", (char*)"()J", (void*) jfr_host_total_memory + (char*)"hostTotalMemory", (char*)"()J", (void*) jfr_host_total_memory, + (char*)"emitDataLoss", (char*)"(J)V", (void*)jfr_emit_data_loss }; const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index 680e6ba621c..8da6600f89e 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -38,6 +38,7 @@ #include "jfr/recorder/storage/jfrEpochStorage.inline.hpp" #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp" #include "jfr/recorder/storage/jfrStorageUtils.inline.hpp" +#include "jfr/recorder/stringpool/jfrStringPool.hpp" #include "jfr/support/jfrKlassUnloading.hpp" #include "jfr/support/jfrThreadLocal.hpp" #include "jfr/utilities/jfrBigEndian.hpp" @@ -493,6 +494,7 @@ void JfrCheckpointManager::end_epoch_shift() { debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) JfrTraceIdEpoch::end_epoch_shift(); assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); + JfrStringPool::on_epoch_shift(); } size_t JfrCheckpointManager::write() { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp index 9e0ee0ad9af..bbc14327801 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp @@ -108,22 +108,20 @@ traceid JfrThreadId::jfr_id(const Thread* t, traceid tid) { } // caller needs ResourceMark -const char* get_java_thread_name(const JavaThread* jt, int& length, oop vthread) { +static const char* get_java_thread_name(const JavaThread* jt, int& length, oop vthread) { assert(jt != nullptr, "invariant"); - const char* name_str = ""; - oop thread_obj = vthread != nullptr ? vthread : jt->threadObj(); - if (thread_obj == nullptr) { - if (jt->is_attaching_via_jni()) { - name_str = ""; - } + oop thread_obj; + if (vthread != nullptr) { + thread_obj = vthread; } else { - const oop name = java_lang_Thread::name(thread_obj); - if (name != nullptr) { - name_str = java_lang_String::as_utf8_string(name, length); + thread_obj = jt->threadObj(); + if (thread_obj == nullptr) { + return nullptr; } } - assert(name_str != nullptr, "unexpected null thread name"); - return name_str; + assert(thread_obj != nullptr, "invariant"); + const oop name = java_lang_Thread::name(thread_obj); + return name != nullptr ? java_lang_String::as_utf8_string(name, length) : nullptr; } const char* JfrThreadName::name(const Thread* t, int& length, oop vthread) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp index e7e83131dfd..78ef7f5b523 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp @@ -269,40 +269,48 @@ void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) { JfrThreadState::serialize(writer); } -void JfrThreadConstant::write_name(JfrCheckpointWriter& writer, const char* name, int length) { - if (length == 0) { +void JfrThreadConstant::write_name(JfrCheckpointWriter& writer) { + if (_length == 0) { writer.write_empty_string(); return; } - writer.write(name); + writer.write(_name); +} + +void JfrThreadConstant::write_os_name(JfrCheckpointWriter& writer, bool is_vthread) { + if (is_vthread) { + // Write the null string categorically as the os name for virtual threads. + writer.write((const char*)nullptr); + return; + } + write_name(writer); } void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) { assert(_thread != nullptr, "invariant"); - const bool vthread = _vthread != nullptr; + const bool is_vthread = _vthread != nullptr; writer.write_key(JfrThreadId::jfr_id(_thread, _tid)); - int length = -1; - const char* const name = JfrThreadName::name(_thread, length, _vthread); - write_name(writer, name, length); - writer.write(_vthread != nullptr ? static_cast(0) : JfrThreadId::os_id(_thread)); + _name = JfrThreadName::name(_thread, _length, _vthread); + write_os_name(writer, is_vthread); + writer.write(is_vthread ? static_cast(0) : JfrThreadId::os_id(_thread)); if (!_thread->is_Java_thread()) { - write_name(writer, nullptr, 0); // java name + writer.write((const char*)nullptr); // java name writer.write(0); // java thread id writer.write(0); // java thread group writer.write(false); // isVirtual - } else { - write_name(writer, name, length); - writer.write(JfrThreadId::jfr_id(_thread, _tid)); - // java thread group - VirtualThread threadgroup reserved id 1 - const traceid thread_group_id = vthread ? 1 : - JfrThreadGroup::thread_group_id(JavaThread::cast(_thread), Thread::current()); - writer.write(thread_group_id); - writer.write(vthread); // isVirtual - if (!vthread) { - JfrThreadGroup::serialize(&writer, thread_group_id); - } - // VirtualThread threadgroup already serialized invariant. + return; + } + write_name(writer); + writer.write(JfrThreadId::jfr_id(_thread, _tid)); + // java thread group - VirtualThread threadgroup reserved id 1 + const traceid thread_group_id = is_vthread ? 1 : + JfrThreadGroup::thread_group_id(JavaThread::cast(_thread), Thread::current()); + writer.write(thread_group_id); + writer.write(is_vthread); // isVirtual + if (!is_vthread) { + JfrThreadGroup::serialize(&writer, thread_group_id); } + // VirtualThread threadgroup already serialized invariant. } void BytecodeConstant::serialize(JfrCheckpointWriter& writer) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp index 127db555a62..680629aaa69 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp @@ -107,9 +107,13 @@ class JfrThreadConstant : public JfrSerializer { Thread* _thread; traceid _tid; oop _vthread; - void write_name(JfrCheckpointWriter& writer, const char* name, int length); + const char* _name; + int _length; + void write_name(JfrCheckpointWriter& writer); + void write_os_name(JfrCheckpointWriter& writer, bool is_vthread); public: - JfrThreadConstant(Thread* t, traceid tid, oop vthread = nullptr) : _thread(t), _tid(tid), _vthread(vthread) {} + JfrThreadConstant(Thread* t, traceid tid, oop vthread = nullptr) : + _thread(t), _tid(tid), _vthread(vthread), _name(nullptr), _length(-1) {} void serialize(JfrCheckpointWriter& writer); }; diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp index 00ebd710d30..971377043c3 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp @@ -103,7 +103,7 @@ void JfrPostBox::deposit(int new_messages) { void JfrPostBox::asynchronous_post(int msg) { assert(!is_synchronous(msg), "invariant"); deposit(msg); - JfrMonitorTryLock try_msg_lock(JfrMsg_lock); + JfrMutexTryLock try_msg_lock(JfrMsg_lock); if (try_msg_lock.acquired()) { JfrMsg_lock->notify_all(); } diff --git a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp index e89147b97df..edbf5ef6981 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp @@ -259,3 +259,11 @@ void JfrBuffer::set_context(u1 context) { void JfrBuffer::clear_context() { set(&_context, 0); } + +ByteSize JfrBuffer::pos_offset() { + return byte_offset_of(JfrBuffer, _pos); +} + +ByteSize JfrBuffer::flags_offset() { + return byte_offset_of(JfrBuffer, _flags); +} \ No newline at end of file diff --git a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp index ae059e9230c..7a7e7fdf21f 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "runtime/atomic.hpp" +#include "utilities/sizes.hpp" // // Represents a piece of committed memory. @@ -174,6 +175,11 @@ class JfrBuffer { u1 context() const; void set_context(u1 context); void clear_context(); + + // Code generation + static ByteSize pos_offset(); + static ByteSize flags_offset(); + }; #endif // SHARE_JFR_RECORDER_STORAGE_JFRBUFFER_HPP diff --git a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp index 019793d0456..49f7719d00d 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp @@ -38,8 +38,10 @@ #include "jfr/utilities/jfrIterator.hpp" #include "jfr/utilities/jfrLinkedList.inline.hpp" #include "jfr/utilities/jfrTime.hpp" +#include "jfr/utilities/jfrTryLock.hpp" #include "jfr/writers/jfrNativeEventWriter.hpp" #include "logging/log.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" @@ -172,15 +174,18 @@ static BufferPtr acquire_lease(size_t size, JfrStorageMspace* mspace, JfrStorage } } -static BufferPtr acquire_promotion_buffer(size_t size, JfrStorageMspace* mspace, JfrStorage& storage_instance, size_t retry_count, Thread* thread) { +BufferPtr JfrStorage::acquire_promotion_buffer(size_t size, JfrStorageMspace* mspace, JfrStorage& storage_instance, size_t retry_count, Thread* thread) { assert(size <= mspace->min_element_size(), "invariant"); while (true) { - BufferPtr buffer= mspace_acquire_live_with_retry(size, mspace, retry_count, thread); - if (buffer == nullptr && storage_instance.control().should_discard()) { + BufferPtr buffer = mspace_acquire_live_with_retry(size, mspace, retry_count, thread); + if (buffer != nullptr) { + return buffer; + } + if (storage_instance.control().should_discard()) { storage_instance.discard_oldest(thread); continue; } - return buffer; + return storage_instance.control().to_disk() ? JfrStorage::acquire_transient(size, thread) : nullptr; } } @@ -250,6 +255,10 @@ bool JfrStorage::flush_regular_buffer(BufferPtr buffer, Thread* thread) { assert(promotion_buffer->free_size() >= unflushed_size, "invariant"); buffer->move(promotion_buffer, unflushed_size); assert(buffer->empty(), "invariant"); + if (promotion_buffer->transient()) { + promotion_buffer->set_retired(); + register_full(promotion_buffer, thread); + } return true; } @@ -276,9 +285,17 @@ void JfrStorage::release_large(BufferPtr buffer, Thread* thread) { void JfrStorage::register_full(BufferPtr buffer, Thread* thread) { assert(buffer != nullptr, "invariant"); - assert(buffer->acquired_by(thread), "invariant"); assert(buffer->retired(), "invariant"); if (_full_list->add(buffer)) { + if (thread->is_Java_thread()) { + JavaThread* jt = JavaThread::cast(thread); + if (jt->thread_state() == _thread_in_native) { + // Transition java thread to vm so it can issue a notify. + ThreadInVMfromNative transition(jt); + _post_box.post(MSG_FULLBUFFER); + return; + } + } _post_box.post(MSG_FULLBUFFER); } } @@ -316,7 +333,8 @@ static void log_discard(size_t pre_full_count, size_t post_full_count, size_t am } void JfrStorage::discard_oldest(Thread* thread) { - if (JfrBuffer_lock->try_lock()) { + JfrMutexTryLock mutex(JfrBuffer_lock); + if (mutex.acquired()) { if (!control().should_discard()) { // another thread handled it return; @@ -326,7 +344,6 @@ void JfrStorage::discard_oldest(Thread* thread) { while (_full_list->is_nonempty()) { BufferPtr oldest = _full_list->remove(); assert(oldest != nullptr, "invariant"); - assert(oldest->identity() != nullptr, "invariant"); discarded_size += oldest->discard(); assert(oldest->unflushed_size() == 0, "invariant"); if (oldest->transient()) { @@ -335,10 +352,10 @@ void JfrStorage::discard_oldest(Thread* thread) { } oldest->reinitialize(); assert(!oldest->retired(), "invariant"); + assert(oldest->identity() != nullptr, "invariant"); oldest->release(); // publish break; } - JfrBuffer_lock->unlock(); log_discard(num_full_pre_discard, control().full_count(), discarded_size); } } diff --git a/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp b/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp index 4d0d5ac7230..39d7e330db2 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -64,6 +64,7 @@ class JfrStorage : public JfrCHeapObj { BufferPtr flush_regular(BufferPtr cur, const u1* cur_pos, size_t used, size_t req, bool native, Thread* thread); BufferPtr flush_large(BufferPtr cur, const u1* cur_pos, size_t used, size_t req, bool native, Thread* thread); BufferPtr provision_large(BufferPtr cur, const u1* cur_pos, size_t used, size_t req, bool native, Thread* thread); + BufferPtr acquire_promotion_buffer(size_t size, JfrStorageMspace* mspace, JfrStorage& storage_instance, size_t retry_count, Thread* thread); void release(BufferPtr buffer, Thread* thread); size_t clear(); diff --git a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp index cc099cdb92e..47cfca6b892 100644 --- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp +++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp @@ -24,6 +24,9 @@ #include "precompiled.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp" @@ -39,6 +42,45 @@ #include "runtime/javaThread.hpp" #include "runtime/safepoint.hpp" +static int generation_offset = invalid_offset; +static jobject string_pool = nullptr; + +static unsigned short generation = 0; + +static bool setup_string_pool_offsets(TRAPS) { + const char class_name[] = "jdk/jfr/internal/StringPool"; + Symbol* const k_sym = SymbolTable::new_symbol(class_name); + assert(k_sym != nullptr, "invariant"); + Klass* klass = SystemDictionary::resolve_or_fail(k_sym, true, CHECK_false); + assert(klass != nullptr, "invariant"); + klass->initialize(CHECK_false); + assert(!klass->should_be_initialized(), "invariant"); + assert(string_pool == nullptr, "invariant"); + jobject pool = JfrJavaSupport::global_jni_handle(klass->java_mirror(), THREAD); + if (pool == nullptr) { + return false; + } + const char generation_name[] = "generation"; + Symbol* const generation_sym = SymbolTable::new_symbol(generation_name); + assert(generation_sym != nullptr, "invariant"); + assert(invalid_offset == generation_offset, "invariant"); + if (!JfrJavaSupport::compute_field_offset(generation_offset, klass, generation_sym, vmSymbols::short_signature(), true)) { + JfrJavaSupport::destroy_global_jni_handle(pool); + return false; + } + assert(generation_offset != invalid_offset, "invariant"); + string_pool = pool; + return true; +} + +static bool initialize_java_string_pool() { + static bool initialized = false; + if (!initialized) { + initialized = setup_string_pool_offsets(JavaThread::current()); + } + return initialized; +} + typedef JfrStringPool::BufferPtr BufferPtr; static JfrSignal _new_string; @@ -75,6 +117,10 @@ static const size_t string_pool_cache_count = 2; static const size_t string_pool_buffer_size = 512 * K; bool JfrStringPool::initialize() { + if (!initialize_java_string_pool()) { + return false; + } + assert(_mspace == nullptr, "invariant"); _mspace = create_mspace(string_pool_buffer_size, 0, @@ -230,3 +276,12 @@ void JfrStringPool::register_full(BufferPtr buffer, Thread* thread) { assert(buffer->acquired_by(thread), "invariant"); assert(buffer->retired(), "invariant"); } + +void JfrStringPool::on_epoch_shift() { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + assert(!JfrTraceIdEpoch::is_synchronizing(), "invariant"); + assert(string_pool != nullptr, "invariant"); + oop mirror = JfrJavaSupport::resolve_non_null(string_pool); + assert(mirror != nullptr, "invariant"); + mirror->short_field_put(generation_offset, generation++); +} diff --git a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp index c4ff2af6c5c..a65e6cd4d93 100644 --- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp +++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp @@ -69,10 +69,12 @@ class JfrStringPool : public JfrCHeapObj { bool initialize(); static void destroy(); static bool is_modified(); + static void on_epoch_shift(); // mspace callback void register_full(BufferPtr buffer, Thread* thread); + friend class JfrCheckpointManager; friend class JfrRecorder; friend class JfrRecorderService; friend class JfrStringPoolFlush; diff --git a/src/hotspot/share/jfr/support/jfrIntrinsics.cpp b/src/hotspot/share/jfr/support/jfrIntrinsics.cpp index aaecd4500f2..59d4b37f028 100644 --- a/src/hotspot/share/jfr/support/jfrIntrinsics.cpp +++ b/src/hotspot/share/jfr/support/jfrIntrinsics.cpp @@ -71,6 +71,16 @@ void* JfrIntrinsicSupport::write_checkpoint(JavaThread* jt) { return JfrJavaEventWriter::event_writer(jt); } +void JfrIntrinsicSupport::return_lease(JavaThread* jt) { + DEBUG_ONLY(assert_precondition(jt);) + ThreadStateTransition::transition_from_java(jt, _thread_in_native); + assert(jt->jfr_thread_local()->has_java_event_writer(), "invariant"); + assert(jt->jfr_thread_local()->shelved_buffer() != nullptr, "invariant"); + JfrJavaEventWriter::flush(jt->jfr_thread_local()->java_event_writer(), 0, 0, jt); + assert(jt->jfr_thread_local()->shelved_buffer() == nullptr, "invariant"); + ThreadStateTransition::transition_from_native(jt, _thread_in_Java); +} + void JfrIntrinsicSupport::load_barrier(const Klass* klass) { assert(klass != nullptr, "sanity"); JfrTraceIdLoadBarrier::load_barrier(klass); diff --git a/src/hotspot/share/jfr/support/jfrIntrinsics.hpp b/src/hotspot/share/jfr/support/jfrIntrinsics.hpp index aa307062cfa..2e59749a61d 100644 --- a/src/hotspot/share/jfr/support/jfrIntrinsics.hpp +++ b/src/hotspot/share/jfr/support/jfrIntrinsics.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2012, 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 @@ -37,6 +37,7 @@ class JfrIntrinsicSupport : AllStatic { public: static void* write_checkpoint(JavaThread* jt); + static void return_lease(JavaThread* jt); static void load_barrier(const Klass* klass); static address epoch_address(); static address signal_address(); @@ -61,7 +62,7 @@ class JfrIntrinsicSupport : AllStatic { do_name( getClassId_name, "getClassId") \ do_intrinsic(_getEventWriter, jdk_jfr_internal_JVM, getEventWriter_name, getEventWriter_signature, F_SN) \ do_name( getEventWriter_name, "getEventWriter") \ - + do_intrinsic(_jvm_commit, jdk_jfr_internal_JVM, commit_name, long_long_signature, F_SN) #else // !INCLUDE_JFR #define JFR_TEMPLATES(template) diff --git a/src/hotspot/share/jfr/support/jfrThreadExtension.hpp b/src/hotspot/share/jfr/support/jfrThreadExtension.hpp index a98cda7d415..1396b6d51c1 100644 --- a/src/hotspot/share/jfr/support/jfrThreadExtension.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadExtension.hpp @@ -26,6 +26,7 @@ #define SHARE_JFR_SUPPORT_JFRTHREADEXTENSION_HPP #include "jfr/periodic/sampling/jfrThreadSampler.hpp" +#include "jfr/recorder/storage/jfrBuffer.hpp" #include "jfr/support/jfrThreadId.hpp" #define DEFINE_THREAD_LOCAL_FIELD_JFR mutable JfrThreadLocal _jfr_thread_local @@ -49,6 +50,18 @@ #define VTHREAD_EXCLUDED_OFFSET_JFR JfrThreadLocal::vthread_excluded_offset() +#define JAVA_BUFFER_OFFSET_JFR \ + JfrThreadLocal::java_buffer_offset() + THREAD_LOCAL_OFFSET_JFR + +#define NOTIFY_OFFSET_JFR \ + JfrThreadLocal::notified_offset() + THREAD_LOCAL_OFFSET_JFR + +#define JFR_BUFFER_POS_OFFSET \ + JfrBuffer::pos_offset() + +#define JFR_BUFFER_FLAGS_OFFSET \ + JfrBuffer::flags_offset() + #define THREAD_LOCAL_WRITER_OFFSET_JFR \ JfrThreadLocal::java_event_writer_offset() + THREAD_LOCAL_OFFSET_JFR diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 9b661efe18b..6e4e6644080 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -76,6 +76,7 @@ JfrThreadLocal::JfrThreadLocal() : _vthread_excluded(false), _jvm_thread_excluded(false), _vthread(false), + _notified(false), _dead(false) { Thread* thread = Thread::current_or_null(); _parent_trace_id = thread != nullptr ? jvm_thread_id(thread) : (traceid)0; @@ -247,6 +248,10 @@ ByteSize JfrThreadLocal::java_event_writer_offset() { return byte_offset_of(JfrThreadLocal, _java_event_writer); } +ByteSize JfrThreadLocal::java_buffer_offset() { + return byte_offset_of(JfrThreadLocal, _java_buffer); +} + ByteSize JfrThreadLocal::vthread_id_offset() { return byte_offset_of(JfrThreadLocal, _vthread_id); } @@ -263,6 +268,10 @@ ByteSize JfrThreadLocal::vthread_excluded_offset() { return byte_offset_of(JfrThreadLocal, _vthread_excluded); } +ByteSize JfrThreadLocal::notified_offset() { + return byte_offset_of(JfrThreadLocal, _notified); +} + void JfrThreadLocal::set(bool* exclusion_field, bool state) { assert(exclusion_field != nullptr, "invariant"); *exclusion_field = state; diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp index 6d71b94362e..da7d1f5e2ef 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp @@ -70,6 +70,7 @@ class JfrThreadLocal { bool _vthread_excluded; bool _jvm_thread_excluded; bool _vthread; + bool _notified; bool _dead; JfrBuffer* install_native_buffer() const; @@ -250,6 +251,18 @@ class JfrThreadLocal { _wallclock_time = wallclock_time; } + bool is_notified() { + return _notified; + } + + void notify() { + _notified = true; + } + + void clear_notification() { + _notified = false; + } + bool is_dead() const { return _dead; } @@ -273,10 +286,12 @@ class JfrThreadLocal { // Code generation static ByteSize java_event_writer_offset(); + static ByteSize java_buffer_offset(); static ByteSize vthread_id_offset(); static ByteSize vthread_offset(); static ByteSize vthread_epoch_offset(); static ByteSize vthread_excluded_offset(); + static ByteSize notified_offset(); friend class JfrJavaThread; friend class JfrCheckpointManager; diff --git a/src/hotspot/share/jfr/utilities/jfrTryLock.hpp b/src/hotspot/share/jfr/utilities/jfrTryLock.hpp index 10b3c93f5dc..5c76fa66321 100644 --- a/src/hotspot/share/jfr/utilities/jfrTryLock.hpp +++ b/src/hotspot/share/jfr/utilities/jfrTryLock.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -50,25 +50,23 @@ class JfrTryLock { } }; -class JfrMonitorTryLock : public StackObj { +class JfrMutexTryLock : public StackObj { private: - Monitor* _lock; + Mutex* _mutex; bool _acquired; public: - JfrMonitorTryLock(Monitor* lock) : _lock(lock), _acquired(lock->try_lock()) {} - - ~JfrMonitorTryLock() { + JfrMutexTryLock(Mutex* mutex) : _mutex(mutex), _acquired(mutex->try_lock()) {} + ~JfrMutexTryLock() { if (_acquired) { - assert(_lock->owned_by_self(), "invariant"); - _lock->unlock(); + assert(_mutex->owned_by_self(), "invariant"); + _mutex->unlock(); } } bool acquired() const { return _acquired; } - }; #endif // SHARE_JFR_UTILITIES_JFRTRYLOCK_HPP diff --git a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp index 2c56fad846a..5ff319782b2 100644 --- a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp +++ b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp @@ -37,6 +37,7 @@ #include "oops/oop.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/safepoint.hpp" #include "runtime/threads.hpp" @@ -45,7 +46,6 @@ static int start_pos_offset = invalid_offset; static int start_pos_address_offset = invalid_offset; static int current_pos_offset = invalid_offset; static int max_pos_offset = invalid_offset; -static int notified_offset = invalid_offset; static int excluded_offset = invalid_offset; static int thread_id_offset = invalid_offset; static int valid_offset = invalid_offset; @@ -64,13 +64,6 @@ static bool setup_event_writer_offsets(TRAPS) { JfrJavaSupport::compute_field_offset(start_pos_offset, klass, start_pos_sym, vmSymbols::long_signature()); assert(start_pos_offset != invalid_offset, "invariant"); - const char start_pos_address_name[] = "startPositionAddress"; - Symbol* const start_pos_address_sym = SymbolTable::new_symbol(start_pos_address_name); - assert(start_pos_address_sym != nullptr, "invariant"); - assert(invalid_offset == start_pos_address_offset, "invariant"); - JfrJavaSupport::compute_field_offset(start_pos_address_offset, klass, start_pos_address_sym, vmSymbols::long_signature()); - assert(start_pos_address_offset != invalid_offset, "invariant"); - const char event_pos_name[] = "currentPosition"; Symbol* const event_pos_sym = SymbolTable::new_symbol(event_pos_name); assert(event_pos_sym != nullptr, "invariant"); @@ -85,13 +78,6 @@ static bool setup_event_writer_offsets(TRAPS) { JfrJavaSupport::compute_field_offset(max_pos_offset, klass, max_pos_sym, vmSymbols::long_signature()); assert(max_pos_offset != invalid_offset, "invariant"); - const char notified_name[] = "notified"; - Symbol* const notified_sym = SymbolTable::new_symbol(notified_name); - assert (notified_sym != nullptr, "invariant"); - assert(invalid_offset == notified_offset, "invariant"); - JfrJavaSupport::compute_field_offset(notified_offset, klass, notified_sym, vmSymbols::bool_signature()); - assert(notified_offset != invalid_offset, "invariant"); - const char excluded_name[] = "excluded"; Symbol* const excluded_sym = SymbolTable::new_symbol(excluded_name); assert(excluded_sym != nullptr, "invariant"); @@ -123,11 +109,9 @@ bool JfrJavaEventWriter::initialize() { return initialized; } -jboolean JfrJavaEventWriter::flush(jobject writer, jint used, jint requested, JavaThread* jt) { - DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); +void JfrJavaEventWriter::flush(jobject writer, jint used, jint requested, JavaThread* jt) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); assert(writer != nullptr, "invariant"); - oop const w = JNIHandles::resolve_non_null(writer); - assert(w != nullptr, "invariant"); JfrBuffer* const current = jt->jfr_thread_local()->java_buffer(); assert(current != nullptr, "invariant"); JfrBuffer* const buffer = JfrStorage::flush(current, used, requested, false, jt); @@ -138,23 +122,48 @@ jboolean JfrJavaEventWriter::flush(jobject writer, jint used, jint requested, Ja const bool is_valid = buffer->free_size() >= (size_t)(used + requested); u1* const new_current_position = is_valid ? buffer->pos() + used : buffer->pos(); assert(start_pos_offset != invalid_offset, "invariant"); + // can safepoint here + ThreadInVMfromNative transition(jt); + oop const w = JNIHandles::resolve_non_null(writer); + assert(w != nullptr, "invariant"); w->long_field_put(start_pos_offset, (jlong)buffer->pos()); w->long_field_put(current_pos_offset, (jlong)new_current_position); // only update java writer if underlying memory changed if (buffer != current) { - w->long_field_put(start_pos_address_offset, (jlong)buffer->pos_address()); w->long_field_put(max_pos_offset, (jlong)buffer->end()); } if (!is_valid) { // mark writer as invalid for this write attempt w->release_bool_field_put(valid_offset, JNI_FALSE); - return JNI_FALSE; } - // An exclusive use of a leased buffer is treated equivalent to - // holding a system resource. As such, it should be released as soon as possible. - // Returning true here signals that the thread will need to call flush again - // on EventWriter.endEvent() and that flush will return the lease. - return buffer->lease() ? JNI_TRUE : JNI_FALSE; +} + +jlong JfrJavaEventWriter::commit(jlong next_position) { + assert(next_position != 0, "invariant"); + JavaThread* const jt = JavaThread::current(); + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + JfrThreadLocal* const tl = jt->jfr_thread_local(); + assert(tl != nullptr, "invariant"); + assert(tl->has_java_event_writer(), "invariant"); + assert(tl->has_java_buffer(), "invariant"); + JfrBuffer* const current = tl->java_buffer(); + assert(current != nullptr, "invariant"); + u1* const next = reinterpret_cast(next_position); + assert(next >= current->start(), "invariant"); + assert(next <= current->end(), "invariant"); + if (tl->is_notified()) { + tl->clear_notification(); + return reinterpret_cast(current->pos()); + } + // set_pos() has release semantics + current->set_pos(next); + if (!current->lease()) { + return next_position; + } + assert(current->lease(), "invariant"); + flush(tl->java_event_writer(), 0, 0, jt); + return 0; // signals that the buffer lease was returned. } class JfrJavaEventWriterNotificationClosure : public ThreadClosure { @@ -198,9 +207,14 @@ void JfrJavaEventWriter::notify(JavaThread* jt) { assert(jt != nullptr, "invariant"); assert(SafepointSynchronize::is_at_safepoint(), "invariant"); if (jt->jfr_thread_local()->has_java_event_writer()) { - oop buffer_writer = JNIHandles::resolve_non_null(jt->jfr_thread_local()->java_event_writer()); - assert(buffer_writer != nullptr, "invariant"); - buffer_writer->release_bool_field_put(notified_offset, JNI_TRUE); + JfrThreadLocal* const tl = jt->jfr_thread_local(); + assert(tl != nullptr, "invariant"); + oop event_writer = JNIHandles::resolve_non_null(tl->java_event_writer()); + assert(event_writer != nullptr, "invariant"); + const jlong start_pos = event_writer->long_field(start_pos_offset); + if (event_writer->long_field(current_pos_offset) > start_pos) { + tl->notify(); + } } } @@ -210,14 +224,13 @@ static jobject create_new_event_writer(JfrBuffer* buffer, JfrThreadLocal* tl, TR HandleMark hm(THREAD); static const char klass[] = "jdk/jfr/internal/event/EventWriter"; static const char method[] = ""; - static const char signature[] = "(JJJJZZ)V"; + static const char signature[] = "(JJJZZ)V"; JavaValue result(T_OBJECT); JfrJavaArguments args(&result, klass, method, signature, CHECK_NULL); // parameters args.push_long((jlong)buffer->pos()); args.push_long((jlong)buffer->end()); - args.push_long((jlong)buffer->pos_address()); args.push_long((jlong)JfrThreadLocal::thread_id(THREAD)); args.push_int((jint)JNI_TRUE); // valid args.push_int(tl->is_excluded() ? (jint)JNI_TRUE : (jint)JNI_FALSE); // excluded @@ -228,7 +241,6 @@ static jobject create_new_event_writer(JfrBuffer* buffer, JfrThreadLocal* tl, TR jobject JfrJavaEventWriter::event_writer(JavaThread* jt) { DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); JfrThreadLocal* const tl = jt->jfr_thread_local(); - assert(tl->shelved_buffer() == nullptr, "invariant"); jobject h_writer = tl->java_event_writer(); if (h_writer != nullptr) { oop writer = JNIHandles::resolve_non_null(h_writer); diff --git a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp index af99690ba47..b93d6ad8cac 100644 --- a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp +++ b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -31,6 +31,7 @@ #include "utilities/exceptions.hpp" class JavaThread; +class JfrBuffer; class Thread; class JfrJavaEventWriter : AllStatic { @@ -48,7 +49,8 @@ class JfrJavaEventWriter : AllStatic { static void include(traceid tid, const JavaThread* jt); static jobject event_writer(JavaThread* t); static jobject new_event_writer(TRAPS); - static jboolean flush(jobject writer, jint used, jint requested, JavaThread* jt); + static void flush(jobject writer, jint used, jint requested, JavaThread* jt); + static jlong commit(jlong next_position); }; #endif // SHARE_JFR_WRITERS_JFRJAVAEVENTWRITER_HPP diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 10524082f19..b71c213f478 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -591,8 +591,8 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz #if defined(AARCH64) || defined(PPC64) const size_t alignment = Metaspace::reserve_alignment(); - // AArch64: Try to align metaspace so that we can decode a compressed - // klass with a single MOVK instruction. We can do this iff the + // AArch64: Try to align metaspace class space so that we can decode a + // compressed klass with a single MOVK instruction. We can do this iff the // compressed class base is a multiple of 4G. // Additionally, above 32G, ensure the lower LogKlassAlignmentInBytes bits // of the upper 32-bits of the address are zero so we can handle a shift @@ -615,19 +615,39 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz { nullptr, nullptr, 0 } }; + // Calculate a list of all possible values for the starting address for the + // compressed class space. + ResourceMark rm; + GrowableArray
list(36); for (int i = 0; search_ranges[i].from != nullptr; i ++) { address a = search_ranges[i].from; assert(CompressedKlassPointers::is_valid_base(a), "Sanity"); while (a < search_ranges[i].to) { - ReservedSpace rs(size, Metaspace::reserve_alignment(), - os::vm_page_size(), (char*)a); - if (rs.is_reserved()) { - assert(a == (address)rs.base(), "Sanity"); - return rs; - } + list.append(a); a += search_ranges[i].increment; } } + + int len = list.length(); + int r = 0; + if (!DumpSharedSpaces) { + // Starting from a random position in the list. If the address cannot be reserved + // (the OS already assigned it for something else), go to the next position, wrapping + // around if necessary, until we exhaust all the items. + os::init_random((int)os::javaTimeNanos()); + r = os::random(); + log_info(metaspace)("Randomizing compressed class space: start from %d out of %d locations", + r % len, len); + } + for (int i = 0; i < len; i++) { + address a = list.at((i + r) % len); + ReservedSpace rs(size, Metaspace::reserve_alignment(), + os::vm_page_size(), (char*)a); + if (rs.is_reserved()) { + assert(a == (address)rs.base(), "Sanity"); + return rs; + } + } #endif // defined(AARCH64) || defined(PPC64) #ifdef AARCH64 diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 30d510f4fc5..4b911620cf9 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -679,6 +679,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method) { #ifdef JFR_HAVE_INTRINSICS case vmIntrinsics::_counterTime: case vmIntrinsics::_getEventWriter: + case vmIntrinsics::_jvm_commit: #endif case vmIntrinsics::_currentTimeMillis: case vmIntrinsics::_nanoTime: diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 6f4099d7feb..50f9f4b85f3 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -706,6 +706,10 @@ void CallGenerator::do_late_inline_helper() { result = (result_size == 1) ? kit.pop() : kit.pop_pair(); } + if (call->is_CallStaticJava() && call->as_CallStaticJava()->is_boxing_method()) { + result = kit.must_be_not_null(result, false); + } + if (inline_cg()->is_inline()) { C->set_has_loops(C->has_loops() || inline_cg()->method()->has_loops()); C->env()->notice_inlined_method(inline_cg()->method()); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 70ad7d24e39..2b78d7b3b24 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -824,7 +824,9 @@ void Parse::catch_call_exceptions(ciExceptionHandlerStream& handlers) { if (!default_handler) { bcis->append(-1); - extypes->append(TypeOopPtr::make_from_klass(env()->Throwable_klass())->is_instptr()); + const Type* extype = TypeOopPtr::make_from_klass(env()->Throwable_klass())->is_instptr(); + extype = extype->join(TypeInstPtr::NOTNULL); + extypes->append(extype); } int len = bcis->length(); diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 65a58341f3f..ef0c7208356 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -91,9 +91,14 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { // See that the merge point contains some constants Node *con1=nullptr; uint i4; - for( i4 = 1; i4 < phi->req(); i4++ ) { + RegionNode* phi_region = phi->region(); + for (i4 = 1; i4 < phi->req(); i4++ ) { con1 = phi->in(i4); - if( !con1 ) return nullptr; // Do not optimize partially collapsed merges + // Do not optimize partially collapsed merges + if (con1 == nullptr || phi_region->in(i4) == nullptr || igvn->type(phi_region->in(i4)) == Type::TOP) { + igvn->_worklist.push(iff); + return nullptr; + } if( con1->is_Con() ) break; // Found a constant // Also allow null-vs-not-null checks const TypePtr *tp = igvn->type(con1)->isa_ptr(); @@ -115,7 +120,7 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { // No intervening control, like a simple Call Node* r = iff->in(0); - if (!r->is_Region() || r->is_Loop() || phi->region() != r || r->as_Region()->is_copy()) { + if (!r->is_Region() || r->is_Loop() || phi_region != r || r->as_Region()->is_copy()) { return nullptr; } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 4e93083ee42..c995a1c6502 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -494,6 +494,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { #ifdef JFR_HAVE_INTRINSICS case vmIntrinsics::_counterTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, JfrTime::time_function()), "counterTime"); case vmIntrinsics::_getEventWriter: return inline_native_getEventWriter(); + case vmIntrinsics::_jvm_commit: return inline_native_jvm_commit(); #endif case vmIntrinsics::_currentTimeMillis: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, os::javaTimeMillis), "currentTimeMillis"); case vmIntrinsics::_nanoTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, os::javaTimeNanos), "nanoTime"); @@ -3003,6 +3004,136 @@ bool LibraryCallKit::inline_native_classID() { return true; } +//------------------------inline_native_jvm_commit------------------ +bool LibraryCallKit::inline_native_jvm_commit() { + enum { _true_path = 1, _false_path = 2, PATH_LIMIT }; + + // Save input memory and i_o state. + Node* input_memory_state = reset_memory(); + set_all_memory(input_memory_state); + Node* input_io_state = i_o(); + + // TLS. + Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); + // Jfr java buffer. + Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, _gvn.transform(MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR))))); + Node* java_buffer = _gvn.transform(new LoadPNode(control(), input_memory_state, java_buffer_offset, TypePtr::BOTTOM, TypeRawPtr::NOTNULL, MemNode::unordered)); + Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET))))); + + // Load the current value of the notified field in the JfrThreadLocal. + Node* notified_offset = basic_plus_adr(top(), tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); + Node* notified = make_load(control(), notified_offset, TypeInt::BOOL, T_BOOLEAN, MemNode::unordered); + + // Test for notification. + Node* notified_cmp = _gvn.transform(new CmpINode(notified, _gvn.intcon(1))); + Node* test_notified = _gvn.transform(new BoolNode(notified_cmp, BoolTest::eq)); + IfNode* iff_notified = create_and_map_if(control(), test_notified, PROB_MIN, COUNT_UNKNOWN); + + // True branch, is notified. + Node* is_notified = _gvn.transform(new IfTrueNode(iff_notified)); + set_control(is_notified); + + // Reset notified state. + Node* notified_reset_memory = store_to_memory(control(), notified_offset, _gvn.intcon(0), T_BOOLEAN, Compile::AliasIdxRaw, MemNode::unordered); + + // Iff notified, the return address of the commit method is the current position of the backing java buffer. This is used to reset the event writer. + Node* current_pos_X = _gvn.transform(new LoadXNode(control(), input_memory_state, java_buffer_pos_offset, TypeRawPtr::NOTNULL, TypeX_X, MemNode::unordered)); + // Convert the machine-word to a long. + Node* current_pos = _gvn.transform(ConvX2L(current_pos_X)); + + // False branch, not notified. + Node* not_notified = _gvn.transform(new IfFalseNode(iff_notified)); + set_control(not_notified); + set_all_memory(input_memory_state); + + // Arg is the next position as a long. + Node* arg = argument(0); + // Convert long to machine-word. + Node* next_pos_X = _gvn.transform(ConvL2X(arg)); + + // Store the next_position to the underlying jfr java buffer. + Node* commit_memory; +#ifdef _LP64 + commit_memory = store_to_memory(control(), java_buffer_pos_offset, next_pos_X, T_LONG, Compile::AliasIdxRaw, MemNode::release); +#else + commit_memory = store_to_memory(control(), java_buffer_pos_offset, next_pos_X, T_INT, Compile::AliasIdxRaw, MemNode::release); +#endif + + // Now load the flags from off the java buffer and decide if the buffer is a lease. If so, it needs to be returned post-commit. + Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET))))); + Node* flags = make_load(control(), java_buffer_flags_offset, TypeInt::UBYTE, T_BYTE, MemNode::unordered); + Node* lease_constant = _gvn.transform(_gvn.intcon(4)); + + // And flags with lease constant. + Node* lease = _gvn.transform(new AndINode(flags, lease_constant)); + + // Branch on lease to conditionalize returning the leased java buffer. + Node* lease_cmp = _gvn.transform(new CmpINode(lease, lease_constant)); + Node* test_lease = _gvn.transform(new BoolNode(lease_cmp, BoolTest::eq)); + IfNode* iff_lease = create_and_map_if(control(), test_lease, PROB_MIN, COUNT_UNKNOWN); + + // False branch, not a lease. + Node* not_lease = _gvn.transform(new IfFalseNode(iff_lease)); + + // True branch, is lease. + Node* is_lease = _gvn.transform(new IfTrueNode(iff_lease)); + set_control(is_lease); + + // Make a runtime call, which can safepoint, to return the leased buffer. This updates both the JfrThreadLocal and the Java event writer oop. + Node* call_return_lease = make_runtime_call(RC_NO_LEAF, + OptoRuntime::void_void_Type(), + StubRoutines::jfr_return_lease(), + "return_lease", TypePtr::BOTTOM); + Node* call_return_lease_control = _gvn.transform(new ProjNode(call_return_lease, TypeFunc::Control)); + + RegionNode* lease_compare_rgn = new RegionNode(PATH_LIMIT); + record_for_igvn(lease_compare_rgn); + PhiNode* lease_compare_mem = new PhiNode(lease_compare_rgn, Type::MEMORY, TypePtr::BOTTOM); + record_for_igvn(lease_compare_mem); + PhiNode* lease_compare_io = new PhiNode(lease_compare_rgn, Type::ABIO); + record_for_igvn(lease_compare_io); + PhiNode* lease_result_value = new PhiNode(lease_compare_rgn, TypeLong::LONG); + record_for_igvn(lease_result_value); + + // Update control and phi nodes. + lease_compare_rgn->init_req(_true_path, call_return_lease_control); + lease_compare_rgn->init_req(_false_path, not_lease); + + lease_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + lease_compare_mem->init_req(_false_path, commit_memory); + + lease_compare_io->init_req(_true_path, i_o()); + lease_compare_io->init_req(_false_path, input_io_state); + + lease_result_value->init_req(_true_path, null()); // if the lease was returned, return 0. + lease_result_value->init_req(_false_path, arg); // if not lease, return new updated position. + + RegionNode* result_rgn = new RegionNode(PATH_LIMIT); + PhiNode* result_mem = new PhiNode(result_rgn, Type::MEMORY, TypePtr::BOTTOM); + PhiNode* result_io = new PhiNode(result_rgn, Type::ABIO); + PhiNode* result_value = new PhiNode(result_rgn, TypeLong::LONG); + + // Update control and phi nodes. + result_rgn->init_req(_true_path, is_notified); + result_rgn->init_req(_false_path, _gvn.transform(lease_compare_rgn)); + + result_mem->init_req(_true_path, notified_reset_memory); + result_mem->init_req(_false_path, _gvn.transform(lease_compare_mem)); + + result_io->init_req(_true_path, input_io_state); + result_io->init_req(_false_path, _gvn.transform(lease_compare_io)); + + result_value->init_req(_true_path, current_pos); + result_value->init_req(_false_path, _gvn.transform(lease_result_value)); + + // Set output state. + set_control(_gvn.transform(result_rgn)); + set_all_memory(_gvn.transform(result_mem)); + set_i_o(_gvn.transform(result_io)); + set_result(result_rgn, result_value); + return true; +} + /* * The intrinsic is a model of this pseudo-code: * @@ -4016,9 +4147,9 @@ bool LibraryCallKit::inline_unsafe_newArray(bool uninitialized) { CallJavaNode* slow_call = nullptr; if (uninitialized) { // Generate optimized virtual call (holder class 'Unsafe' is final) - slow_call = generate_method_call(vmIntrinsics::_allocateUninitializedArray, false, false); + slow_call = generate_method_call(vmIntrinsics::_allocateUninitializedArray, false, false, true); } else { - slow_call = generate_method_call_static(vmIntrinsics::_newArray); + slow_call = generate_method_call_static(vmIntrinsics::_newArray, true); } Node* slow_result = set_results_for_java_call(slow_call); // this->control() comes from set_results_for_java_call @@ -4263,7 +4394,7 @@ Node* LibraryCallKit::generate_virtual_guard(Node* obj_klass, // not another intrinsic. (E.g., don't use this for making an // arraycopy call inside of the copyOf intrinsic.) CallJavaNode* -LibraryCallKit::generate_method_call(vmIntrinsics::ID method_id, bool is_virtual, bool is_static) { +LibraryCallKit::generate_method_call(vmIntrinsicID method_id, bool is_virtual, bool is_static, bool res_not_null) { // When compiling the intrinsic method itself, do not use this technique. guarantee(callee() != C->method(), "cannot make slow-call to self"); @@ -4272,6 +4403,14 @@ LibraryCallKit::generate_method_call(vmIntrinsics::ID method_id, bool is_virtual guarantee(method_id == method->intrinsic_id(), "must match"); const TypeFunc* tf = TypeFunc::make(method); + if (res_not_null) { + assert(tf->return_type() == T_OBJECT, ""); + const TypeTuple* range = tf->range(); + const Type** fields = TypeTuple::fields(range->cnt()); + fields[TypeFunc::Parms] = range->field_at(TypeFunc::Parms)->filter_speculative(TypePtr::NOTNULL); + const TypeTuple* new_range = TypeTuple::make(range->cnt(), fields); + tf = TypeFunc::make(tf->domain(), new_range); + } CallJavaNode* slow_call; if (is_static) { assert(!is_virtual, ""); @@ -4421,7 +4560,7 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { // No need for PreserveJVMState, because we're using up the present state. set_all_memory(init_mem); vmIntrinsics::ID hashCode_id = is_static ? vmIntrinsics::_identityHashCode : vmIntrinsics::_hashCode; - CallJavaNode* slow_call = generate_method_call(hashCode_id, is_virtual, is_static); + CallJavaNode* slow_call = generate_method_call(hashCode_id, is_virtual, is_static, false); Node* slow_result = set_results_for_java_call(slow_call); // this->control() comes from set_results_for_java_call result_reg->init_req(_slow_path, control()); @@ -4947,7 +5086,7 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) { set_control(_gvn.transform(slow_region)); if (!stopped()) { PreserveJVMState pjvms(this); - CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual); + CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual, false, true); // We need to deoptimize on exception (see comment above) Node* slow_result = set_results_for_java_call(slow_call, false, /* deoptimize */ true); // this->control() comes from set_results_for_java_call diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 50b6a47971f..d4d6e50b2a5 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -176,13 +176,9 @@ class LibraryCallKit : public GraphKit { Node* generate_array_guard_common(Node* kls, RegionNode* region, bool obj_array, bool not_array); Node* generate_virtual_guard(Node* obj_klass, RegionNode* slow_region); - CallJavaNode* generate_method_call(vmIntrinsics::ID method_id, - bool is_virtual = false, bool is_static = false); - CallJavaNode* generate_method_call_static(vmIntrinsics::ID method_id) { - return generate_method_call(method_id, false, true); - } - CallJavaNode* generate_method_call_virtual(vmIntrinsics::ID method_id) { - return generate_method_call(method_id, true, false); + CallJavaNode* generate_method_call(vmIntrinsicID method_id, bool is_virtual, bool is_static, bool res_not_null); + CallJavaNode* generate_method_call_static(vmIntrinsicID method_id, bool res_not_null) { + return generate_method_call(method_id, false, true, res_not_null); } Node* load_field_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, DecoratorSet decorators = IN_HEAP, bool is_static = false, ciInstanceKlass* fromKls = nullptr); Node* field_address_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, bool is_exact = true, bool is_static = false, ciInstanceKlass* fromKls = nullptr); @@ -253,6 +249,7 @@ class LibraryCallKit : public GraphKit { #ifdef JFR_HAVE_INTRINSICS bool inline_native_classID(); bool inline_native_getEventWriter(); + bool inline_native_jvm_commit(); void extend_setCurrentThread(Node* jt, Node* thread); #endif bool inline_native_Class_query(vmIntrinsics::ID id); diff --git a/src/hotspot/share/opto/subtypenode.cpp b/src/hotspot/share/opto/subtypenode.cpp index bb508f02ea8..3c50eac2e25 100644 --- a/src/hotspot/share/opto/subtypenode.cpp +++ b/src/hotspot/share/opto/subtypenode.cpp @@ -34,6 +34,7 @@ const Type* SubTypeCheckNode::sub(const Type* sub_t, const Type* super_t) const { const TypeKlassPtr* superk = super_t->isa_klassptr(); + assert(sub_t != Type::TOP && !TypePtr::NULL_PTR->higher_equal(sub_t), "should be not null"); const TypeKlassPtr* subk = sub_t->isa_klassptr() ? sub_t->is_klassptr() : sub_t->is_oopptr()->as_klass_type(); // Oop can't be a subtype of abstract type that has no subclass. diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index ce9d7fab5ed..9e12e9d51e0 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -1602,13 +1602,8 @@ JvmtiEnvBase::is_in_thread_list(jint count, const jthread* list, oop jt_oop) { class VM_SetNotifyJvmtiEventsMode : public VM_Operation { private: - static bool _whitebox_used; bool _enable; - // This function is needed only for testing purposes to support multiple - // enable&disable notifyJvmti events. Otherwise, there can be only one call - // to enable_virtual_threads_notify_jvmti() for late binding agents. There - // have to be no JvmtiThreadState's and need to correct them in such a case. static void correct_jvmti_thread_state(JavaThread* jt) { oop ct_oop = jt->threadObj(); oop vt_oop = jt->vthread(); @@ -1618,8 +1613,7 @@ class VM_SetNotifyJvmtiEventsMode : public VM_Operation { bool virt = vt_oop != nullptr && java_lang_VirtualThread::is_instance(vt_oop); // Correct jt->jvmti_thread_state() and jt->jvmti_vthread(). - // It was not maintained while notifyJvmti was disabled but there can be - // a leftover from previous cycle when notification were enabled. + // It was not maintained while notifyJvmti was disabled. if (virt) { jt->set_jvmti_thread_state(nullptr); // reset jt->jvmti_thread_state() jt->set_jvmti_vthread(vt_oop); // restore jt->jvmti_vthread() @@ -1640,9 +1634,7 @@ class VM_SetNotifyJvmtiEventsMode : public VM_Operation { count++; continue; // no need in JvmtiThreadState correction below if in transition } - if (_whitebox_used) { - correct_jvmti_thread_state(jt); // needed in testing environment only - } + correct_jvmti_thread_state(jt); } return count; } @@ -1651,9 +1643,6 @@ class VM_SetNotifyJvmtiEventsMode : public VM_Operation { VMOp_Type type() const { return VMOp_SetNotifyJvmtiEventsMode; } bool allow_nested_vm_operations() const { return false; } VM_SetNotifyJvmtiEventsMode(bool enable) : _enable(enable) { - if (!enable) { - _whitebox_used = true; // disabling is available via WhiteBox only - } } void doit() { @@ -1664,8 +1653,6 @@ class VM_SetNotifyJvmtiEventsMode : public VM_Operation { } }; -bool VM_SetNotifyJvmtiEventsMode::_whitebox_used = false; - // This function is to support agents loaded into running VM. // Must be called in thread-in-native mode. bool diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 87a77eb2be3..7342e432758 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -497,9 +497,7 @@ void before_exit(JavaThread* thread, bool halt) { // Stop the WatcherThread. We do this before disenrolling various // PeriodicTasks to reduce the likelihood of races. - if (PeriodicTask::num_tasks() > 0) { - WatcherThread::stop(); - } + WatcherThread::stop(); // shut down the StatSampler task StatSampler::disengage(); diff --git a/src/hotspot/share/runtime/nonJavaThread.cpp b/src/hotspot/share/runtime/nonJavaThread.cpp index 3fbfa080e1f..f537afcaa06 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -156,7 +156,7 @@ void NamedThread::print_on(outputStream* st) const { // timer interrupts exists on the platform. WatcherThread* WatcherThread::_watcher_thread = nullptr; -bool WatcherThread::_startable = false; +bool WatcherThread::_run_all_tasks = false; volatile bool WatcherThread::_should_terminate = false; WatcherThread::WatcherThread() : NonJavaThread() { @@ -185,6 +185,11 @@ int WatcherThread::sleep() const { return 0; // we did not sleep. } + if (!_run_all_tasks) { + ml.wait(100); + return 0; + } + // remaining will be zero if there are no tasks, // causing the WatcherThread to sleep until a task is // enrolled @@ -280,7 +285,10 @@ void WatcherThread::run() { break; } - PeriodicTask::real_time_tick(time_waited); + // Don't process enrolled tasks until VM is fully initialized. + if (_run_all_tasks) { + PeriodicTask::real_time_tick(time_waited); + } } // Signal that it is terminated @@ -293,18 +301,16 @@ void WatcherThread::run() { } void WatcherThread::start() { - assert(PeriodicTask_lock->owned_by_self(), "PeriodicTask_lock required"); - - if (watcher_thread() == nullptr && _startable) { - _should_terminate = false; - // Create the single instance of WatcherThread - new WatcherThread(); - } + MonitorLocker ml(PeriodicTask_lock); + _should_terminate = false; + // Create the single instance of WatcherThread + new WatcherThread(); } -void WatcherThread::make_startable() { - assert(PeriodicTask_lock->owned_by_self(), "PeriodicTask_lock required"); - _startable = true; +void WatcherThread::run_all_tasks() { + MonitorLocker ml(PeriodicTask_lock); + _run_all_tasks = true; + ml.notify(); } void WatcherThread::stop() { diff --git a/src/hotspot/share/runtime/nonJavaThread.hpp b/src/hotspot/share/runtime/nonJavaThread.hpp index 7807af6e228..6d9095924d9 100644 --- a/src/hotspot/share/runtime/nonJavaThread.hpp +++ b/src/hotspot/share/runtime/nonJavaThread.hpp @@ -110,7 +110,7 @@ class WatcherThread: public NonJavaThread { private: static WatcherThread* _watcher_thread; - static bool _startable; + static bool _run_all_tasks; // volatile due to at least one lock-free read volatile static bool _should_terminate; public: @@ -137,9 +137,9 @@ class WatcherThread: public NonJavaThread { // Create and start the single instance of WatcherThread, or stop it on shutdown static void start(); static void stop(); - // Only allow start once the VM is sufficiently initialized - // Otherwise the first task to enroll will trigger the start - static void make_startable(); + // Allow executing registered tasks once the VM is sufficiently + // initialized. Meanwhile only error reporting will be checked. + static void run_all_tasks(); private: int sleep() const; }; diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 0739de77b8a..12a0e953771 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -386,7 +386,7 @@ bool ObjectMonitor::enter(JavaThread* current) { return false; } - JFR_ONLY(JfrConditionalFlushWithStacktrace flush(current);) + JFR_ONLY(JfrConditionalFlush flush(current);) EventJavaMonitorEnter event; if (event.is_started()) { event.set_monitorClass(object()->klass()); diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 7a6974088ba..36dd503cd70 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -181,6 +181,8 @@ address StubRoutines::_cont_returnBarrierExc = nullptr; JFR_ONLY(RuntimeStub* StubRoutines::_jfr_write_checkpoint_stub = nullptr;) JFR_ONLY(address StubRoutines::_jfr_write_checkpoint = nullptr;) +JFR_ONLY(RuntimeStub* StubRoutines::_jfr_return_lease_stub = nullptr;) +JFR_ONLY(address StubRoutines::_jfr_return_lease = nullptr;) // Initialization // diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 5ce9176f08a..b414b0b905b 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -259,6 +259,8 @@ class StubRoutines: AllStatic { JFR_ONLY(static RuntimeStub* _jfr_write_checkpoint_stub;) JFR_ONLY(static address _jfr_write_checkpoint;) + JFR_ONLY(static RuntimeStub* _jfr_return_lease_stub;) + JFR_ONLY(static address _jfr_return_lease;) // Vector Math Routines static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; @@ -455,6 +457,7 @@ class StubRoutines: AllStatic { static address cont_returnBarrierExc(){return _cont_returnBarrierExc; } JFR_ONLY(static address jfr_write_checkpoint() { return _jfr_write_checkpoint; }) + JFR_ONLY(static address jfr_return_lease() { return _jfr_return_lease; }) static address select_fill_function(BasicType t, bool aligned, const char* &name); diff --git a/src/hotspot/share/runtime/task.cpp b/src/hotspot/share/runtime/task.cpp index d38c1d145d0..9eccd368d25 100644 --- a/src/hotspot/share/runtime/task.cpp +++ b/src/hotspot/share/runtime/task.cpp @@ -29,6 +29,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/nonJavaThread.hpp" #include "runtime/task.hpp" +#include "runtime/threads.hpp" #include "runtime/timer.hpp" int PeriodicTask::_num_tasks = 0; @@ -95,10 +96,9 @@ void PeriodicTask::enroll() { } WatcherThread* thread = WatcherThread::watcher_thread(); + assert(thread != nullptr || !Threads::is_vm_complete(), "vm created but no WatcherThread"); if (thread != nullptr) { thread->unpark(); - } else { - WatcherThread::start(); } } diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 49e3d1c7a33..dedab658bc0 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -559,6 +559,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { return status; } + // Create WatcherThread as soon as we can since we need it in case + // of hangs during error reporting. + WatcherThread::start(); + // Add main_thread to threads list to finish barrier setup with // on_thread_attach. Should be before starting to build Java objects in // init_globals2, which invokes barriers. @@ -808,19 +812,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { CLEAR_PENDING_EXCEPTION; } - { - MutexLocker ml(PeriodicTask_lock); - // Make sure the WatcherThread can be started by WatcherThread::start() - // or by dynamic enrollment. - WatcherThread::make_startable(); - // Start up the WatcherThread if there are any periodic tasks - // NOTE: All PeriodicTasks should be registered by now. If they - // aren't, late joiners might appear to start slowly (we might - // take a while to process their first tick). - if (PeriodicTask::num_tasks() > 0) { - WatcherThread::start(); - } - } + // Let WatcherThread run all registered periodic tasks now. + // NOTE: All PeriodicTasks should be registered by now. If they + // aren't, late joiners might appear to start slowly (we might + // take a while to process their first tick). + WatcherThread::run_all_tasks(); create_vm_timer.end(); #ifdef ASSERT diff --git a/src/java.base/share/classes/java/lang/ScopedValue.java b/src/java.base/share/classes/java/lang/ScopedValue.java index 9aab6669a7c..7fea72469e2 100644 --- a/src/java.base/share/classes/java/lang/ScopedValue.java +++ b/src/java.base/share/classes/java/lang/ScopedValue.java @@ -394,19 +394,20 @@ public T get(ScopedValue key) { * to its value in the current thread. * When the operation completes (normally or with an exception), each scoped value * in the mapping will revert to being unbound, or revert to its previous value - * when previously bound, in the current thread. + * when previously bound, in the current thread. If {@code op} completes with an + * exception then it propagated by this method. * - *

Scoped values are intended to be used in a structured manner. - * If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain - * StructuredTaskScope#close() close} it, then exiting {@code op} causes the - * underlying construct of each {@code StructuredTaskScope} created in the - * dynamic scope to be closed. This may require blocking until all child threads - * have completed their sub-tasks. The closing is done in the reverse order that - * they were created. Once closed, {@link StructureViolationException} is thrown. + *

Scoped values are intended to be used in a structured manner. If code + * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope} + * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected + * as a structure violation when the operation completes (normally or with an + * exception). In that case, the underlying construct of the {@code StructuredTaskScope} + * is closed and {@link StructureViolationException} is thrown. * * @param op the operation to run * @param the type of the result of the operation * @return the result + * @throws StructureViolationException if a structure violation is detected * @throws Exception if {@code op} completes with an exception * @see ScopedValue#callWhere(ScopedValue, Object, Callable) */ @@ -423,19 +424,20 @@ public R call(Callable op) throws Exception { * to its value in the current thread. * When the operation completes (normally or with an exception), each scoped value * in the mapping will revert to being unbound, or revert to its previous value - * when previously bound, in the current thread. + * when previously bound, in the current thread. If {@code op} completes with an + * exception then it propagated by this method. * - *

Scoped values are intended to be used in a structured manner. - * If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain - * StructuredTaskScope#close() close} it, then exiting {@code op} causes the - * underlying construct of each {@code StructuredTaskScope} created in the - * dynamic scope to be closed. This may require blocking until all child threads - * have completed their sub-tasks. The closing is done in the reverse order that - * they were created. Once closed, {@link StructureViolationException} is thrown. + *

Scoped values are intended to be used in a structured manner. If code + * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope} + * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected + * as a structure violation when the operation completes (normally or with an + * exception). In that case, the underlying construct of the {@code StructuredTaskScope} + * is closed and {@link StructureViolationException} is thrown. * * @param op the operation to run * @param the type of the result of the operation * @return the result + * @throws StructureViolationException if a structure violation is detected * @see ScopedValue#getWhere(ScopedValue, Object, Supplier) */ public R get(Supplier op) { @@ -486,17 +488,18 @@ private R runWith(Snapshot newSnapshot, Callable op) { * in the current thread. * When the operation completes (normally or with an exception), each scoped value * in the mapping will revert to being unbound, or revert to its previous value - * when previously bound, in the current thread. + * when previously bound, in the current thread. If {@code op} completes with an + * exception then it propagated by this method. * - *

Scoped values are intended to be used in a structured manner. - * If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain - * StructuredTaskScope#close() close} it, then exiting {@code op} causes the - * underlying construct of each {@code StructuredTaskScope} created in the - * dynamic scope to be closed. This may require blocking until all child threads - * have completed their sub-tasks. The closing is done in the reverse order that - * they were created. Once closed, {@link StructureViolationException} is thrown. + *

Scoped values are intended to be used in a structured manner. If code + * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope} + * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected + * as a structure violation when the operation completes (normally or with an + * exception). In that case, the underlying construct of the {@code StructuredTaskScope} + * is closed and {@link StructureViolationException} is thrown. * * @param op the operation to run + * @throws StructureViolationException if a structure violation is detected * @see ScopedValue#runWhere(ScopedValue, Object, Runnable) */ public void run(Runnable op) { @@ -553,15 +556,15 @@ public static Carrier where(ScopedValue key, T value) { * Calls a value-returning operation with a {@code ScopedValue} bound to a value * in the current thread. When the operation completes (normally or with an * exception), the {@code ScopedValue} will revert to being unbound, or revert to - * its previous value when previously bound, in the current thread. + * its previous value when previously bound, in the current thread. If {@code op} + * completes with an exception then it propagated by this method. * - *

Scoped values are intended to be used in a structured manner. - * If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain - * StructuredTaskScope#close() close} it, then exiting {@code op} causes the - * underlying construct of each {@code StructuredTaskScope} created in the - * dynamic scope to be closed. This may require blocking until all child threads - * have completed their sub-tasks. The closing is done in the reverse order that - * they were created. Once closed, {@link StructureViolationException} is thrown. + *

Scoped values are intended to be used in a structured manner. If code + * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope} + * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected + * as a structure violation when the operation completes (normally or with an + * exception). In that case, the underlying construct of the {@code StructuredTaskScope} + * is closed and {@link StructureViolationException} is thrown. * * @implNote * This method is implemented to be equivalent to: @@ -576,6 +579,7 @@ public static Carrier where(ScopedValue key, T value) { * @param the result type * @param op the operation to call * @return the result + * @throws StructureViolationException if a structure violation is detected * @throws Exception if the operation completes with an exception */ public static R callWhere(ScopedValue key, @@ -588,15 +592,15 @@ public static R callWhere(ScopedValue key, * Invokes a supplier of results with a {@code ScopedValue} bound to a value * in the current thread. When the operation completes (normally or with an * exception), the {@code ScopedValue} will revert to being unbound, or revert to - * its previous value when previously bound, in the current thread. + * its previous value when previously bound, in the current thread. If {@code op} + * completes with an exception then it propagated by this method. * - *

Scoped values are intended to be used in a structured manner. - * If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain - * StructuredTaskScope#close() close} it, then exiting {@code op} causes the - * underlying construct of each {@code StructuredTaskScope} created in the - * dynamic scope to be closed. This may require blocking until all child threads - * have completed their sub-tasks. The closing is done in the reverse order that - * they were created. Once closed, {@link StructureViolationException} is thrown. + *

Scoped values are intended to be used in a structured manner. If code + * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope} + * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected + * as a structure violation when the operation completes (normally or with an + * exception). In that case, the underlying construct of the {@code StructuredTaskScope} + * is closed and {@link StructureViolationException} is thrown. * * @implNote * This method is implemented to be equivalent to: @@ -611,6 +615,7 @@ public static R callWhere(ScopedValue key, * @param the result type * @param op the operation to call * @return the result + * @throws StructureViolationException if a structure violation is detected */ public static R getWhere(ScopedValue key, T value, @@ -622,15 +627,15 @@ public static R getWhere(ScopedValue key, * Run an operation with a {@code ScopedValue} bound to a value in the current * thread. When the operation completes (normally or with an exception), the * {@code ScopedValue} will revert to being unbound, or revert to its previous value - * when previously bound, in the current thread. + * when previously bound, in the current thread. If {@code op} completes with an + * exception then it propagated by this method. * - *

Scoped values are intended to be used in a structured manner. - * If {@code op} creates a {@link StructuredTaskScope} but does not {@linkplain - * StructuredTaskScope#close() close} it, then exiting {@code op} causes the - * underlying construct of each {@code StructuredTaskScope} created in the - * dynamic scope to be closed. This may require blocking until all child threads - * have completed their sub-tasks. The closing is done in the reverse order that - * they were created. Once closed, {@link StructureViolationException} is thrown. + *

Scoped values are intended to be used in a structured manner. If code + * invoked directly or indirectly by the operation creates a {@link StructuredTaskScope} + * but does not {@linkplain StructuredTaskScope#close() close} it, then it is detected + * as a structure violation when the operation completes (normally or with an + * exception). In that case, the underlying construct of the {@code StructuredTaskScope} + * is closed and {@link StructureViolationException} is thrown. * * @implNote * This method is implemented to be equivalent to: @@ -643,6 +648,7 @@ public static R getWhere(ScopedValue key, * @param value the value, can be {@code null} * @param the type of the value * @param op the operation to call + * @throws StructureViolationException if a structure violation is detected */ public static void runWhere(ScopedValue key, T value, Runnable op) { where(key, value).run(op); 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 c074cc8f896..2c2332018f0 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1252,7 +1252,7 @@ static MemorySegment ofAddress(long address) { * {@code srcOffset} through {@code srcOffset + bytes - 1} in the source segment are copied into the destination * segment at offset {@code dstOffset} through {@code dstOffset + bytes - 1}. *

- * If the source segment overlaps with this segment, then the copying is performed as if the bytes at + * If the source segment overlaps with the destination segment, then the copying is performed as if the bytes at * offset {@code srcOffset} through {@code srcOffset + bytes - 1} in the source segment were first copied into a * temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into * the destination segment at offset {@code dstOffset} through {@code dstOffset + bytes - 1}. @@ -1300,7 +1300,7 @@ static void copy(MemorySegment srcSegment, long srcOffset, * If the byte order of the two element layouts differ, the bytes corresponding to each element to be copied * are swapped accordingly during the copy operation. *

- * If the source segment overlaps with this segment, then the copying is performed as if the bytes at + * If the source segment overlaps with the destination segment, then the copying is performed as if the bytes at * offset {@code srcOffset} through {@code srcOffset + (elementCount * S) - 1} in the source segment were first copied into a * temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into * the destination segment at offset {@code dstOffset} through {@code dstOffset + (elementCount * S) - 1}. diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 51321e7cf5b..65da1cc9e74 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -2844,10 +2844,16 @@ public MethodHandle findConstructor(Class refc, MethodType type) throws NoSuc * Such a resolution, as specified in JVMS {@jvms 5.4.3.1}, attempts to locate and load the class, * and then determines whether the class is accessible to this lookup object. *

+ * For a class or an interface, the name is the {@linkplain ClassLoader##binary-name binary name}. + * For an array class of {@code n} dimensions, the name begins with {@code n} occurrences + * of {@code '['} and followed by the element type as encoded in the + * {@linkplain Class##nameFormat table} specified in {@link Class#getName}. + *

* The lookup context here is determined by the {@linkplain #lookupClass() lookup class}, * its class loader, and the {@linkplain #lookupModes() lookup modes}. * - * @param targetName the fully qualified name of the class to be looked up. + * @param targetName the {@linkplain ClassLoader##binary-name binary name} of the class + * or the string representing an array class * @return the requested class. * @throws SecurityException if a security manager is present and it * refuses access diff --git a/src/java.base/share/classes/java/text/ChoiceFormat.java b/src/java.base/share/classes/java/text/ChoiceFormat.java index 94fa85cf917..cf87f4452cf 100644 --- a/src/java.base/share/classes/java/text/ChoiceFormat.java +++ b/src/java.base/share/classes/java/text/ChoiceFormat.java @@ -343,10 +343,6 @@ public ChoiceFormat(double[] limits, String[] formats) { * If the limit array is not in ascending order, the results of formatting * will be incorrect. * @param formats are the formats you want to use for each limit. - * They can be either Format objects or Strings. - * When formatting with object Y, - * if the object is a NumberFormat, then ((NumberFormat) Y).format(X) - * is called. Otherwise Y.toString() is called. * @throws NullPointerException if {@code limits} or * {@code formats} is {@code null} */ diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java index 1f27572ef16..d136f7affce 100644 --- a/src/java.base/share/classes/java/util/ArrayList.java +++ b/src/java.base/share/classes/java/util/ArrayList.java @@ -1509,7 +1509,10 @@ private void updateSizeAndModCount(int sizeChange) { public Spliterator spliterator() { checkForComodification(); - // ArrayListSpliterator not used here due to late-binding + // This Spliterator needs to late-bind to the subList, not the outer + // ArrayList. Note that it is legal for structural changes to be made + // to a subList after spliterator() is called but before any spliterator + // operations that would causing binding are performed. return new Spliterator() { private int index = offset; // current index, modified on advance/split private int fence = -1; // -1 until used; then one past last index @@ -1628,9 +1631,7 @@ final class ArrayListSpliterator implements Spliterator { * be worthwhile in practice. To carry this out, we (1) lazily * initialize fence and expectedModCount until the latest * point that we need to commit to the state we are checking - * against; thus improving precision. (This doesn't apply to - * SubLists, that create spliterators with current non-lazy - * values). (2) We perform only a single + * against; thus improving precision. (2) We perform only a single * ConcurrentModificationException check at the end of forEach * (the most performance-sensitive method). When using forEach * (as opposed to iterators), we can normally only detect diff --git a/src/java.base/share/classes/java/util/Deque.java b/src/java.base/share/classes/java/util/Deque.java index 13a2e150d97..fd42bc356de 100644 --- a/src/java.base/share/classes/java/util/Deque.java +++ b/src/java.base/share/classes/java/util/Deque.java @@ -617,8 +617,14 @@ public interface Deque extends Queue, SequencedCollection { * {@inheritDoc} * * @implSpec - * The implementation in this interface returns an instance of a reverse-ordered - * Deque that delegates its operations to this Deque. + * The implementation in this interface returns a reverse-ordered Deque + * view. The {@code reversed()} method of the view returns a reference + * to this Deque. Other operations on the view are implemented via calls to + * public methods on this Deque. The exact relationship between calls on the + * view and calls on this Deque is unspecified. However, order-sensitive + * operations generally delegate to the appropriate method with the opposite + * orientation. For example, calling {@code getFirst} on the view results in + * a call to {@code getLast} on this Deque. * * @return a reverse-ordered view of this collection, as a {@code Deque} * @since 21 diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index e9576455ca7..17cf635aa7a 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.java @@ -888,8 +888,14 @@ default E removeLast() { * {@inheritDoc} * * @implSpec - * The implementation in this interface returns an instance of a reverse-ordered - * List that delegates its operations to this List. + * The implementation in this interface returns a reverse-ordered List + * view. The {@code reversed()} method of the view returns a reference + * to this List. Other operations on the view are implemented via calls to + * public methods on this List. The exact relationship between calls on the + * view and calls on this List is unspecified. However, order-sensitive + * operations generally delegate to the appropriate method with the opposite + * orientation. For example, calling {@code getFirst} on the view results in + * a call to {@code getLast} on this List. * * @return a reverse-ordered view of this collection, as a {@code List} * @since 21 diff --git a/src/java.base/share/classes/java/util/ReverseOrderDequeView.java b/src/java.base/share/classes/java/util/ReverseOrderDequeView.java index a580ad8f7db..695db6a74dc 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderDequeView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderDequeView.java @@ -61,7 +61,7 @@ public Iterator iterator() { } public Spliterator spliterator() { - return Spliterators.spliteratorUnknownSize(base.descendingIterator(), 0); + return Spliterators.spliterator(this, Spliterator.ORDERED); } // ========== Collection ========== diff --git a/src/java.base/share/classes/java/util/ReverseOrderListView.java b/src/java.base/share/classes/java/util/ReverseOrderListView.java index 0f7409bef16..42b57e2d8b9 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderListView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderListView.java @@ -151,8 +151,7 @@ public Iterator iterator() { } public Spliterator spliterator() { - // TODO can probably improve this - return Spliterators.spliteratorUnknownSize(new DescendingIterator(), 0); + return Spliterators.spliterator(this, Spliterator.ORDERED); } // ========== Collection ========== diff --git a/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java b/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java index 678c82fc311..3719852e2a5 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java @@ -111,7 +111,7 @@ public Iterator iterator() { } public Spliterator spliterator() { - return Spliterators.spliteratorUnknownSize(descendingIterator(base), 0); + return Spliterators.spliterator(this, Spliterator.ORDERED); } // ========== Collection ========== diff --git a/src/java.base/share/classes/java/util/SortedMap.java b/src/java.base/share/classes/java/util/SortedMap.java index 1af9d59e83e..447b0a2c7e7 100644 --- a/src/java.base/share/classes/java/util/SortedMap.java +++ b/src/java.base/share/classes/java/util/SortedMap.java @@ -316,8 +316,14 @@ default V putLast(K k, V v) { * {@inheritDoc} * * @implSpec - * The implementation in this interface returns an instance of a reverse-ordered - * SortedMap that delegates its operations to this SortedMap. + * The implementation in this interface returns a reverse-ordered SortedMap + * view. The {@code reversed()} method of the view returns a reference + * to this SortedMap. Other operations on the view are implemented via calls to + * public methods on this SortedMap. The exact relationship between calls on the + * view and calls on this SortedMap is unspecified. However, order-sensitive + * operations generally delegate to the appropriate method with the opposite + * orientation. For example, calling {@code firstEntry} on the view results in + * a call to {@code lastEntry} on this SortedMap. * * @return a reverse-ordered view of this map, as a {@code SortedMap} * @since 21 diff --git a/src/java.base/share/classes/java/util/SortedSet.java b/src/java.base/share/classes/java/util/SortedSet.java index 48473a1fed5..3b4612457c6 100644 --- a/src/java.base/share/classes/java/util/SortedSet.java +++ b/src/java.base/share/classes/java/util/SortedSet.java @@ -360,8 +360,14 @@ default E removeLast() { * {@inheritDoc} * * @implSpec - * The implementation in this interface returns an instance of a reverse-ordered - * SortedSet that delegates its operations to this SortedSet. + * The implementation in this interface returns a reverse-ordered SortedSet + * view. The {@code reversed()} method of the view returns a reference + * to this SortedSet. Other operations on the view are implemented via calls to + * public methods on this SortedSet. The exact relationship between calls on the + * view and calls on this SortedSet is unspecified. However, order-sensitive + * operations generally delegate to the appropriate method with the opposite + * orientation. For example, calling {@code getFirst} on the view results in + * a call to {@code getLast} on this SortedSet. * * @return a reverse-ordered view of this collection, as a {@code SortedSet} * @since 21 diff --git a/src/java.base/share/classes/java/util/Spliterators.java b/src/java.base/share/classes/java/util/Spliterators.java index b0d1f3a9124..c047d96ab49 100644 --- a/src/java.base/share/classes/java/util/Spliterators.java +++ b/src/java.base/share/classes/java/util/Spliterators.java @@ -948,10 +948,12 @@ static final class ArraySpliterator implements Spliterator { private int index; // current index, modified on advance/split private final int fence; // one past last index private final int characteristics; - private long estimatedSize; // estimated size, to help to split evenly + private long estimatedSize; // if >= 0, the estimated size, to help to split evenly + // if -1, exact size is known to be fence - index /** * Creates a spliterator covering all of the given array. + * Its size is known exactly and it is SIZED and SUBSIZED. * @param array the array, assumed to be unmodified during use * @param additionalCharacteristics Additional spliterator characteristics * of this spliterator's source or elements beyond {@code SIZED} and @@ -962,7 +964,8 @@ public ArraySpliterator(Object[] array, int additionalCharacteristics) { } /** - * Creates a spliterator covering the given array and range + * Creates a spliterator covering the given array and range. + * Its size is known exactly and it is SIZED and SUBSIZED. * @param array the array, assumed to be unmodified during use * @param origin the least index (inclusive) to cover * @param fence one past the greatest index to cover @@ -978,6 +981,18 @@ public ArraySpliterator(Object[] array, int origin, int fence, int additionalCha this.estimatedSize = -1; } + /** + * Creates a spliterator covering the given array and range but that is + * not SIZED or SUBSIZED. This case occurs as a result of splitting another + * spliterator that is not sized, so it's inappropriate for one of its + * sub-spliterators to be sized. + * @param array the array, assumed to be unmodified during use + * @param origin the least index (inclusive) to cover + * @param fence one past the greatest index to cover + * @param characteristics characteristics of this spliterator's source; {@code SIZED} and + * {@code SUBSIZED} are removed if present + * @param estimatedSize the size estimate; should always be nonnegative + */ private ArraySpliterator(Object[] array, int origin, int fence, int characteristics, long estimatedSize) { this.array = array; this.index = origin; diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 909621bd5ba..7182f98706d 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -1822,8 +1822,7 @@ public Iterator iterator() { } public Spliterator spliterator() { - // TODO can probably improve this - return Spliterators.spliteratorUnknownSize(new DescendingIterator(), 0); + return Spliterators.spliterator(this, Spliterator.ORDERED); } // ========== Collection ========== diff --git a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java index 3487df96c5d..4d9f8cb45b9 100644 --- a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java +++ b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java @@ -59,7 +59,7 @@ * used to get the result completed successfully, or the exception if the subtask failed. * {@snippet lang=java : * Callable task1 = ... - * Callable task1 = ... + * Callable task2 = ... * * try (var scope = new StructuredTaskScope()) { * diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index 10625828f81..c8fcac4bda0 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -30,6 +30,7 @@ import jdk.internal.misc.ThreadTracker; import sun.security.action.GetPropertyAction; import sun.security.util.ManifestEntryVerifier; +import sun.security.util.SignatureFileVerifier; import java.io.ByteArrayInputStream; import java.io.EOFException; @@ -144,8 +145,6 @@ public class JarFile extends ZipFile { private static final Runtime.Version RUNTIME_VERSION; private static final boolean MULTI_RELEASE_ENABLED; private static final boolean MULTI_RELEASE_FORCED; - // The maximum size of array to allocate. Some VMs reserve some header words in an array. - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private SoftReference manRef; private JarEntry manEntry; @@ -799,8 +798,11 @@ private void initializeVerifier() { private byte[] getBytes(ZipEntry ze) throws IOException { try (InputStream is = super.getInputStream(ze)) { long uncompressedSize = ze.getSize(); - if (uncompressedSize > MAX_ARRAY_SIZE) { - throw new IOException("Unsupported size: " + uncompressedSize); + if (uncompressedSize > SignatureFileVerifier.MAX_SIG_FILE_SIZE) { + throw new IOException("Unsupported size: " + uncompressedSize + + " for JarEntry " + ze.getName() + + ". Allowed max size: " + + SignatureFileVerifier.MAX_SIG_FILE_SIZE + " bytes"); } int len = (int)uncompressedSize; int bytesRead; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index e1c02759cb1..52d975005e0 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -69,6 +69,7 @@ import jdk.internal.vm.annotation.Stable; import sun.nio.cs.UTF_8; import sun.nio.fs.DefaultFileSystemProvider; +import sun.security.action.GetBooleanAction; import sun.security.util.SignatureFileVerifier; import static java.util.zip.ZipConstants64.*; @@ -121,6 +122,12 @@ public class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_DELETE = 0x4; + /** + * Flag which specifies whether the validation of the Zip64 extra + * fields should be disabled + */ + private static final boolean disableZip64ExtraFieldValidation = + GetBooleanAction.privilegedGetProperty("jdk.util.zip.disableZip64ExtraFieldValidation"); /** * Opens a zip file for reading. * @@ -1199,6 +1206,16 @@ private int checkAndAddEntry(int pos, int index) if (entryPos + nlen > cen.length - ENDHDR) { zerror("invalid CEN header (bad header size)"); } + + int elen = CENEXT(cen, pos); + if (elen > 0 && !disableZip64ExtraFieldValidation) { + long extraStartingOffset = pos + CENHDR + nlen; + if ((int)extraStartingOffset != extraStartingOffset) { + zerror("invalid CEN header (bad extra offset)"); + } + checkExtraFields(pos, (int)extraStartingOffset, elen); + } + try { ZipCoder zcp = zipCoderForPos(pos); int hash = zcp.checkedHash(cen, entryPos, nlen); @@ -1214,7 +1231,6 @@ private int checkAndAddEntry(int pos, int index) // a String via zcp.toString, an Exception will be thrown int clen = CENCOM(cen, pos); if (clen > 0) { - int elen = CENEXT(cen, pos); int start = entryPos + nlen + elen; zcp.toString(cen, start, clen); } @@ -1224,6 +1240,119 @@ private int checkAndAddEntry(int pos, int index) return nlen; } + /** + * Validate the Zip64 Extra block fields + * @param startingOffset Extra Field starting offset within the CEN + * @param extraFieldLen Length of this Extra field + * @throws ZipException If an error occurs validating the Zip64 Extra + * block + */ + private void checkExtraFields(int cenPos, int startingOffset, + int extraFieldLen) throws ZipException { + // Extra field Length cannot exceed 65,535 bytes per the PKWare + // APP.note 4.4.11 + if (extraFieldLen > 0xFFFF) { + zerror("invalid extra field length"); + } + // CEN Offset where this Extra field ends + int extraEndOffset = startingOffset + extraFieldLen; + if (extraEndOffset > cen.length) { + zerror("Invalid CEN header (extra data field size too long)"); + } + int currentOffset = startingOffset; + while (currentOffset < extraEndOffset) { + int tag = get16(cen, currentOffset); + currentOffset += Short.BYTES; + + int tagBlockSize = get16(cen, currentOffset); + int tagBlockEndingOffset = currentOffset + tagBlockSize; + + // The ending offset for this tag block should not go past the + // offset for the end of the extra field + if (tagBlockEndingOffset > extraEndOffset) { + zerror("Invalid CEN header (invalid zip64 extra data field size)"); + } + currentOffset += Short.BYTES; + + if (tag == ZIP64_EXTID) { + // Get the compressed size; + long csize = CENSIZ(cen, cenPos); + // Get the uncompressed size; + long size = CENLEN(cen, cenPos); + checkZip64ExtraFieldValues(currentOffset, tagBlockSize, + csize, size); + } + currentOffset += tagBlockSize; + } + } + + /** + * Validate the Zip64 Extended Information Extra Field (0x0001) block + * size and that the uncompressed size and compressed size field + * values are not negative. + * Note: As we do not use the LOC offset or Starting disk number + * field value we will not validate them + * @param off the starting offset for the Zip64 field value + * @param blockSize the size of the Zip64 Extended Extra Field + * @param csize CEN header compressed size value + * @param size CEN header uncompressed size value + * @throws ZipException if an error occurs + */ + private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, + long size) + throws ZipException { + byte[] cen = this.cen; + // Validate the Zip64 Extended Information Extra Field (0x0001) + // length. + if (!isZip64ExtBlockSizeValid(blockSize)) { + zerror("Invalid CEN header (invalid zip64 extra data field size)"); + } + // Check the uncompressed size is not negative + // Note we do not need to check blockSize is >= 8 as + // we know its length is at least 8 from the call to + // isZip64ExtBlockSizeValid() + if ((size == ZIP64_MAGICVAL)) { + if(get64(cen, off) < 0) { + zerror("Invalid zip64 extra block size value"); + } + } + // Check the compressed size is not negative + if ((csize == ZIP64_MAGICVAL) && (blockSize >= 16)) { + if (get64(cen, off + 8) < 0) { + zerror("Invalid zip64 extra block compressed size value"); + } + } + } + + /** + * Validate the size and contents of a Zip64 extended information field + * The order of the Zip64 fields is fixed, but the fields MUST + * only appear if the corresponding LOC or CEN field is set to 0xFFFF: + * or 0xFFFFFFFF: + * Uncompressed Size - 8 bytes + * Compressed Size - 8 bytes + * LOC Header offset - 8 bytes + * Disk Start Number - 4 bytes + * See PKWare APP.Note Section 4.5.3 for more details + * + * @param blockSize the Zip64 Extended Information Extra Field size + * @return true if the extra block size is valid; false otherwise + */ + private static boolean isZip64ExtBlockSizeValid(int blockSize) { + /* + * As the fields must appear in order, the block size indicates which + * fields to expect: + * 8 - uncompressed size + * 16 - uncompressed size, compressed size + * 24 - uncompressed size, compressed sise, LOC Header offset + * 28 - uncompressed size, compressed sise, LOC Header offset, + * and Disk start number + */ + return switch(blockSize) { + case 8, 16, 24, 28 -> true; + default -> false; + }; + } private int getEntryHash(int index) { return entries[index]; } private int getEntryNext(int index) { return entries[index + 1]; } private int getEntryPos(int index) { return entries[index + 2]; } diff --git a/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java b/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java index 0b8cdfa7e48..e9c75d4a3b2 100644 --- a/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java +++ b/src/java.base/share/classes/jdk/internal/misc/ThreadFlock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -451,7 +451,7 @@ public boolean isClosed() { } /** - * {@return a stream of the live threads in this flock} + * {@return a stream of the threads in this flock} * The elements of the stream are threads that were started in this flock * but have not terminated. The stream will reflect the set of threads in the * flock at some point at or since the creation of the stream. It may or may @@ -459,7 +459,7 @@ public boolean isClosed() { * stream. */ public Stream threads() { - return threads.stream().filter(Thread::isAlive); + return threads.stream(); } /** 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 0f85b538a43..b1da2186660 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -36,6 +36,7 @@ import java.util.jar.Attributes; import java.util.jar.Manifest; +import sun.security.action.GetIntegerAction; import sun.security.jca.Providers; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; @@ -83,6 +84,12 @@ public class SignatureFileVerifier { private static final String META_INF = "META-INF/"; + // the maximum allowed size in bytes for the signature-related files + public static final int MAX_SIG_FILE_SIZE = initializeMaxSigFileSize(); + + // The maximum size of array to allocate. Some VMs reserve some header words in an array. + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + /** * Create the named SignatureFileVerifier. * @@ -833,4 +840,24 @@ void updateSigners(CodeSigner[] newSigners, signerCache.add(cachedSigners); signers.put(name, cachedSigners); } + + private static int initializeMaxSigFileSize() { + /* + * System property "jdk.jar.maxSignatureFileSize" used to configure + * the maximum allowed number of bytes for the signature-related files + * in a JAR file. + */ + Integer tmp = GetIntegerAction.privilegedGetProperty( + "jdk.jar.maxSignatureFileSize", 8000000); + if (tmp < 0 || tmp > MAX_ARRAY_SIZE) { + if (debug != null) { + debug.println("Default signature file size 8000000 bytes " + + "is used as the specified size for the " + + "jdk.jar.maxSignatureFileSize system property " + + "is out of range: " + tmp); + } + tmp = 8000000; + } + return tmp; + } } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index c6f051ea17f..7b2f7e957b3 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -912,7 +912,8 @@ jdk.tls.legacyAlgorithms=NULL, anon, RC4, DES, 3DES_EDE_CBC # Note: This property is currently used by OpenJDK's JSSE implementation. It # is not guaranteed to be examined and used by other implementations. # -jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 +jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37, \ + ChaCha20-Poly1305 KeyUpdate 2^37 # # Cryptographic Jurisdiction Policy defaults diff --git a/src/java.base/share/data/cacerts/secomscrootca1 b/src/java.base/share/data/cacerts/secomscrootca1 deleted file mode 100644 index d300e31195d..00000000000 --- a/src/java.base/share/data/cacerts/secomscrootca1 +++ /dev/null @@ -1,27 +0,0 @@ -Owner: OU=Security Communication RootCA1, O=SECOM Trust.net, C=JP -Issuer: OU=Security Communication RootCA1, O=SECOM Trust.net, C=JP -Serial number: 0 -Valid from: Tue Sep 30 04:20:49 GMT 2003 until: Sat Sep 30 04:20:49 GMT 2023 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY -MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t -dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 -WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD -VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 -9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ -DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 -Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N -QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ -xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G -A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG -kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr -Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 -Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU -JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot -RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== ------END CERTIFICATE----- diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java b/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java index 6a77df29c6c..2cbc4c377a2 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java @@ -75,10 +75,6 @@ static Path fromUri(UnixFileSystem fs, URI uri) { int pos = 0; while (pos < len) { char c = p.charAt(pos++); - if ((c == '/') && (pos < len) && (p.charAt(pos) == '/')) { - // skip redundant slashes - continue; - } byte b; if (c == '%') { assert (pos+2) <= len; @@ -92,6 +88,10 @@ static Path fromUri(UnixFileSystem fs, URI uri) { throw new IllegalArgumentException("Bad escape"); b = (byte)c; } + if (b == '/' && rlen > 0 && result[rlen-1] == '/') { + // skip redundant slashes + continue; + } result[rlen++] = b; } if (rlen != result.length) diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index 04a1e5cf6b5..9ed0ed30959 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -562,6 +562,7 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) /* write the two structs and the data buffer */ if (writeFully(c->childenv[1], (char *)&magic, sizeof(magic)) != sizeof(magic)) { // magic number first + free(buf); return -1; } #ifdef DEBUG @@ -570,6 +571,7 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) if (writeFully(c->childenv[1], (char *)c, sizeof(*c)) != sizeof(*c) || writeFully(c->childenv[1], (char *)&sp, sizeof(sp)) != sizeof(sp) || writeFully(c->childenv[1], buf, bufsize) != bufsize) { + free(buf); return -1; } /* We're done. Let jspwanhelper know he can't expect any more data from us. */ diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java index 329f183ce7a..66d89ae1fc5 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java @@ -45,6 +45,7 @@ import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.ResponseInfo; import java.net.http.HttpResponse.BodySubscriber; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import jdk.internal.net.http.ResponseSubscribers.PathSubscriber; @@ -229,16 +230,120 @@ private FileDownloadBodyHandler(Path directory, static final String DISPOSITION_TYPE = "attachment;"; /** The "filename" parameter. */ - static final Pattern FILENAME = Pattern.compile("filename\\s*=", CASE_INSENSITIVE); + static final Pattern FILENAME = Pattern.compile("filename\\s*=\\s*", CASE_INSENSITIVE); static final List PROHIBITED = List.of(".", "..", "", "~" , "|"); + // Characters disallowed in token values + + static final Set NOT_ALLOWED_IN_TOKEN = Set.of( + '(', ')', '<', '>', '@', + ',', ';', ':', '\\', '"', + '/', '[', ']', '?', '=', + '{', '}', ' ', '\t'); + + static boolean allowedInToken(char c) { + if (NOT_ALLOWED_IN_TOKEN.contains(c)) + return false; + // exclude CTL chars <= 31, == 127, or anything >= 128 + return isTokenText(c); + } + static final UncheckedIOException unchecked(ResponseInfo rinfo, String msg) { String s = String.format("%s in response [%d, %s]", msg, rinfo.statusCode(), rinfo.headers()); return new UncheckedIOException(new IOException(s)); } + static final UncheckedIOException unchecked(String msg) { + return new UncheckedIOException(new IOException(msg)); + } + + // Process a "filename=" parameter, which is either a "token" + // or a "quoted string". If a token, it is terminated by a + // semicolon or the end of the string. + // If a quoted string (surrounded by "" chars then the closing " + // terminates the name. + // quoted strings may contain quoted-pairs (eg embedded " chars) + + static String processFilename(String src) throws UncheckedIOException { + if ("".equals(src)) + return src; + if (src.charAt(0) == '\"') { + return processQuotedString(src.substring(1)); + } else { + return processToken(src); + } + } + + static boolean isTokenText(char c) throws UncheckedIOException { + return c > 31 && c < 127; + } + + static boolean isQuotedStringText(char c) throws UncheckedIOException { + return c > 31; + } + + static String processQuotedString(String src) throws UncheckedIOException { + boolean inqpair = false; + int len = src.length(); + StringBuilder sb = new StringBuilder(); + + for (int i=0; i apply(ResponseInfo responseInfo) { String dispoHeader = responseInfo.headers().firstValue("Content-Disposition") @@ -256,13 +361,7 @@ public BodySubscriber apply(ResponseInfo responseInfo) { } int n = matcher.end(); - int semi = dispoHeader.substring(n).indexOf(";"); - String filenameParam; - if (semi < 0) { - filenameParam = dispoHeader.substring(n); - } else { - filenameParam = dispoHeader.substring(n, n + semi); - } + String filenameParam = processFilename(dispoHeader.substring(n)); // strip all but the last path segment int x = filenameParam.lastIndexOf("/"); @@ -276,19 +375,6 @@ public BodySubscriber apply(ResponseInfo responseInfo) { filenameParam = filenameParam.trim(); - if (filenameParam.startsWith("\"")) { // quoted-string - if (!filenameParam.endsWith("\"") || filenameParam.length() == 1) { - throw unchecked(responseInfo, - "Badly quoted Content-Disposition filename parameter"); - } - filenameParam = filenameParam.substring(1, filenameParam.length() -1 ); - } else { // token, - if (filenameParam.contains(" ")) { // space disallowed - throw unchecked(responseInfo, - "unquoted space in Content-Disposition filename parameter"); - } - } - if (PROHIBITED.contains(filenameParam)) { throw unchecked(responseInfo, "Prohibited Content-Disposition filename parameter:" 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 1412c3644dd..7214220ba9d 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 @@ -1734,6 +1734,8 @@ private void handleSwitch(JCTree switchTree, Symbol enumSym = TreeInfo.symbol(expr); if (enumSym == null || !enumSym.isEnum() || enumSym.kind != VAR) { log.error(expr.pos(), Errors.EnumLabelMustBeEnumConstant); + } else if (!constants.add(enumSym)) { + log.error(label.pos(), Errors.DuplicateCaseLabel); } } else { log.error(expr.pos(), Errors.EnumLabelMustBeUnqualifiedEnum); @@ -1765,6 +1767,8 @@ private void handleSwitch(JCTree switchTree, (stringSwitch ? Errors.StringConstReq : intSwitch ? Errors.ConstExprReq : Errors.PatternOrEnumReq)); + } else if (!constants.add(s)) { + log.error(label.pos(), Errors.DuplicateCaseLabel); } } else if (!stringSwitch && !intSwitch) { log.error(label.pos(), Errors.ConstantLabelNotCompatible(pattype, seltype)); 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 57061d38dd8..e6de3f574b3 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 @@ -4733,10 +4733,13 @@ private boolean patternDominated(JCPattern existingPattern, JCPattern currentPat return false; } } - if (currentPattern instanceof JCBindingPattern) { - return existingPattern instanceof JCBindingPattern; + if (currentPattern instanceof JCBindingPattern || + currentPattern instanceof JCAnyPattern) { + return existingPattern instanceof JCBindingPattern || + existingPattern instanceof JCAnyPattern; } else if (currentPattern instanceof JCRecordPattern currentRecordPattern) { - if (existingPattern instanceof JCBindingPattern) { + if (existingPattern instanceof JCBindingPattern || + existingPattern instanceof JCAnyPattern) { return true; } else if (existingPattern instanceof JCRecordPattern existingRecordPattern) { List existingNested = existingRecordPattern.nested; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index ff77c9cdf18..d897dc3ef24 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -773,15 +773,16 @@ private boolean exhausts(JCExpression selector, List cases) { patternSet.add(new BindingPattern(e.getKey().type)); } } - List patterns = List.from(patternSet); + Set patterns = patternSet; try { boolean repeat = true; while (repeat) { - List updatedPatterns; + Set updatedPatterns; updatedPatterns = reduceBindingPatterns(selector.type, patterns); updatedPatterns = reduceNestedPatterns(updatedPatterns); updatedPatterns = reduceRecordPatterns(updatedPatterns); - repeat = updatedPatterns != patterns; + updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); + repeat = !updatedPatterns.equals(patterns); patterns = updatedPatterns; if (checkCovered(selector.type, patterns)) { return true; @@ -794,7 +795,7 @@ private boolean exhausts(JCExpression selector, List cases) { } } - private boolean checkCovered(Type seltype, List patterns) { + private boolean checkCovered(Type seltype, Iterable patterns) { for (Type seltypeComponent : components(seltype)) { for (PatternDescription pd : patterns) { if (pd instanceof BindingPattern bp && @@ -830,7 +831,7 @@ private List components(Type seltype) { * is found, it is removed, and replaced with a binding pattern * for the sealed supertype. */ - private List reduceBindingPatterns(Type selectorType, List patterns) { + private Set reduceBindingPatterns(Type selectorType, Set patterns) { Set existingBindings = patterns.stream() .filter(pd -> pd instanceof BindingPattern) .map(pd -> ((BindingPattern) pd).type.tsym) @@ -838,12 +839,13 @@ private List reduceBindingPatterns(Type selectorType, List

toRemove = new HashSet<>(); Set toAdd = new HashSet<>(); for (Type sup : types.directSupertypes(bpOne.type)) { ClassSymbol clazz = (ClassSymbol) sup.tsym; + clazz.complete(); + if (clazz.isSealed() && clazz.isAbstract() && //if a binding pattern for clazz already exists, no need to analyze it again: !existingBindings.contains(clazz)) { @@ -869,7 +871,6 @@ private List reduceBindingPatterns(Type selectorType, List

currentPermittedSubTypes = allPermittedSubTypes((ClassSymbol) bpOther.type.tsym, s -> true); @@ -886,31 +887,21 @@ private List reduceBindingPatterns(Type selectorType, List

newPatterns = new HashSet<>(patterns); + newPatterns.addAll(toAdd); + return newPatterns; } } } @@ -926,6 +917,8 @@ private Set allPermittedSubTypes(ClassSymbol root, Predicate allPermittedSubTypes(ClassSymbol root, Predicate reduceNestedPatterns(List patterns) { + private Set reduceNestedPatterns(Set patterns) { /* implementation note: * finding a sub-set of patterns that only differ in a single * column is time-consuming task, so this method speeds it up by: @@ -971,13 +964,14 @@ private List reduceNestedPatterns(List p for (var e : groupByRecordClass.entrySet()) { int nestedPatternsCount = e.getKey().getRecordComponents().size(); + Set current = new HashSet<>(e.getValue()); for (int mismatchingCandidate = 0; mismatchingCandidate < nestedPatternsCount; mismatchingCandidate++) { int mismatchingCandidateFin = mismatchingCandidate; var groupByHashes = - e.getValue() + current .stream() //error recovery, ignore patterns with incorrect number of nested patterns: .filter(pd -> pd.nested.length == nestedPatternsCount) @@ -1012,37 +1006,35 @@ private List reduceNestedPatterns(List p } } - var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(List.collector()); + var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(Collectors.toSet()); var updatedPatterns = reduceNestedPatterns(nestedPatterns); updatedPatterns = reduceRecordPatterns(updatedPatterns); + updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); updatedPatterns = reduceBindingPatterns(rpOne.fullComponentTypes()[mismatchingCandidateFin], updatedPatterns); - if (nestedPatterns != updatedPatterns) { - ListBuffer result = new ListBuffer<>(); - Set toRemove = Collections.newSetFromMap(new IdentityHashMap<>()); - - toRemove.addAll(join); - - for (PatternDescription p : patterns) { - if (!toRemove.contains(p)) { - result.append(p); - } - } + if (!nestedPatterns.equals(updatedPatterns)) { + current.removeAll(join); for (PatternDescription nested : updatedPatterns) { PatternDescription[] newNested = Arrays.copyOf(rpOne.nested, rpOne.nested.length); newNested[mismatchingCandidateFin] = nested; - result.append(new RecordPattern(rpOne.recordType(), + current.add(new RecordPattern(rpOne.recordType(), rpOne.fullComponentTypes(), newNested)); } - return result.toList(); } } } } + + if (!current.equals(new HashSet<>(e.getValue()))) { + Set result = new HashSet<>(patterns); + result.removeAll(e.getValue()); + result.addAll(current); + return result; + } } return patterns; } @@ -1052,22 +1044,22 @@ private List reduceNestedPatterns(List p * all the $nestedX pattern cover the given record component, * and replace those with a simple binding pattern over $record. */ - private List reduceRecordPatterns(List patterns) { - var newPatterns = new ListBuffer(); + private Set reduceRecordPatterns(Set patterns) { + var newPatterns = new HashSet(); boolean modified = false; for (PatternDescription pd : patterns) { if (pd instanceof RecordPattern rpOne) { PatternDescription reducedPattern = reduceRecordPattern(rpOne); if (reducedPattern != rpOne) { - newPatterns.append(reducedPattern); + newPatterns.add(reducedPattern); modified = true; continue; } } - newPatterns.append(pd); + newPatterns.add(pd); } - return modified ? newPatterns.toList() : patterns; - } + return modified ? newPatterns : patterns; + } private PatternDescription reduceRecordPattern(PatternDescription pattern) { if (pattern instanceof RecordPattern rpOne) { @@ -1099,6 +1091,23 @@ private PatternDescription reduceRecordPattern(PatternDescription pattern) { return pattern; } + private Set removeCoveredRecordPatterns(Set patterns) { + Set existingBindings = patterns.stream() + .filter(pd -> pd instanceof BindingPattern) + .map(pd -> ((BindingPattern) pd).type.tsym) + .collect(Collectors.toSet()); + Set result = new HashSet<>(patterns); + + for (Iterator it = result.iterator(); it.hasNext();) { + PatternDescription pd = it.next(); + if (pd instanceof RecordPattern rp && existingBindings.contains(rp.recordType.tsym)) { + it.remove(); + } + } + + return result; + } + public void visitTry(JCTry tree) { ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index b0b176b36df..6f6c3a192e8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -1546,7 +1546,10 @@ protected void scanDocComment() { return; } - skip('*'); + if (skip('*') != 0 && is('/')) { + return ; + } + skipWhitespace(); if (isEOLN()) { 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 5cf5a91150c..ff662ac1320 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 @@ -879,6 +879,7 @@ public JCPattern parsePattern(int pos, JCModifiers mods, JCExpression parsedType JCExpression e; if (token.kind == UNDERSCORE && parsedType == null) { nextToken(); + checkSourceLevel(Feature.UNNAMED_VARIABLES); pattern = toP(F.at(token.pos).AnyPattern()); } else { diff --git a/src/jdk.internal.le/linux/native/lible/CLibrary.cpp b/src/jdk.internal.le/linux/native/lible/CLibrary.cpp index 8ce6cf42c26..7815fe0cd2e 100644 --- a/src/jdk.internal.le/linux/native/lible/CLibrary.cpp +++ b/src/jdk.internal.le/linux/native/lible/CLibrary.cpp @@ -187,6 +187,7 @@ JNIEXPORT void JNICALL Java_jdk_internal_org_jline_terminal_impl_jna_linux_CLibr int error = ttyname_r(fd, data, len); if (error != 0) { + delete[] data; throw_errno(env); return ; } diff --git a/src/jdk.internal.le/macosx/native/lible/CLibrary.cpp b/src/jdk.internal.le/macosx/native/lible/CLibrary.cpp index 545d8023453..760e090f5e8 100644 --- a/src/jdk.internal.le/macosx/native/lible/CLibrary.cpp +++ b/src/jdk.internal.le/macosx/native/lible/CLibrary.cpp @@ -191,6 +191,7 @@ JNIEXPORT void JNICALL Java_jdk_internal_org_jline_terminal_impl_jna_osx_CLibrar int error = ttyname_r(fd, data, len); if (error != 0) { + delete[] data; throw_errno(env); return ; } diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index 5c0ecc49655..117fea688c7 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -753,6 +753,13 @@ void verifyJar(String jarName) && SignatureFileVerifier.isBlockOrSF(name)) { String alias = name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf('.')); + long uncompressedSize = je.getSize(); + if (uncompressedSize > SignatureFileVerifier.MAX_SIG_FILE_SIZE) { + unparsableSignatures.putIfAbsent(alias, String.format( + rb.getString("history.unparsable"), name)); + continue; + } + try { if (name.endsWith(".SF")) { Manifest sf = new Manifest(is); diff --git a/src/jdk.javadoc/share/man/javadoc.1 b/src/jdk.javadoc/share/man/javadoc.1 index df34deefd54..f25033fd70b 100644 --- a/src/jdk.javadoc/share/man/javadoc.1 +++ b/src/jdk.javadoc/share/man/javadoc.1 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. +.\" Copyright (c) 1994, 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 @@ -35,7 +35,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "JAVADOC" "1" "2023" "JDK 21-ea" "JDK Commands" +.TH "JAVADOC" "1" "2023" "JDK 21" "JDK Commands" .hy .SH NAME .PP @@ -84,9 +84,9 @@ options, package names, and source file names in any order. The \f[V]javadoc\f[R] tool parses the declarations and documentation comments in a set of Java source files and produces corresponding HTML pages that describe (by default) the public and protected classes, -nested classes (but not anonymous inner classes), interfaces, -constructors, methods, and fields. -You can use the \f[V]javadoc\f[R] tool to generate the API documentation +nested and unnamed classes (but not anonymous inner classes), +interfaces, constructors, methods, and fields. +You can use the\f[V]javadoc\f[R] tool to generate the API documentation or the implementation documentation for a set of source files. .PP You can run the \f[V]javadoc\f[R] tool on entire packages, individual diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java index 7595339cc36..4031e70d24b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java @@ -56,6 +56,9 @@ public String getOSName() { * @return the OS thread ID, or {@code -1} if doesn't exist */ public long getOSThreadId() { + if (isVirtual()) { + return -1L; + } Long l = getTyped("osThreadId", Long.class, -1L); return l.longValue(); } @@ -90,7 +93,8 @@ public String getJavaName() { */ public long getJavaThreadId() { Long l = getTyped("javaThreadId", Long.class, -1L); - return l.longValue(); + long id = l.longValue(); + return id == 0 ? -1L : id; } /** diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java index 1e9f68c47ca..f12cf68628e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java @@ -33,7 +33,7 @@ import jdk.jfr.internal.SecuritySupport.SafePath; // This class keeps track of files that can't be deleted -// so they can a later staged be removed. +// so they can at a later staged be removed. final class FilePurger { private static final Set paths = new LinkedHashSet<>(); @@ -63,6 +63,13 @@ private static void removeOldest() { } private static boolean delete(SafePath p) { + try { + if (!SecuritySupport.exists(p)) { + return true; + } + } catch (IOException e) { + // ignore + } try { SecuritySupport.delete(p); return true; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index c06ea36a920..c5a769c529c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -457,7 +457,17 @@ public boolean hasNativeJFR() { /** * Flushes the EventWriter for this thread. */ - public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize); + public static native void flush(EventWriter writer, int uncommittedSize, int requestedSize); + + /** + * Commits an event to the underlying buffer by setting the nextPosition. + * + * @param nextPosition + * + * @return the next startPosition + */ + @IntrinsicCandidate + public static native long commit(long nextPosition); /** * Flushes all thread buffers to disk and the constant pool data needed to read @@ -641,4 +651,11 @@ public boolean hasNativeJFR() { * JVM runs in a container. */ public native long hostTotalMemory(); + + /** + * Emit a jdk.DataLoss event for the specified amount of bytes. + * + * @param bytes number of bytes that were lost + */ + public static native void emitDataLoss(long bytes); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java index a6c0be983de..cf10a83ba64 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java @@ -25,6 +25,7 @@ package jdk.jfr.internal; +import static jdk.jfr.internal.LogLevel.ERROR; import static jdk.jfr.internal.LogLevel.INFO; import static jdk.jfr.internal.LogLevel.TRACE; import static jdk.jfr.internal.LogLevel.WARN; @@ -448,10 +449,19 @@ private void startDiskMonitor() { } private void finishChunk(RepositoryChunk chunk, Instant time, PlatformRecording ignoreMe) { - chunk.finish(time); - for (PlatformRecording r : getRecordings()) { - if (r != ignoreMe && r.getState() == RecordingState.RUNNING) { - r.appendChunk(chunk); + if (chunk.finish(time)) { + for (PlatformRecording r : getRecordings()) { + if (r != ignoreMe && r.getState() == RecordingState.RUNNING) { + r.appendChunk(chunk); + } + } + } else { + if (chunk.isMissingFile()) { + // With one chunkfile found missing, its likely more could've been removed too. Iterate through all recordings, + // and check for missing files. This will emit more error logs that can be seen in subsequent recordings. + for (PlatformRecording r : getRecordings()) { + r.removeNonExistantPaths(); + } } } // Decrease initial reference count @@ -498,17 +508,24 @@ private void periodicTask() { return; } while (true) { - synchronized (this) { - if (jvm.shouldRotateDisk()) { - rotateDisk(); - } - if (isToDisk()) { - EventLog.update(); + long wait = Options.getWaitInterval(); + try { + synchronized (this) { + if (jvm.shouldRotateDisk()) { + rotateDisk(); + } + if (isToDisk()) { + EventLog.update(); + } } + long minDelta = PeriodicEvents.doPeriodic(); + wait = Math.min(minDelta, Options.getWaitInterval()); + } catch (Throwable t) { + // Catch everything and log, but don't allow it to end the periodic task + Logger.log(JFR_SYSTEM, ERROR, "Error in Periodic task: " + t.getClass().getName()); + } finally { + takeNap(wait); } - long minDelta = PeriodicEvents.doPeriodic(); - long wait = Math.min(minDelta, Options.getWaitInterval()); - takeNap(wait); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java index 54aaa8a727f..53c67b899fd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java @@ -26,12 +26,15 @@ package jdk.jfr.internal; import static jdk.jfr.internal.LogLevel.DEBUG; +import static jdk.jfr.internal.LogLevel.ERROR; +import static jdk.jfr.internal.LogLevel.INFO; import static jdk.jfr.internal.LogLevel.WARN; import static jdk.jfr.internal.LogTag.JFR; import java.io.IOException; import java.io.InputStream; import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; import java.nio.file.StandardOpenOption; import java.security.AccessControlContext; import java.security.AccessController; @@ -716,17 +719,33 @@ public void dump(WriteableUserPath writeableUserPath) throws IOException { public void dumpStopped(WriteableUserPath userPath) throws IOException { synchronized (recorder) { - userPath.doPrivilegedIO(() -> { - try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(userPath.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { - long bytes = cc.transferTo(fc); - Logger.log(LogTag.JFR, LogLevel.INFO, "Transferred " + bytes + " bytes from the disk repository"); - // No need to force if no data was transferred, which avoids IOException when device is /dev/null - if (bytes != 0) { - fc.force(true); - } - } - return null; - }); + transferChunksWithRetry(userPath); + } + } + + private void transferChunksWithRetry(WriteableUserPath userPath) throws IOException { + userPath.doPrivilegedIO(() -> { + try { + transferChunks(userPath); + } catch (NoSuchFileException nsfe) { + Logger.log(LogTag.JFR, LogLevel.ERROR, "Missing chunkfile when writing recording \"" + name + "\" (" + id + ") to " + userPath.getRealPathText() + "."); + // if one chunkfile was missing, its likely more are missing + removeNonExistantPaths(); + // and try the transfer again + transferChunks(userPath); + } + return null; + }); + } + + private void transferChunks(WriteableUserPath userPath) throws IOException { + try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(userPath.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { + long bytes = cc.transferTo(fc); + Logger.log(LogTag.JFR, LogLevel.INFO, "Transferred " + bytes + " bytes from the disk repository"); + // No need to force if no data was transferred, which avoids IOException when device is /dev/null + if (bytes != 0) { + fc.force(true); + } } } @@ -878,4 +897,27 @@ public void removePath(SafePath path) { } } } + + void removeNonExistantPaths() { + synchronized (recorder) { + Iterator it = chunks.iterator(); + Logger.log(JFR, INFO, "Checking for missing chunkfiles for recording \"" + name + "\" (" + id + ")"); + while (it.hasNext()) { + RepositoryChunk chunk = it.next(); + if (chunk.isMissingFile()) { + String msg = "Chunkfile \"" + chunk.getFile() + "\" is missing. " + + "Data loss might occur from " + chunk.getStartTime(); + if (chunk.getEndTime() != null) { + msg += " to " + chunk.getEndTime(); + } + Logger.log(JFR, ERROR, msg); + + JVM.emitDataLoss(chunk.getSize()); + + it.remove(); + removed(chunk); + } + } + } + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java index 58cb9e53738..72d3a719452 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java @@ -29,7 +29,10 @@ import java.io.RandomAccessFile; import java.nio.channels.ReadableByteChannel; import java.time.Instant; +import java.time.Period; +import java.time.Duration; import java.util.Comparator; +import java.util.Optional; import jdk.jfr.internal.SecuritySupport.SafePath; @@ -55,20 +58,25 @@ public int compare(RepositoryChunk c1, RepositoryChunk c2) { this.unFinishedRAF = SecuritySupport.createRandomAccessFile(chunkFile); } - void finish(Instant endTime) { + boolean finish(Instant endTime) { try { - finishWithException(endTime); + unFinishedRAF.close(); + size = SecuritySupport.getFileSize(chunkFile); + this.endTime = endTime; + if (Logger.shouldLog(LogTag.JFR_SYSTEM, LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Chunk finished: " + chunkFile); + } + return true; } catch (IOException e) { - Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not finish chunk. " + e.getClass() + " "+ e.getMessage()); - } - } - - private void finishWithException(Instant endTime) throws IOException { - unFinishedRAF.close(); - this.size = SecuritySupport.getFileSize(chunkFile); - this.endTime = endTime; - if (Logger.shouldLog(LogTag.JFR_SYSTEM, LogLevel.DEBUG)) { - Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Chunk finished: " + chunkFile); + final String reason; + if (isMissingFile()) { + reason = "Chunkfile \""+ getFile() + "\" is missing. " + + "Data loss might occur from " + getStartTime() + " to " + endTime; + } else { + reason = e.getClass().getName(); + } + Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not finish chunk. " + reason); + return false; } } @@ -103,16 +111,14 @@ private void delete(SafePath f) { } private void destroy() { - if (!isFinished()) { - finish(Instant.MIN); - } - delete(chunkFile); try { unFinishedRAF.close(); } catch (IOException e) { if (Logger.shouldLog(LogTag.JFR, LogLevel.ERROR)) { Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not close random access file: " + chunkFile.toString() + ". File will not be deleted due to: " + e.getMessage()); } + } finally { + delete(chunkFile); } } @@ -174,4 +180,12 @@ public long getCurrentFileSize() { return 0L; } } + + boolean isMissingFile() { + try { + return !SecuritySupport.exists(chunkFile); + } catch (IOException ioe) { + return true; + } + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java index 0ce7832dd99..6513895069e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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,8 @@ public final class StringPool { private static final int MAX_SIZE = 32 * 1024; /* max size bytes */ private static final long MAX_SIZE_UTF16 = 16 * 1024 * 1024; + /* mask for constructing generation relative string id. */ + private static final long SID_MASK = -65536; /* string id index */ private static final AtomicLong sidIdx = new AtomicLong(1); /* looking at a biased data set 4 is a good value */ @@ -48,18 +50,69 @@ public final class StringPool { private static int preCacheOld = 0; /* max size bytes */ private static long currentSizeUTF16; + /* string pool generation (0-65535) set by the JVM on epoch shift. Not private to avoid being optimized away. */ + static short generation = 0; - public static void reset() { - cache.clear(); - synchronized (StringPool.class) { - currentSizeUTF16 = 0; + /* internalSid is a composite id [48-bit externalSid][16-bit generation]. */ + private static boolean isCurrentGeneration(long internalSid) { + return generation == (short)internalSid; + } + + private static long updateInternalSid(long internalSid) { + return (internalSid & SID_MASK) | generation; + } + + private static long nextInternalSid() { + return sidIdx.getAndIncrement() << 16 | generation; + } + + /* externalSid is the most significant 48-bits of the internalSid. */ + private static long externalSid(long internalSid) { + return internalSid >> 16; + } + + /* synchronized because of writing the string to the JVM. */ + private static synchronized long storeString(String s) { + Long lsid = cache.get(s); + long internalSid; + if (lsid != null) { + internalSid = lsid.longValue(); + if (isCurrentGeneration(internalSid)) { + // Someone already updated the cache. + return externalSid(internalSid); + } + internalSid = updateInternalSid(internalSid); + } else { + // Not yet added or the cache was cleared. + internalSid = nextInternalSid(); + currentSizeUTF16 += s.length(); } + long extSid = externalSid(internalSid); + // Write the string to the JVM before publishing to the cache. + JVM.addStringConstant(extSid, s); + cache.put(s, internalSid); + return extSid; + } + + /* a string fetched from the string pool must be of the current generation */ + private static long ensureCurrentGeneration(String s, Long lsid) { + long internalSid = lsid.longValue(); + return isCurrentGeneration(internalSid) ? externalSid(internalSid) : storeString(s); } + /* + * The string pool uses a generational id scheme to sync the JVM and Java sides. + * The string pool relies on the EventWriter and its implementation, especially + * its ability to restart event write attempts on interleaving epoch shifts. + * Even though a string id is generationally valid during StringPool lookup, + * the JVM can evolve the generation before the event is committed, + * effectively invalidating the fetched string id. The event restart mechanism + * of the EventWriter ensures that committed strings are in the correct generation. + */ public static long addString(String s) { Long lsid = cache.get(s); if (lsid != null) { - return lsid.longValue(); + return ensureCurrentGeneration(s, lsid); } if (!preCache(s)) { /* we should not pool this string */ @@ -72,17 +125,6 @@ public static long addString(String s) { return storeString(s); } - private static long storeString(String s) { - long sid = sidIdx.getAndIncrement(); - /* we can race but it is ok */ - cache.put(s, sid); - synchronized (StringPool.class) { - JVM.addStringConstant(sid, s); - currentSizeUTF16 += s.length(); - } - return sid; - } - private static boolean preCache(String s) { if (preCache[0].equals(s)) { return true; @@ -100,4 +142,9 @@ private static boolean preCache(String s) { preCache[preCacheOld] = s; return false; } + + private static synchronized void reset() { + cache.clear(); + currentSizeUTF16 = 0; + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java index 3f2d52a95c7..70fb31950fe 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java @@ -63,15 +63,12 @@ public final class EventWriter { // The JVM needs access to these values. Don't remove private final long threadID; private long startPosition; - private long startPositionAddress; private long currentPosition; private long maxPosition; private boolean valid; - boolean notified; // Not private to avoid being optimized away boolean excluded; private PlatformEventType eventType; - private boolean flushOnEnd; private boolean largeSize = false; // User code must not be able to instantiate @@ -213,10 +210,6 @@ private void reserveEventSizeField() { public void reset() { currentPosition = startPosition; - if (flushOnEnd) { - flushOnEnd = flush(); - } - valid = true; } private boolean isValidForSize(int requestedSize) { @@ -224,7 +217,7 @@ private boolean isValidForSize(int requestedSize) { return false; } if (currentPosition + requestedSize > maxPosition) { - flushOnEnd = flush(usedSize(), requestedSize); + flush(usedSize(), requestedSize); // retry if (!valid) { return false; @@ -233,31 +226,18 @@ private boolean isValidForSize(int requestedSize) { return true; } - private boolean isNotified() { - return notified; - } - - private void resetNotified() { - notified = false; - } - - private void resetStringPool() { - StringPool.reset(); - } - private int usedSize() { return (int) (currentPosition - startPosition); } - private boolean flush() { - return flush(usedSize(), 0); + private void flush() { + flush(usedSize(), 0); } - private boolean flush(int usedSize, int requestedSize) { - return JVM.flush(this, usedSize, requestedSize); + private void flush(int usedSize, int requestedSize) { + JVM.flush(this, usedSize, requestedSize); } - public boolean beginEvent(EventConfiguration configuration, long typeId) { // Malicious code could take the EventConfiguration object from one // event class field and assign it to another. This check makes sure @@ -278,6 +258,7 @@ public boolean beginEvent(EventConfiguration configuration, long typeId) { public boolean endEvent() { if (!valid) { reset(); + valid = true; return true; } final int eventSize = usedSize(); @@ -285,7 +266,6 @@ public boolean endEvent() { reset(); return true; } - if (largeSize) { Bits.putInt(startPosition, makePaddedInt(eventSize)); } else { @@ -299,32 +279,29 @@ public boolean endEvent() { return false; } } - - if (isNotified()) { - resetNotified(); - resetStringPool(); - reset(); - // returning false will trigger restart of the event write attempt - return false; + long nextPosition = JVM.commit(currentPosition); + if (nextPosition == currentPosition) { + // Successful commit. Update the writer start position. + startPosition = nextPosition; + return true; } - startPosition = currentPosition; - unsafe.storeStoreFence(); - unsafe.putAddress(startPositionAddress, currentPosition); - // the event is now committed - if (flushOnEnd) { - flushOnEnd = flush(); + // If nextPosition == 0, the event was committed, the underlying buffer lease + // returned and new writer positions updated. Nothing to do. + if (nextPosition == 0) { + return true; } - return true; + // The commit was aborted because of an interleaving epoch shift. + // The nextPosition returned is the current start position. + // Reset the writer and return false to restart the write attempt. + currentPosition = nextPosition; + return false; } - private EventWriter(long startPos, long maxPos, long startPosAddress, long threadID, boolean valid, boolean excluded) { + private EventWriter(long startPos, long maxPos, long threadID, boolean valid, boolean excluded) { startPosition = currentPosition = startPos; maxPosition = maxPos; - startPositionAddress = startPosAddress; this.threadID = threadID; - flushOnEnd = false; this.valid = valid; - notified = false; this.excluded = excluded; } diff --git a/src/jdk.jfr/share/man/jfr.1 b/src/jdk.jfr/share/man/jfr.1 index 7538c429726..ebd77bac983 100644 --- a/src/jdk.jfr/share/man/jfr.1 +++ b/src/jdk.jfr/share/man/jfr.1 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. +.\" Copyright (c) 2019, 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 @@ -39,16 +39,32 @@ .hy .SH NAME .PP -jfr - parse and print Flight Recorder files +jfr - print and manipulate Flight Recorder files .SH SYNOPSIS .PP To print the contents of a flight recording to standard out: .PP \f[V]jfr\f[R] \f[V]print\f[R] [\f[I]options\f[R]] \f[I]file\f[R] .PP +To display aggregated event data on standard out: +.PP +\f[V]jfr\f[R] \f[V]view\f[R] [\f[I]options\f[R]] \f[I]file\f[R] +.PP +To configure a .jfc settings file: +.PP +\f[V]jfr\f[R] \f[V]configure\f[R] [\f[I]options\f[R]] +.PP To print metadata information about flight recording events: .PP -\f[V]jfr\f[R] \f[V]metadata\f[R] \f[I]file\f[R] +\f[V]jfr\f[R] \f[V]metadata\f[R] [\f[I]file\f[R]] +.PP +To view the summary statistics for a flight recording file: +.PP +\f[V]jfr\f[R] \f[V]summary\f[R] \f[I]file\f[R] +.PP +To remove events from a flight recording file: +.PP +\f[V]jfr\f[R] \f[V]scrub\f[R] [\f[I]options\f[R]] \f[I]file\f[R] .PP To assemble chunk files into a flight recording file: .PP @@ -57,10 +73,6 @@ To assemble chunk files into a flight recording file: To disassemble a flight recording file into chunk files: .PP \f[V]jfr\f[R] \f[V]disassmble\f[R] [\f[I]options\f[R]] \f[I]file\f[R] -.PP -To view the summary statistics for a flight recording file: -.PP -\f[V]jfr\f[R] \f[V]summary\f[R] \f[I]file\f[R] .TP \f[I]options\f[R] Optional: Specifies command-line options separated by spaces. @@ -79,7 +91,8 @@ The \f[V]jfr\f[R] command provides a tool for interacting with flight recorder files (\f[V].jfr\f[R]). The main function is to filter, summarize and output flight recording files into human readable format. -There is also support for merging and splitting recording files. +There is also support for scrubbing, merging and splitting recording +files. .PP Flight recording files are created and saved as binary formatted files. Having a tool that can extract the contents from a flight recording and @@ -91,17 +104,24 @@ The \f[V]jfr\f[R] command has several subcommands: .IP \[bu] 2 \f[V]print\f[R] .IP \[bu] 2 +\f[V]view\f[R] +.IP \[bu] 2 +\f[V]configure\f[R] +.IP \[bu] 2 +\f[V]metadata\f[R] +.IP \[bu] 2 \f[V]summary\f[R] .IP \[bu] 2 +\f[V]scrub\f[R] +.IP \[bu] 2 \f[V]assemble\f[R] .IP \[bu] 2 \f[V]disassemble\f[R] -.IP \[bu] 2 -\f[V]metadata\f[R] .SS \f[V]jfr print\f[R] subcommand .PP Use \f[V]jfr print\f[R] to print the contents of a flight recording file to standard out. +.PP The syntax is: .PP \f[V]jfr print\f[R] [\f[V]--xml\f[R]|\f[V]--json\f[R]] @@ -112,23 +132,23 @@ The syntax is: where: .TP \f[V]--xml\f[R] -Print the recording in XML format +Print the recording in XML format. .TP \f[V]--json\f[R] -Print the recording in JSON format +Print the recording in JSON format. .TP \f[V]--categories\f[R] <\f[I]filters\f[R]> Select events matching a category name. The filter is a comma-separated list of names, simple and/or qualified, -and/or quoted glob patterns +and/or quoted glob patterns. .TP \f[V]--events\f[R] <\f[I]filters\f[R]> Select events matching an event name. The filter is a comma-separated list of names, simple and/or qualified, -and/or quoted glob patterns +and/or quoted glob patterns. .TP \f[V]--stack-depth\f[R] <\f[I]depth\f[R]> -Number of frames in stack traces, by default 5 +Number of frames in stack traces, by default 5. .TP <\f[I]file\f[R]> Location of the recording file (\f[V].jfr\f[R]) @@ -164,6 +184,111 @@ that has the value 0.52 is formatted as 52%. Stack traces are by default truncated to 5 frames, but the number can be increased/decreased using the \f[V]--stack-depth\f[R] command-line option. +.SS \f[V]jfr view\f[R] subcommand +.PP +Use \f[V]jfr view\f[R] to aggregate and display event data on standard +out. +.PP +The syntax is: +.PP +\f[V]jfr view\f[R] [\f[V]--verbose\f[R]] [\f[V]--width\f[R] +<\f[I]integer\f[R]>] [\f[V]--truncate\f[R] <\f[I]mode\f[R]>] +[\f[V]--cell-height\f[R] <\f[I]integer\f[R]>] <\f[I]view\f[R]> +<\f[I]file\f[R]> +.PP +where: +.TP +\f[V]--verbose\f[R] +Displays the query that makes up the view. +.TP +\f[V]--width\f[R] <\f[I]integer\f[R]> +The width of the view in characters. +Default value depends on the view. +.TP +\f[V]--truncate\f[R] <\f[I]mode\f[R]> +How to truncate content that exceeds space in a table cell. +Mode can be \[aq]beginning\[aq] or \[aq]end\[aq]. +Default value is \[aq]end\[aq]. +.TP +\f[V]--cell-height\f[R] <\f[I]integer\f[R]> +Maximum number of rows in a table cell. +Default value depends on the view. +.TP +<\f[I]view\f[R]> +Name of the view or event type to display. +Use \f[V]jfr --help view\f[R] to see a list of available views. +.TP +<\f[I]file\f[R]> +Location of the recording file (.jfr) +.PP +The parameter can be an event type name. +Use the \f[V]jfr view types \f[R] to see a list. +To display all views, use \f[V]jfr view all-views \f[R]. +To display all events, use \f[V]jfr view all-events \f[R]. +.SS \f[V]jfr configure\f[R] subcommand +.PP +Use \f[V]jfr configure\f[R] to configure a .jfc settings file. +.PP +The syntax is: +.PP +\f[V]jfr configure\f[R] [--interactive] [--verbose] [--input ] [--output +] [option=value]* [event-setting=value]* +.TP +\f[V]--interactive\f[R] +Interactive mode where the configuration is determined by a set of +questions. +.TP +\f[V]--verbose\f[R] +Displays the modified settings. +.TP +\f[V]--input\f[R] <\f[I]files\f[R]> +A comma-separated list of .jfc files from which the new configuration is +based. +If no file is specified, the default file in the JDK is used +(default.jfc). +If \[aq]none\[aq] is specified, the new configuration starts empty. +.TP +\f[V]--output\f[R] <\f[I]file\f[R]> +The filename of the generated output file. +If not specified, the filename custom.jfc will be used. +.TP +\f[I]option=value\f[R] +The option value to modify. +To see available options, use \f[V]jfr help configure\f[R] +.TP +\f[I]event-setting=value\f[R] +The event setting value to modify. +Use the form: <\f[I]event-name>#= To add a new event +setting, prefix the event name with \[aq]+\[aq]. +.PP +The whitespace delimiter can be omitted for timespan values, i.e. +20ms. +For more information about the settings syntax, see Javadoc of the +jdk.jfr package. +.SS \f[V]jfr metadata\f[R] subcommand +.PP +Use \f[V]jfr metadata\f[R] to display information about events, such as +event names, categories and field layout within a flight recording file. +.PP +The syntax is: +.PP +\f[V]jfr metadata\f[R] [--categories ] [--events ] [] +.TP +\f[V]--categories\f[R] <\f[I]filter\f[R]> +Select events matching a category name. +The filter is a comma-separated list of names, simple and/or qualified, +and/or quoted glob patterns. +.TP +\f[V]--events\f[R] <\f[I]filter\f[R]> +Select events matching an event name. +The filter is a comma-separated list of names, simple and/or qualified, +and/or quoted glob patterns. +.TP +<\f[I]file\f[R]> +Location of the recording file (.jfr) +.PP +If the parameter is omitted, metadata from the JDK where the +\[aq]jfr\[aq] tool is located will be used. .SS \f[V]jfr summary\f[R] subcommand .PP Use \f[V]jfr summary\f[R] to print statistics for a recording. @@ -180,18 +305,47 @@ where: .TP <\f[I]file\f[R]> Location of the flight recording file (\f[V].jfr\f[R]) -.SS \f[V]jfr metadata\f[R] subcommand +.SS \f[V]jfr scrub\f[R] subcommand +.PP +Use \f[V]jfr scrub\f[R] to remove sensitive contents from a file or to +reduce its size. .PP -Use \f[V]jfr metadata\f[R] to view information about events, such as -event names, categories and field layout within a flight recording file. The syntax is: .PP -\f[V]jfr metadata\f[R] <\f[I]file\f[R]> +\f[V]jfr scrub\f[R] [--include-events <\f[I]filter\f[R]>] +[--exclude-events <\f[I]filter\f[R]>] [--include-categories +<\f[I]filter\f[R]>] [--exclude-categories <\f[I]filter\f[R]>] +[--include-threads <\f[I]filter\f[R]>] [--exclude-threads +<\f[I]filter\f[R]>] <\f[I]input-file\f[R]> [] +.TP +\f[V]--include-events\f[R] <\f[I]filter\f[R]> +Select events matching an event name. +.TP +\f[V]--exclude-events\f[R] <\f[I]filter\f[R]> +Exclude events matching an event name. +.TP +\f[V]--include-categories\f[R] <\f[I]filter\f[R]> +Select events matching a category name. +.TP +\f[V]--exclude-categories\f[R] <\f[I]filter\f[R]> +Exclude events matching a category name. +.TP +\f[V]--include-threads\f[R] <\f[I]filter\f[R]> +Select events matching a thread name. +.TP +\f[V]--exclude-threads\f[R] <\f[I]filter\f[R]> +Exclude events matching a thread name. .PP -where: +<\f[I]input-file\f[R]> :The input file to read events from. .TP -<\f[I]file\f[R]> -Location of the flight recording file (\f[V].jfr\f[R]) +<\f[I]output-file\f[R]> +The output file to write filter events to. +If no file is specified, it will be written to the same path as the +input file, but with \[dq]-scrubbed\[dq] appended to the filename. +.PP +The filter is a comma-separated list of names, simple and/or qualified, +and/or quoted glob patterns. +If multiple filters are used, they are applied in the specified order. .SS jfr \f[V]assemble\f[R] subcommand .PP Use jfr \f[V]assemble\f[R] to assemble chunk files into a recording @@ -204,10 +358,10 @@ The syntax is: where: .TP <\f[I]repository\f[R]> -Directory where the repository containing chunk files is located +Directory where the repository containing chunk files is located. .TP <\f[I]file\f[R]> -Location of the flight recording file (\f[V].jfr\f[R]) +Location of the flight recording file (\f[V].jfr\f[R]). .PP Flight recording information is written in chunks. A chunk contains all of the information necessary for parsing. @@ -220,6 +374,7 @@ files that are not finished (.part) are excluded. .PP Use \f[V]jfr disassemble\f[R] to decompose a flight recording file into its chunk file pieces. +.PP The syntax is: .PP \f[V]jfr disassemble\f[R] [\f[V]--max-chunks\f[R] <\f[I]chunks\f[R]>] @@ -266,10 +421,16 @@ where: .IP \[bu] 2 \f[V]print\f[R] .IP \[bu] 2 +\f[V]view\f[R] +.IP \[bu] 2 +\f[V]configure\f[R] +.IP \[bu] 2 \f[V]metadata\f[R] .IP \[bu] 2 \f[V]summary\f[R] .IP \[bu] 2 +\f[V]scrub\f[R] +.IP \[bu] 2 \f[V]assemble\f[R] .IP \[bu] 2 \f[V]disassemble\f[R] diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java index 9310e0fd49a..2dad37186ed 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java @@ -79,7 +79,8 @@ public class JdiInitiator { * @param timeout the start-up time-out in milliseconds. If zero or negative, * will not wait thus will timeout immediately if not already started. * @param customConnectorArgs custom arguments passed to the connector. - * These are JDI com.sun.jdi.connect.Connector arguments. + * These are JDI com.sun.jdi.connect.Connector arguments. The {@code vmexec} + * argument is not supported. */ @SuppressWarnings("this-escape") public JdiInitiator(int port, List remoteVMOptions, String remoteAgent, @@ -105,7 +106,10 @@ public JdiInitiator(int port, List remoteVMOptions, String remoteAgent, argumentName2Value.put("localAddress", host); } } - argumentName2Value.putAll(customConnectorArgs); + customConnectorArgs.entrySet() + .stream() + .filter(e -> !"vmexec".equals(e.getKey())) + .forEach(e -> argumentName2Value.put(e.getKey(), e.getValue())); this.connectorArgs = mergeConnectorArgs(connector, argumentName2Value); this.vm = isLaunch ? launchTarget() diff --git a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java index 705ca4fb596..03b2a3c0f57 100644 --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java @@ -3070,6 +3070,11 @@ private void readExtra(ZipFileSystem zipfs) throws IOException { if (extra == null) return; int elen = extra.length; + // Extra field Length cannot exceed 65,535 bytes per the PKWare + // APP.note 4.4.11 + if (elen > 0xFFFF) { + throw new ZipException("invalid extra field length"); + } int off = 0; int newOff = 0; boolean hasZip64LocOffset = false; @@ -3079,26 +3084,40 @@ private void readExtra(ZipFileSystem zipfs) throws IOException { int tag = SH(extra, pos); int sz = SH(extra, pos + 2); pos += 4; - if (pos + sz > elen) // invalid data - break; + if (pos + sz > elen) { // invalid data + throw new ZipException("Invalid CEN header (invalid zip64 extra data field size)"); + } switch (tag) { case EXTID_ZIP64 : + // Check to see if we have a valid block size + if (!isZip64ExtBlockSizeValid(sz)) { + throw new ZipException("Invalid CEN header (invalid zip64 extra data field size)"); + } if (size == ZIP64_MINVAL) { if (pos + 8 > elen) // invalid zip64 extra break; // fields, just skip size = LL(extra, pos); + if (size < 0) { + throw new ZipException("Invalid zip64 extra block size value"); + } pos += 8; } if (csize == ZIP64_MINVAL) { if (pos + 8 > elen) break; csize = LL(extra, pos); + if (csize < 0) { + throw new ZipException("Invalid zip64 extra block compressed size value"); + } pos += 8; } if (locoff == ZIP64_MINVAL) { if (pos + 8 > elen) break; locoff = LL(extra, pos); + if (locoff < 0) { + throw new ZipException("Invalid zip64 extra block LOC offset value"); + } } break; case EXTID_NTFS: @@ -3156,6 +3175,36 @@ private void readExtra(ZipFileSystem zipfs) throws IOException { extra = null; } + /** + * Validate the size and contents of a Zip64 extended information field + * The order of the Zip64 fields is fixed, but the fields MUST + * only appear if the corresponding LOC or CEN field is set to 0xFFFF: + * or 0xFFFFFFFF: + * Uncompressed Size - 8 bytes + * Compressed Size - 8 bytes + * LOC Header offset - 8 bytes + * Disk Start Number - 4 bytes + * See PKWare APP.Note Section 4.5.3 for more details + * + * @param blockSize the Zip64 Extended Information Extra Field size + * @return true if the extra block size is valid; false otherwise + */ + private static boolean isZip64ExtBlockSizeValid(int blockSize) { + /* + * As the fields must appear in order, the block size indicates which + * fields to expect: + * 8 - uncompressed size + * 16 - uncompressed size, compressed size + * 24 - uncompressed size, compressed sise, LOC Header offset + * 28 - uncompressed size, compressed sise, LOC Header offset, + * and Disk start number + */ + return switch(blockSize) { + case 8, 16, 24, 28 -> true; + default -> false; + }; + } + /** * Read the LOC extra field to obtain the Info-ZIP Extended Timestamp fields * @param zipfs The Zip FS to use diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index d5e82ecc571..beb2681713e 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -119,6 +119,7 @@ serviceability/sa/TestRevPtrsForInvokeDynamic.java 8241235 generic-all serviceability/jvmti/ModuleAwareAgents/ThreadStart/MAAThreadStart.java 8225354 windows-all serviceability/jvmti/vthread/GetSetLocalTest/GetSetLocalTest.java 8286836 generic-all +serviceability/jvmti/vthread/VThreadTLSTest/VThreadTLSTest.java#id1 8300051 generic-all serviceability/dcmd/gc/RunFinalizationTest.java 8227120 linux-all,windows-x64 serviceability/sa/ClhsdbCDSCore.java 8294316,8267433 macosx-x64 @@ -173,5 +174,3 @@ vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manySame_b/TestDescription.java vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8076494 windows-x64 - -vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001.java 8310551 linux-all diff --git a/test/hotspot/jtreg/compiler/splitif/TestCrashAtIGVNSplitIfSubType.java b/test/hotspot/jtreg/compiler/splitif/TestCrashAtIGVNSplitIfSubType.java new file mode 100644 index 00000000000..72af0fd132c --- /dev/null +++ b/test/hotspot/jtreg/compiler/splitif/TestCrashAtIGVNSplitIfSubType.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, 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 + * @bug 8303279 + * @summary C2: crash in SubTypeCheckNode::sub() at IGVN split if + * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:StressSeed=598200189 TestCrashAtIGVNSplitIfSubType + * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN TestCrashAtIGVNSplitIfSubType + */ + +public class TestCrashAtIGVNSplitIfSubType { + private static volatile int barrier; + + public static void main(String[] args) { + A a = new A(); + B b = new B(); + for (int i = 0; i < 20_000; i++) { + test(a); + test(b); + testHelper1(null, 0); + } + } + + private static void test(Object o) { + int i = 2; + for (; i < 4; i *= 2) { + + } + o = testHelper1(o, i); + if (o instanceof A) { + barrier = 0x42; + } + } + + private static Object testHelper1(Object o, int i) { + if (i < 3) { + o = null; + } else { + if (o == null) { + } + } + if (i < 2) { + barrier = 42; + } + return o; + } + + private static class A { + } + + private static class B { + } +} diff --git a/test/hotspot/jtreg/runtime/jni/nativeStack/TestNativeStack.java b/test/hotspot/jtreg/runtime/jni/nativeStack/TestNativeStack.java index 36880fe256c..81ce1da7846 100644 --- a/test/hotspot/jtreg/runtime/jni/nativeStack/TestNativeStack.java +++ b/test/hotspot/jtreg/runtime/jni/nativeStack/TestNativeStack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -24,7 +24,7 @@ /* * @test * @bug 8295974 - * @requires os.family != "windows" + * @requires os.family != "windows" & os.arch != "arm" * @library /test/lib * @summary Generate a JNI Fatal error, or a warning, in a launched VM and check * the native stack is present as expected. diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTLSTest/VThreadTLSTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTLSTest/VThreadTLSTest.java new file mode 100644 index 00000000000..34d480991bb --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTLSTest/VThreadTLSTest.java @@ -0,0 +1,102 @@ +/* + * 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. + * + * 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 Verifies JVMTI GetLocalStorage/SetLocalStorage + * @requires vm.continuations + * @requires vm.jvmti + * @run main/othervm/native -agentlib:VThreadTLSTest VThreadTLSTest + */ + +/** + * @test + * @bug 8311556 + * @summary Verifies JVMTI GetLocalStorage/SetLocalStorage + * @requires vm.continuations + * @requires vm.jvmti + * @run main/othervm/native -Djdk.attach.allowAttachSelf=true -XX:+EnableDynamicAgentLoading VThreadTLSTest attach + */ + +import com.sun.tools.attach.VirtualMachine; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class VThreadTLSTest { + static final String AGENT_LIB = "VThreadTLSTest"; + static volatile boolean attached; + static volatile boolean failed; + + static void log(String msg) { System.out.println(msg); } + static native long getTLS(); + static native void setTLS(long value); + + static void test() { + try { + while (!attached) { + // keep mounted + } + long threadId = Thread.currentThread().threadId(); + setTLS(threadId); + long mountedValue = getTLS(); + + if (mountedValue != threadId) { + log("Error: wrong TLS value while mounted: " + threadId + ", " + mountedValue); + failed = true; + return; + } + for (int count = 0; count < 10; count++) { + Thread.sleep(1); + long tlsValue = getTLS(); + if (tlsValue != threadId) { + log("Error: wrong TLS value after yield: expected: " + threadId + " got: " + tlsValue); + failed = true; + return; + } + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws Exception { + try (ExecutorService execService = Executors.newVirtualThreadPerTaskExecutor()) { + for (int threadCount = 0; threadCount < 20; threadCount++) { + execService.execute(() -> test()); + } + if (args.length == 1 && args[0].equals("attach")) { + log("loading " + AGENT_LIB + " lib"); + VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); + vm.loadAgentLibrary(AGENT_LIB); + } + Thread.sleep(10); + attached = true; + } + if (failed) { + throw new RuntimeException("Test FAILED: errors encountered"); + } else { + log("Test passed"); + } + } +} + diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTLSTest/libVThreadTLSTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTLSTest/libVThreadTLSTest.cpp new file mode 100644 index 00000000000..ac4da3a76a7 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTLSTest/libVThreadTLSTest.cpp @@ -0,0 +1,79 @@ +/* + * 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. + * + * 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 +#include +#include +#include "jvmti_common.h" + +extern "C" { + +static jvmtiEnv *jvmti; + +JNIEXPORT jlong JNICALL +Java_VThreadTLSTest_getTLS(JNIEnv* jni, jclass clazz) { + void* data; + jvmtiError err = jvmti->GetThreadLocalStorage(nullptr, &data); + check_jvmti_status(jni, err, "getTLS: Failed in JVMTI GetThreadLocalStorage"); + return (jlong)data; +} + +JNIEXPORT void JNICALL +Java_VThreadTLSTest_setTLS(JNIEnv* jni, jclass clazz, jlong value) { + jvmtiError err = jvmti->SetThreadLocalStorage(nullptr, (void*)value); + check_jvmti_status(jni, err, "setTLS: Failed in JVMTI SetThreadLocalStorage"); +} + +jint agent_init(JavaVM *jvm, char *options, void *reserved) { + jvmtiCapabilities caps; + jvmtiError err; + + if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) { + LOG("agent_init: could not initialize JVMTI\n"); + return JNI_ERR; + } + memset(&caps, 0, sizeof(caps)); + caps.can_support_virtual_threads = 1; + + err = jvmti->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + LOG("agent_init: error in JVMTI AddCapabilities: %s (%d)\n", TranslateError(err), err); + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + LOG("Agent_OnLoad\n"); + return agent_init(jvm, options, reserved); +} + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { + LOG("Agent_OnAttach\n"); + return agent_init(jvm, options, reserved); +} + +} // extern "C" + diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001.java index a2d46e4ac15..73f251b71e9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -89,7 +89,7 @@ public static int run(String argv[], PrintStream out) { static final String LAST_BREAK = DEBUGGEE_CLASS + ".breakHere"; static final String MYTHREAD = "MyThread"; static final String DEBUGGEE_THREAD = DEBUGGEE_CLASS + "$" + MYTHREAD; - static final String DEBUGGEE_RESULT = DEBUGGEE_CLASS + ".notInterrupted.get()"; + static final String DEBUGGEE_RESULT = DEBUGGEE_CLASS + ".notInterrupted"; static int numThreads = nsk.jdb.interrupt.interrupt001.interrupt001a.numThreads; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001a.java index 973ee4974cd..25016ce0f37 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/interrupt/interrupt001/interrupt001a.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -56,8 +56,8 @@ public void run() { lock.wait(); } } catch (InterruptedException e) { - notInterrupted.decrementAndGet(); synchronized (waitnotify) { + notInterrupted--; waitnotify.notify(); } } @@ -83,7 +83,7 @@ static void breakHere () {} private JdbArgumentHandler argumentHandler; private Log log; - public static final AtomicInteger notInterrupted = new AtomicInteger(numThreads); + public static volatile int notInterrupted = numThreads; public int runIt(String args[], PrintStream out) { @@ -122,8 +122,8 @@ public int runIt(String args[], PrintStream out) { long waitTime = argumentHandler.getWaitTime() * 60 * 1000; long startTime = System.currentTimeMillis(); - while (notInterrupted.get() > 0 && System.currentTimeMillis() - startTime <= waitTime) { - synchronized (waitnotify) { + synchronized (waitnotify) { + while (notInterrupted > 0 && System.currentTimeMillis() - startTime <= waitTime) { try { waitnotify.wait(waitTime); } catch (InterruptedException e) { diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 1af30f0e294..cbe04d49e11 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -579,8 +579,6 @@ com/sun/nio/sctp/SctpChannel/SocketOptionTests.java 8141694 linux-al sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8161536 generic-all -sun/security/tools/keytool/ListKeychainStore.sh 8156889 macosx-all - sun/security/smartcardio/TestChannel.java 8039280 generic-all sun/security/smartcardio/TestConnect.java 8039280 generic-all sun/security/smartcardio/TestConnectAgain.java 8039280 generic-all diff --git a/test/jdk/java/net/httpclient/AsFileDownloadTest.java b/test/jdk/java/net/httpclient/AsFileDownloadTest.java index 81d5a3d0eba..efccdc0e470 100644 --- a/test/jdk/java/net/httpclient/AsFileDownloadTest.java +++ b/test/jdk/java/net/httpclient/AsFileDownloadTest.java @@ -71,7 +71,7 @@ /* * @test * @summary Basic test for ofFileDownload - * @bug 8196965 + * @bug 8196965 8302475 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * jdk.test.lib.Platform jdk.test.lib.util.FileUtils @@ -125,18 +125,18 @@ public class AsFileDownloadTest { { "024", "attachment; filename=me.txt; filename*=utf-8''you.txt", "me.txt" }, { "025", "attachment; filename=\"m y.txt\"; filename*=utf-8''you.txt", "m y.txt" }, - { "030", "attachment; filename=foo/file1.txt", "file1.txt" }, - { "031", "attachment; filename=foo/bar/file2.txt", "file2.txt" }, - { "032", "attachment; filename=baz\\file3.txt", "file3.txt" }, - { "033", "attachment; filename=baz\\bar\\file4.txt", "file4.txt" }, - { "034", "attachment; filename=x/y\\file5.txt", "file5.txt" }, - { "035", "attachment; filename=x/y\\file6.txt", "file6.txt" }, - { "036", "attachment; filename=x/y\\z/file7.txt", "file7.txt" }, - { "037", "attachment; filename=x/y\\z/\\x/file8.txt", "file8.txt" }, - { "038", "attachment; filename=/root/file9.txt", "file9.txt" }, - { "039", "attachment; filename=../file10.txt", "file10.txt" }, - { "040", "attachment; filename=..\\file11.txt", "file11.txt" }, - { "041", "attachment; filename=foo/../../file12.txt", "file12.txt" }, + { "030", "attachment; filename=\"foo/file1.txt\"", "file1.txt" }, + { "031", "attachment; filename=\"foo/bar/file2.txt\"", "file2.txt" }, + { "032", "attachment; filename=\"baz\\\\file3.txt\"", "file3.txt" }, + { "033", "attachment; filename=\"baz\\\\bar\\\\file4.txt\"", "file4.txt" }, + { "034", "attachment; filename=\"x/y\\\\file5.txt\"", "file5.txt" }, + { "035", "attachment; filename=\"x/y\\\\file6.txt\"", "file6.txt" }, + { "036", "attachment; filename=\"x/y\\\\z/file7.txt\"", "file7.txt" }, + { "037", "attachment; filename=\"x/y\\\\z/\\\\x/file8.txt\"", "file8.txt" }, + { "038", "attachment; filename=\"/root/file9.txt\"", "file9.txt" }, + { "039", "attachment; filename=\"../file10.txt\"", "file10.txt" }, + { "040", "attachment; filename=\"..\\\\file11.txt\"", "file11.txt" }, + { "041", "attachment; filename=\"foo/../../file12.txt\"", "file12.txt" }, }; @DataProvider(name = "positive") @@ -177,19 +177,24 @@ void test(String uriString, String contentDispositionValue, String expectedFilen BodyHandler bh = ofFileDownload(tempDir.resolve(uri.getPath().substring(1)), CREATE, TRUNCATE_EXISTING, WRITE); HttpResponse response = client.send(request, bh); - + Path body = response.body(); out.println("Got response: " + response); - out.println("Got body Path: " + response.body()); + out.println("Got body Path: " + body); String fileContents = new String(Files.readAllBytes(response.body()), UTF_8); out.println("Got body: " + fileContents); assertEquals(response.statusCode(), 200); - assertEquals(response.body().getFileName().toString(), expectedFilename); + assertEquals(body.getFileName().toString(), expectedFilename); assertTrue(response.headers().firstValue("Content-Disposition").isPresent()); assertEquals(response.headers().firstValue("Content-Disposition").get(), contentDispositionValue); assertEquals(fileContents, "May the luck of the Irish be with you!"); + if (!body.toAbsolutePath().startsWith(tempDir.toAbsolutePath())) { + System.out.println("Tempdir = " + tempDir.toAbsolutePath()); + System.out.println("body = " + body.toAbsolutePath()); + throw new AssertionError("body in wrong location"); + } // additional checks unrelated to file download caseInsensitivityOfHeaders(request.headers()); caseInsensitivityOfHeaders(response.headers()); diff --git a/test/jdk/java/util/concurrent/StructuredTaskScope/StressShutdown.java b/test/jdk/java/util/concurrent/StructuredTaskScope/StressShutdown.java new file mode 100644 index 00000000000..592333a169e --- /dev/null +++ b/test/jdk/java/util/concurrent/StructuredTaskScope/StressShutdown.java @@ -0,0 +1,95 @@ +/* + * 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. + * + * 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 8311867 + * @summary Stress test of StructuredTaskScope.shutdown with running and starting threads + * @enablePreview + * @run junit StressShutdown + */ + +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.StructuredTaskScope; +import java.util.concurrent.ThreadFactory; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; + +class StressShutdown { + + static final Callable SLEEP_FOR_A_DAY = () -> { + Thread.sleep(Duration.ofDays(1)); + return null; + }; + + static Stream testCases() { + Stream factories = Stream.of( + Thread.ofPlatform().factory(), + Thread.ofVirtual().factory() + ); + // 0..15 forks before shutdown, 0..15 forks after shutdown + return factories.flatMap(f -> IntStream.range(0, 256) + .mapToObj(x -> Arguments.of(f, x & 0x0F, (x & 0xF0) >> 4))); + } + + /** + * Test StructuredTaskScope.shutdown with running threads and concurrently with + * threads that are starting. The shutdown should interrupt all threads so that + * join wakes up. + * + * @param factory the ThreadFactory to use + * @param beforeShutdown the number of subtasks to fork before shutdown + * @param afterShutdown the number of subtasks to fork after shutdown + */ + @ParameterizedTest + @MethodSource("testCases") + void testShutdown(ThreadFactory factory, int beforeShutdown, int afterShutdown) + throws InterruptedException + { + try (var scope = new StructuredTaskScope<>(null, factory)) { + // fork subtasks + for (int i = 0; i < beforeShutdown; i++) { + scope.fork(SLEEP_FOR_A_DAY); + } + + // fork subtask to shutdown + scope.fork(() -> { + scope.shutdown(); + return null; + }); + + // fork after forking subtask to shutdown + for (int i = 0; i < afterShutdown; i++) { + scope.fork(SLEEP_FOR_A_DAY); + } + + scope.join(); + } + } +} diff --git a/test/jdk/java/util/zip/TestExtraTime.java b/test/jdk/java/util/zip/TestExtraTime.java index a60e810df0f..0e68e764546 100644 --- a/test/jdk/java/util/zip/TestExtraTime.java +++ b/test/jdk/java/util/zip/TestExtraTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -29,6 +29,8 @@ */ import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -59,7 +61,14 @@ public static void main(String[] args) throws Throwable{ TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai"); - for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) { + // A structurally valid extra data example + byte[] sampleExtra = new byte[Short.BYTES*3]; + ByteBuffer.wrap(sampleExtra).order(ByteOrder.LITTLE_ENDIAN) + .putShort((short) 123) // ID: 123 + .putShort((short) Short.BYTES) // Size: 2 + .putShort((short) 42); // Data: Two bytes + + for (byte[] extra : new byte[][] { null, sampleExtra}) { // ms-dos 1980 epoch problem test0(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra); diff --git a/test/jdk/java/util/zip/ZipFile/CorruptedZipFiles.java b/test/jdk/java/util/zip/ZipFile/CorruptedZipFiles.java index e622a266785..8885e739be1 100644 --- a/test/jdk/java/util/zip/ZipFile/CorruptedZipFiles.java +++ b/test/jdk/java/util/zip/ZipFile/CorruptedZipFiles.java @@ -260,7 +260,7 @@ public void insufficientFilenameLength() throws IOException { public void excessiveExtraFieldLength() throws IOException { short existingExtraLength = buffer.getShort(cenpos + CENEXT); buffer.putShort(cenpos+CENEXT, (short) (existingExtraLength + 1)); - assertZipException(".*bad header size.*"); + assertZipException(".*invalid zip64 extra data field size.*"); } /* @@ -271,7 +271,7 @@ public void excessiveExtraFieldLength() throws IOException { @Test public void excessiveExtraFieldLength2() throws IOException { buffer.putShort(cenpos+CENEXT, (short) 0xfdfd); - assertZipException(".*bad header size.*"); + assertZipException(".*extra data field size too long.*"); } /* diff --git a/test/jdk/jdk/jfr/event/runtime/TestNativeMemoryUsageEvents.java b/test/jdk/jdk/jfr/event/runtime/TestNativeMemoryUsageEvents.java index 14165afb31e..d922ffaa45d 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestNativeMemoryUsageEvents.java +++ b/test/jdk/jdk/jfr/event/runtime/TestNativeMemoryUsageEvents.java @@ -44,8 +44,8 @@ * @library /test/lib * @modules jdk.jfr * jdk.management - * @run main/othervm -XX:NativeMemoryTracking=summary -Xms16m -Xmx128m -Xlog:gc jdk.jfr.event.runtime.TestNativeMemoryUsageEvents true - * @run main/othervm -XX:NativeMemoryTracking=off -Xms16m -Xmx128m -Xlog:gc jdk.jfr.event.runtime.TestNativeMemoryUsageEvents false + * @run main/othervm -XX:NativeMemoryTracking=summary -Xms16m -Xmx128m -XX:-UseLargePages -Xlog:gc jdk.jfr.event.runtime.TestNativeMemoryUsageEvents true + * @run main/othervm -XX:NativeMemoryTracking=off -Xms16m -Xmx128m -XX:-UseLargePages -Xlog:gc jdk.jfr.event.runtime.TestNativeMemoryUsageEvents false */ public class TestNativeMemoryUsageEvents { private final static String UsageTotalEvent = EventNames.NativeMemoryUsageTotal; diff --git a/test/jdk/jdk/jfr/jvm/TestChunkIntegrity.java b/test/jdk/jdk/jfr/jvm/TestChunkIntegrity.java index 0a192431811..0ca887609f4 100644 --- a/test/jdk/jdk/jfr/jvm/TestChunkIntegrity.java +++ b/test/jdk/jdk/jfr/jvm/TestChunkIntegrity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -56,7 +56,6 @@ /** * @test - * @ignore * @key jfr * @requires vm.hasJFR * @library /test/lib /test/jdk diff --git a/test/jdk/jdk/jfr/threading/TestManyVirtualThreads.java b/test/jdk/jdk/jfr/threading/TestManyVirtualThreads.java index 8991d9a3812..1a2ea5238c9 100644 --- a/test/jdk/jdk/jfr/threading/TestManyVirtualThreads.java +++ b/test/jdk/jdk/jfr/threading/TestManyVirtualThreads.java @@ -89,11 +89,11 @@ public static void main(String... args) throws Exception { RecordedThread t = e.getThread(); Asserts.assertNotNull(t); Asserts.assertTrue(t.isVirtual()); + Asserts.assertEquals(t.getOSName(), null); + Asserts.assertEquals(t.getOSThreadId(), -1L); Asserts.assertEquals(t.getJavaName(), ""); // vthreads default name is the empty string. - Asserts.assertEquals(t.getOSName(), ""); - Asserts.assertEquals(t.getThreadGroup().getName(), "VirtualThreads"); Asserts.assertGreaterThan(t.getJavaThreadId(), 0L); - Asserts.assertEquals(t.getOSThreadId(), 0L); + Asserts.assertEquals(t.getThreadGroup().getName(), "VirtualThreads"); } } } diff --git a/test/jdk/jdk/jfr/threading/TestNestedVirtualThreads.java b/test/jdk/jdk/jfr/threading/TestNestedVirtualThreads.java index 6d4dbb766e1..2cf95d62281 100644 --- a/test/jdk/jdk/jfr/threading/TestNestedVirtualThreads.java +++ b/test/jdk/jdk/jfr/threading/TestNestedVirtualThreads.java @@ -74,11 +74,11 @@ public static void main(String... args) throws Exception { RecordedEvent e = events.get(0); RecordedThread t = e.getThread(); Asserts.assertTrue(t.isVirtual()); + Asserts.assertEquals(t.getOSName(), null); + Asserts.assertEquals(t.getOSThreadId(), -1L); Asserts.assertEquals(t.getJavaName(), ""); // vthreads default name is the empty string. - Asserts.assertEquals(t.getOSName(), ""); - Asserts.assertEquals(t.getThreadGroup().getName(), "VirtualThreads"); Asserts.assertGreaterThan(t.getJavaThreadId(), 0L); - Asserts.assertEquals(t.getOSThreadId(), 0L); + Asserts.assertEquals(t.getThreadGroup().getName(), "VirtualThreads"); } } } diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 842706d9017..e982a1e548d 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -28,7 +28,7 @@ * 8209452 8209506 8210432 8195793 8216577 8222089 8222133 8222137 8222136 * 8223499 8225392 8232019 8234245 8233223 8225068 8225069 8243321 8243320 * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 - * 8305975 8304760 8307134 + * 8305975 8304760 8307134 8295894 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -48,13 +48,13 @@ public class VerifyCACerts { // The numbers of certs now. // SapMachine 2021-09-23: Additional certificate for SAP - private static final int COUNT = 98; + private static final int COUNT = 97; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 // SapMachine 2021-09-23: Additional certificate for SAP private static final String CHECKSUM - = "D1:B3:9E:4B:44:0B:EA:87:BE:75:88:7B:2E:C2:7E:51:55:43:43:01:2B:FB:0A:9C:EC:69:FC:A4:DE:97:F3:B7"; + = "70:B9:49:37:03:6A:B4:39:25:08:2D:5F:EE:48:4A:C2:53:EA:33:3B:BC:A2:A3:DF:BA:0F:4D:54:97:7C:97:6C"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -165,8 +165,6 @@ public class VerifyCACerts { "18:F1:FC:7F:20:5D:F8:AD:DD:EB:7F:E0:07:DD:57:E3:AF:37:5A:9C:4D:8D:73:54:6B:F4:F1:FE:D1:E1:8D:35"); put("quovadisrootca3g3 [jdk]", "88:EF:81:DE:20:2E:B0:18:45:2E:43:F8:64:72:5C:EA:5F:BD:1F:C2:D9:D2:05:73:07:09:C5:D8:B8:69:0F:46"); - put("secomscrootca1 [jdk]", - "E7:5E:72:ED:9F:56:0E:EC:6E:B4:80:00:73:A4:3F:C3:AD:19:19:5A:39:22:82:01:78:95:97:4A:99:02:6B:6C"); put("secomscrootca2 [jdk]", "51:3B:2C:EC:B8:10:D4:CD:E5:DD:85:39:1A:DF:C6:C2:DD:60:D8:7B:B7:36:D2:B5:21:48:4A:A4:7A:0E:BE:F6"); put("swisssigngoldg2ca [jdk]", diff --git a/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java b/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java index 9c38465bedc..c0d3bbfda31 100644 --- a/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java @@ -23,18 +23,25 @@ /* * @test - * @bug 8164879 + * @bug 8164879 8300285 * @library ../../ * /test/lib * /javax/net/ssl/templates - * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property + * @summary Verify AEAD TLS cipher suite limits set in the jdk.tls.keyLimits + * property * start a new handshake sequence to renegotiate the symmetric key with an * SSLSocket connection. This test verifies the handshake method was called * via debugging info. It does not verify the renegotiation was successful * as that is very hard. * - * @run main SSLEngineKeyLimit 0 server AES/GCM/NoPadding keyupdate 1050000 - * @run main SSLEngineKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22 + * @run main SSLEngineKeyLimit 0 server TLS_AES_256_GCM_SHA384 + * AES/GCM/NoPadding keyupdate 1050000 + * @run main SSLEngineKeyLimit 1 client TLS_AES_256_GCM_SHA384 + * AES/GCM/NoPadding keyupdate 2^22 + * @run main SSLEngineKeyLimit 0 server TLS_CHACHA20_POLY1305_SHA256 + * AES/GCM/NoPadding keyupdate 1050000, ChaCha20-Poly1305 KeyUpdate 1050000 + * @run main SSLEngineKeyLimit 1 client TLS_CHACHA20_POLY1305_SHA256 + * AES/GCM/NoPadding keyupdate 2^22, ChaCha20-Poly1305 KeyUpdate 2^22 */ /* @@ -78,7 +85,7 @@ public class SSLEngineKeyLimit extends SSLContextTemplate { } /** - * args should have two values: server|client, + * args should have two values: server|client, cipher suite, * Prepending 'p' is for internal use only. */ public static void main(String args[]) throws Exception { @@ -97,7 +104,7 @@ public static void main(String args[]) throws Exception { File f = new File("keyusage."+ System.nanoTime()); PrintWriter p = new PrintWriter(f); p.write("jdk.tls.keyLimits="); - for (int i = 2; i < args.length; i++) { + for (int i = 3; i < args.length; i++) { p.write(" "+ args[i]); } p.close(); @@ -112,10 +119,13 @@ public static void main(String args[]) throws Exception { System.getProperty("test.java.opts")); ProcessBuilder pb = ProcessTools.createTestJvm( - Utils.addTestJavaOpts("SSLEngineKeyLimit", "p", args[1])); + Utils.addTestJavaOpts("SSLEngineKeyLimit", "p", args[1], + args[2])); OutputAnalyzer output = ProcessTools.executeProcess(pb); try { + output.shouldContain(String.format( + "\"cipher suite\" : \"%s", args[2])); if (expectedFail) { output.shouldNotContain("KeyUpdate: write key updated"); output.shouldNotContain("KeyUpdate: read key updated"); @@ -156,9 +166,10 @@ public static void main(String args[]) throws Exception { cTos.clear(); sToc.clear(); - Thread ts = new Thread(serverwrite ? new Client() : new Server()); + Thread ts = new Thread(serverwrite ? new Client() : + new Server(args[2])); ts.start(); - (serverwrite ? new Server() : new Client()).run(); + (serverwrite ? new Server(args[2]) : new Client()).run(); ts.interrupt(); ts.join(); } @@ -395,11 +406,14 @@ protected ContextParameters getServerContextParameters() { } static class Server extends SSLEngineKeyLimit implements Runnable { - Server() throws Exception { + Server(String cipherSuite) throws Exception { super(); eng = initContext().createSSLEngine(); eng.setUseClientMode(false); eng.setNeedClientAuth(true); + if (cipherSuite != null && cipherSuite.length() > 0) { + eng.setEnabledCipherSuites(new String[] { cipherSuite }); + } } public void run() { diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java index 198e0bdd6f0..8b19d39af7e 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -23,14 +23,24 @@ /* * @test - * @bug 8164879 + * @bug 8164879 8300285 * @library ../../ * @library /test/lib * @modules java.base/sun.security.util - * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property - * @run main SSLSocketKeyLimit 0 server AES/GCM/NoPadding keyupdate 1000000 - * @run main SSLSocketKeyLimit 0 client AES/GCM/NoPadding keyupdate 1000000 - * @run main SSLSocketKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22 + * @summary Verify AEAD TLS cipher suite limits set in the jdk.tls.keyLimits + * property + * @run main SSLSocketKeyLimit 0 server TLS_AES_256_GCM_SHA384 + * AES/GCM/NoPadding keyupdate 1000000 + * @run main SSLSocketKeyLimit 0 client TLS_AES_256_GCM_SHA384 + * AES/GCM/NoPadding keyupdate 1000000 + * @run main SSLSocketKeyLimit 1 client TLS_AES_256_GCM_SHA384 + * AES/GCM/NoPadding keyupdate 2^22 + * @run main SSLSocketKeyLimit 0 server TLS_CHACHA20_POLY1305_SHA256 + * AES/GCM/NoPadding keyupdate 1000000, ChaCha20-Poly1305 KeyUpdate 1000000 + * @run main SSLSocketKeyLimit 0 client TLS_CHACHA20_POLY1305_SHA256 + * AES/GCM/NoPadding keyupdate 1000000, ChaCha20-Poly1305 KeyUpdate 1000000 + * @run main SSLSocketKeyLimit 1 client TLS_CHACHA20_POLY1305_SHA256 + * AES/GCM/NoPadding keyupdate 2^22, ChaCha20-Poly1305 KeyUpdate 2^22 */ /** @@ -96,7 +106,7 @@ SSLContext initContext() throws Exception { } /** - * args should have two values: server|client, + * args should have three values: server|client, cipher suite, * Prepending 'p' is for internal use only. */ public static void main(String args[]) throws Exception { @@ -110,7 +120,7 @@ public static void main(String args[]) throws Exception { File f = new File("keyusage."+ System.nanoTime()); PrintWriter p = new PrintWriter(f); p.write("jdk.tls.keyLimits="); - for (int i = 2; i < args.length; i++) { + for (int i = 3; i < args.length; i++) { p.write(" "+ args[i]); } p.close(); @@ -125,10 +135,13 @@ public static void main(String args[]) throws Exception { System.getProperty("test.java.opts")); ProcessBuilder pb = ProcessTools.createTestJvm( - Utils.addTestJavaOpts("SSLSocketKeyLimit", "p", args[1])); + Utils.addTestJavaOpts("SSLSocketKeyLimit", "p", args[1], + args[2])); OutputAnalyzer output = ProcessTools.executeProcess(pb); try { + output.shouldContain(String.format( + "\"cipher suite\" : \"%s", args[2])); if (expectedFail) { output.shouldNotContain("KeyUpdate: write key updated"); output.shouldNotContain("KeyUpdate: read key updated"); @@ -150,7 +163,7 @@ public static void main(String args[]) throws Exception { return; } - if (args.length > 0 && args[0].compareToIgnoreCase("client") == 0) { + if (args.length > 0 && args[1].compareToIgnoreCase("client") == 0) { serverwrite = false; } @@ -162,7 +175,7 @@ public static void main(String args[]) throws Exception { System.setProperty("javax.net.ssl.keyStorePassword", passwd); Arrays.fill(data, (byte)0x0A); - Thread ts = new Thread(new Server()); + Thread ts = new Thread(new Server(args[2])); ts.start(); while (!serverReady) { @@ -200,7 +213,8 @@ void read(SSLSocket s) throws Exception { int len; byte i = 0; try { - System.out.println("Server: connected " + s.getSession().getCipherSuite()); + System.out.println("Server: connected " + + s.getSession().getCipherSuite()); in = s.getInputStream(); out = s.getOutputStream(); while (true) { @@ -212,7 +226,8 @@ void read(SSLSocket s) throws Exception { if (b == 0x0A || b == 0x0D) { continue; } - System.out.println("\nData invalid: " + HexPrinter.minimal().toString(buf)); + System.out.println("\nData invalid: " + + HexPrinter.minimal().toString(buf)); break; } @@ -237,11 +252,14 @@ void read(SSLSocket s) throws Exception { static class Server extends SSLSocketKeyLimit implements Runnable { private SSLServerSocketFactory ssf; private SSLServerSocket ss; - Server() { + Server(String cipherSuite) { super(); try { ssf = initContext().getServerSocketFactory(); ss = (SSLServerSocket) ssf.createServerSocket(serverPort); + if (cipherSuite != null && cipherSuite.length() > 0) { + ss.setEnabledCipherSuites(new String[] { cipherSuite }); + } serverPort = ss.getLocalPort(); } catch (Exception e) { System.out.println("server: " + e.getMessage()); diff --git a/test/jdk/sun/security/tools/keytool/ExportPrivateKeyNoPwd.java b/test/jdk/sun/security/tools/keytool/ExportPrivateKeyNoPwd.java deleted file mode 100644 index 799bf455b23..00000000000 --- a/test/jdk/sun/security/tools/keytool/ExportPrivateKeyNoPwd.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * 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.security.*; - -/* - * Export a private key from the named keychain entry without supplying a - * password. See JDK-8062264. - * - * NOTE: Keychain access controls must already have been lowered to permit - * the target entry to be accessed. - */ -public class ExportPrivateKeyNoPwd { - - public static final void main(String[] args) throws Exception { - - if (args.length != 1) { - throw new Exception( - "ExportPrivateKeyNoPwd: must supply name of a keystore entry"); - } - String alias = args[0]; - - KeyStore ks = KeyStore.getInstance("KeychainStore"); - System.out.println("ExportPrivateKeyNoPwd: loading keychains..."); - ks.load(null, null); - - System.out.println("ExportPrivateKeyNoPwd: exporting key..."); - Key key = ks.getKey(alias, null); - if (key instanceof PrivateKey) { - System.out.println("ExportPrivateKeyNoPwd: exported " + - key.getAlgorithm() + " private key from '" + alias + "'"); - } else { - throw new Exception("Error exporting private key from keychain"); - } - } -} - diff --git a/test/jdk/sun/security/tools/keytool/ListKeyChainStore.java b/test/jdk/sun/security/tools/keytool/ListKeyChainStore.java new file mode 100644 index 00000000000..39626b8dfc5 --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/ListKeyChainStore.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2012, 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. + * + * 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 7133495 8062264 8046777 8153005 + * @summary KeyChain KeyStore implementation retrieves only one private key entry + * @requires (os.family == "mac") + * @library /test/lib + * @run main/othervm/manual ListKeyChainStore + */ + +import jdk.test.lib.SecurityTools; +import jdk.test.lib.process.ProcessTools; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.Key; +import java.security.KeyStore; +import java.security.PrivateKey; + +public class ListKeyChainStore { + private static final String PWD = "xxxxxx"; + private static final String DEFAULT_KEYTOOL = "-list -storetype KeychainStore " + + "-keystore NONE -storepass " + PWD; + private static final String USER_DIR = System.getProperty("user.dir", "."); + private static final String FS = System.getProperty("file.separator"); + private static final String PKCS12_KEYSTORE = USER_DIR + FS + "7133495.p12"; + private static final String KEYCHAIN_FILE = USER_DIR + FS + "7133495.keychain"; + private static final String TEMPORARY_FILE = USER_DIR + FS + "7133495.tmp"; + private static final String USER_KEYCHAIN_LIST = USER_DIR + FS + "user.keychain.list"; + private static final String PRIVATE_KEY_ENTRY = "PrivateKeyEntry"; + + public static void main(String[] args) throws Throwable { + LOG_MSG("WARNING: This test doesn't work on macOS virtualized environment. " + + "`security list-keychains -s` doesn't update the search order."); + + deleteTestTempFilesIfExists(); + + // Get the old security keychain list to restore later + try (PrintStream printStream = new PrintStream(USER_KEYCHAIN_LIST)) { + ProcessTools.executeCommand("sh", "-c", "security list-keychains") + .shouldHaveExitValue(0).outputTo(printStream); + } + + try { + try (PrintStream printStream = new PrintStream(TEMPORARY_FILE)) { + SecurityTools.keytool(DEFAULT_KEYTOOL).shouldHaveExitValue(0) + .outputTo(printStream); + } + int oldPrivateKeyCount = countOccurrences(TEMPORARY_FILE, PRIVATE_KEY_ENTRY); + LOG_MSG("Found " + oldPrivateKeyCount + " private key entries in the " + + "Keychain keystore"); + + // Create the PKCS12 keystore containing 3 public/private key pairs + LOG_MSG("Creating PKCS12 keystore: " + PKCS12_KEYSTORE); + for (int i = 0; i < 3; i++) { + // Use legacy encryption and MAC algorithms, refer macOS open radar FB8988319 + // macOS security framework doesn't work with the latest algorithms + SecurityTools.keytool(String.format("-J-Dkeystore.pkcs12.legacy -genkeypair" + + " -storetype PKCS12 -keystore %s -storepass %s -keyalg rsa -dname " + + "CN=CN%d,OU=OU%d,O=O%d,ST=ST%d,C=US -alias 7133495-%d", + PKCS12_KEYSTORE, PWD, i, i, i, i, i)).shouldHaveExitValue(0); + } + + // Create the keychain + LOG_MSG("Creating keychain: " + KEYCHAIN_FILE); + ProcessTools.executeCommand("sh", "-c", String.format("security create-keychain" + + " -p %s %s", PWD, KEYCHAIN_FILE)).shouldHaveExitValue(0); + + // Unlock the keychain + LOG_MSG("Unlock keychain: " + KEYCHAIN_FILE); + ProcessTools.executeCommand("sh", "-c", String.format("security unlock-keychain" + + " -p %s %s", PWD, KEYCHAIN_FILE)).shouldHaveExitValue(0); + + // Import the key pairs from the PKCS12 keystore into the keychain + // The '-A' option is used to lower the keychain's access controls + LOG_MSG("Importing the key pairs from " + PKCS12_KEYSTORE + + " to " + KEYCHAIN_FILE); + ProcessTools.executeCommand("sh", "-c", String.format("security import %s -k %s" + + " -f pkcs12 -P %s -A", PKCS12_KEYSTORE, KEYCHAIN_FILE, PWD)).shouldHaveExitValue(0); + + // Generate a 2048-bit RSA keypair and import into the keychain + // Its private key is configured with non-default key usage settings + ProcessTools.executeCommand("sh", "-c", String.format("certtool ca k=%s " + + "< " + msg); + } +} diff --git a/test/jdk/sun/security/tools/keytool/ListKeychainStore.sh b/test/jdk/sun/security/tools/keytool/ListKeychainStore.sh deleted file mode 100644 index 6ab1be90065..00000000000 --- a/test/jdk/sun/security/tools/keytool/ListKeychainStore.sh +++ /dev/null @@ -1,188 +0,0 @@ -# -# Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. -# 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 7133495 8041740 8062264 8046777 -# @summary [macosx] KeyChain KeyStore implementation retrieves only one private key entry - -if [ "${TESTJAVA}" = "" ] ; then - JAVAC_CMD=`which javac` - TESTJAVA=`dirname $JAVAC_CMD`/.. -fi - -if [ "${TESTSRC}" = "" ] ; then - TESTSRC="." -fi -if [ "${TESTCLASSES}" = "" ] ; then - TESTCLASSES=`pwd` -fi - -# Only run on MacOS -OS=`uname -s` -case "$OS" in - Darwin ) - ;; - * ) - echo "Will not run test on: ${OS}" - exit 0; - ;; -esac - -PWD="xxxxxx" -KEYTOOL="${TESTJAVA}/bin/keytool ${TESTTOOLVMOPTS} -storetype KeychainStore -keystore NONE -storepass $PWD" -TEMPORARY_P12="$TESTCLASSES/7133495.p12" -TEMPORARY_KC="$TESTCLASSES/7133495.keychain" -TEMPORARY_LIST="$TESTCLASSES/7133495.tmp" -CLEANUP_P12="rm -f $TEMPORARY_P12" -CLEANUP_KC="security delete-keychain $TEMPORARY_KC" -CLEANUP_LIST="rm -f $TEMPORARY_LIST" - -# Count the number of private key entries in the Keychain keystores - -COUNT=`$KEYTOOL -list | grep PrivateKeyEntry | wc -l` -echo "Found $COUNT private key entries in the Keychain keystores" - -# Create a temporary PKCS12 keystore containing 3 public/private keypairs - -RESULT=`$CLEANUP_P12` - -for i in X Y Z -do - ${TESTJAVA}/bin/keytool ${TESTTOOLVMOPTS} -genkeypair \ - -storetype PKCS12 \ - -keystore $TEMPORARY_P12 \ - -storepass $PWD \ - -keyalg rsa \ - -dname "CN=$i,OU=$i,O=$i,ST=$i,C=US" \ - -alias 7133495-$i - - if [ $? -ne 0 ]; then - echo "Error: cannot create keypair $i in the temporary PKCS12 keystore" - RESULT=`$CLEANUP_P12` - exit 1 - fi -done -echo "Created a temporary PKCS12 keystore: $TEMPORARY_P12" - -# Create a temporary keychain - -security create-keychain -p $PWD $TEMPORARY_KC -if [ $? -ne 0 ]; then - echo "Error: cannot create the temporary keychain" - RESULT=`$CLEANUP_P12` - exit 2 -fi -echo "Created a temporary keychain: $TEMPORARY_KC" - -# Unlock the temporary keychain - -security unlock-keychain -p $PWD $TEMPORARY_KC -if [ $? -ne 0 ]; then - echo "Error: cannot unlock the temporary keychain" - RESULT=`$CLEANUP_P12` - RESULT=`$CLEANUP_KC` - exit 3 -fi -echo "Unlocked the temporary keychain" - -# Import the keypairs from the PKCS12 keystore into the keychain -# (The '-A' option is used to lower the temporary keychain's access controls) - -security import $TEMPORARY_P12 -k $TEMPORARY_KC -f pkcs12 -P $PWD -A -if [ $? -ne 0 ]; then - echo "Error: cannot import keypairs from PKCS12 keystore into the keychain" - RESULT=`$CLEANUP_P12` - RESULT=`$CLEANUP_KC` - exit 4 -fi -echo "Imported keypairs from PKCS12 keystore into the keychain" - -# Generate a 2048-bit RSA keypair and import into the temporary keychain -# (its private key is configured with non-default key usage settings) - -certtool c k=$TEMPORARY_KC < $TEMPORARY_LIST -security list-keychains >> $TEMPORARY_LIST -security list-keychains -s `xargs < ${TEMPORARY_LIST}` -`$CLEANUP_LIST` -echo "Temporary keychain search order:" -security list-keychains - -# Recount the number of private key entries in the Keychain keystores -# (3 private keys imported from PKCS12, 1 private key generated by 'certtool') - -RECOUNT=`$KEYTOOL -list | grep PrivateKeyEntry | wc -l` -echo "Found $RECOUNT private key entries in the Keychain keystore" -if [ $RECOUNT -lt `expr $COUNT + 4` ]; then - echo "Error: expected >$COUNT private key entries in the Keychain keystores" - RESULT=`$CLEANUP_P12` - RESULT=`$CLEANUP_KC` - exit 5 -fi - -# Export a private key from the keychain (without supplying a password) -# Access controls have already been lowered (see 'security import ... -A' above) - -${TESTJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/ExportPrivateKeyNoPwd.java || exit 6 -${TESTJAVA}/bin/java ${TESTVMOPTS} ExportPrivateKeyNoPwd x -if [ $? -ne 0 ]; then - echo "Error exporting private key from the temporary keychain" - RESULT=`$CLEANUP_P12` - RESULT=`$CLEANUP_KC` - exit 6 -fi -echo "Exported a private key from the temporary keychain" - -RESULT=`$CLEANUP_P12` -if [ $? -ne 0 ]; then - echo "Error: cannot remove the temporary PKCS12 keystore" - exit 7 -fi -echo "Removed the temporary PKCS12 keystore" - -RESULT=`$CLEANUP_KC` -if [ $? -ne 0 ]; then - echo "Error: cannot remove the temporary keychain" - exit 8 -fi -echo "Removed the temporary keychain" - -exit 0 diff --git a/test/langtools/tools/javac/T8312163.java b/test/langtools/tools/javac/T8312163.java new file mode 100644 index 00000000000..3554522f401 --- /dev/null +++ b/test/langtools/tools/javac/T8312163.java @@ -0,0 +1,57 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8312163 + * @summary Crash in dominance check when compiling unnamed patterns + * @enablePreview + * @compile/fail/ref=T8312163.out -XDrawDiagnostics T8312163.java + */ +public class T8312163 { + sealed interface A permits B {} + record B() implements A {} + + record Rec(A a, A b) {} + + public void test(Rec r) + { + switch (r) { + case Rec(_, _): break; + case Rec(_, B()): // dominated + } + + switch (r) { + case Rec(_, B()): break; + case Rec(_, _): + } + + switch (r) { + case Rec(_, _): break; + case Rec(_, A a): // dominated + } + + switch (r) { + case Rec(_, A a): break; + case Rec(_, _): // dominated + } + + // duplicated unnamed patterns with unnamed pattern variables for reference + switch (r) { + case Rec(var _, var _): break; + case Rec(var _, B()): // dominated + } + + switch (r) { + case Rec(var _, B()): break; + case Rec(var _, var _): + } + + switch (r) { + case Rec(var _, var _): break; + case Rec(var _, A a): // dominated + } + + switch (r) { + case Rec(var _, A a): break; + case Rec(var _, var _): // dominated + } + } +} diff --git a/test/langtools/tools/javac/T8312163.out b/test/langtools/tools/javac/T8312163.out new file mode 100644 index 00000000000..80ec36552c0 --- /dev/null +++ b/test/langtools/tools/javac/T8312163.out @@ -0,0 +1,9 @@ +T8312163.java:18:18: compiler.err.pattern.dominated +T8312163.java:28:18: compiler.err.pattern.dominated +T8312163.java:33:18: compiler.err.pattern.dominated +T8312163.java:39:18: compiler.err.pattern.dominated +T8312163.java:49:18: compiler.err.pattern.dominated +T8312163.java:54:18: compiler.err.pattern.dominated +- compiler.note.preview.filename: T8312163.java, DEFAULT +- compiler.note.preview.recompile +6 errors \ No newline at end of file diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 0b5f67e089f..1daad08f03c 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 7073631 7159445 7156633 8028235 8065753 8205418 8205913 8228451 8237041 8253584 8246774 8256411 8256149 8259050 8266436 8267221 8271928 8275097 8293897 8295401 8304671 + * @bug 7073631 7159445 7156633 8028235 8065753 8205418 8205913 8228451 8237041 8253584 8246774 8256411 8256149 8259050 8266436 8267221 8271928 8275097 8293897 8295401 8304671 8312093 * @summary tests error and diagnostics positions * @author Jan Lahoda * @modules jdk.compiler/com.sun.tools.javac.api @@ -2395,6 +2395,34 @@ public Void visitCase(CaseTree node, Void p) { codes); } + @Test //JDK-8312093 + void testJavadoc() throws IOException { + String code = """ + public class Test { + /***/ + void main() { + } + } + """; + DiagnosticCollector coll = + new DiagnosticCollector<>(); + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null, + null, Arrays.asList(new MyFileObject(code))); + Trees trees = Trees.instance(ct); + CompilationUnitTree cut = ct.parse().iterator().next(); + new TreePathScanner() { + @Override + public Void visitMethod(MethodTree node, Void p) { + if (!node.getName().contentEquals("main")) { + return null; + } + String comment = trees.getDocComment(getCurrentPath()); + assertEquals("Expecting empty comment", "", comment); + return null; + } + }.scan(cut, null); + } + void run(String[] args) throws Exception { int passed = 0, failed = 0; final Pattern p = (args != null && args.length > 0) diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index dca5864e1a9..dad1a0c86f8 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8262891 8268871 8274363 8281100 8294670 + * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 * @summary Check exhaustiveness of switches over sealed types. * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -1540,7 +1540,7 @@ int test(Object o) { "1 error"); } - private static final int NESTING_CONSTANT = 5; + private static final int NESTING_CONSTANT = 4; Set createDeeplyNestedVariants() { Set variants = new HashSet<>(); @@ -1948,6 +1948,54 @@ void test(I i) { """); } + @Test //JDK-8311038 + public void testReducesTooMuch(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + void test(Rec r) { + switch (r) { + case Rec(String s) -> + System.out.println("I2" + s.toString()); + case Rec(Object o) -> + System.out.println("I2"); + } + } + record Rec(Object o) {} + } + """); + } + + @Test //JDK-8311815: + public void testAmbiguousRecordUsage(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + record Pair(I i1, I i2) {} + sealed interface I {} + record C() implements I {} + record D() implements I {} + + void exhaustinvenessWithInterface(Pair pairI) { + switch (pairI) { + case Pair(D fst, C snd) -> { + } + case Pair(C fst, C snd) -> { + } + case Pair(C fst, I snd) -> { + } + case Pair(D fst, D snd) -> { + } + } + } + } + """); + } + private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException { doTest(base, libraryCode, testCode, false, expectedErrors); } diff --git a/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java index c050ed46d52..c24cb8bf95a 100644 --- a/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java +++ b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8300543 8309336 + * @bug 8300543 8309336 8311825 * @summary Check switches work correctly with qualified enum constants * @compile/fail/ref=EnumSwitchQualifiedErrors.out -XDrawDiagnostics EnumSwitchQualifiedErrors.java */ @@ -69,6 +69,30 @@ int testPatternMatchingSwitch5(Object e) { }; } + int testQualifiedDuplicate1(Object o) { + return switch(o) { + case E1.A -> 1; + case E1.A -> 2; + default -> -1; + }; + } + + int testQualifiedDuplicate2(E1 e) { + return switch(e) { + case A -> 1; + case E1.A -> 2; + default -> -1; + }; + } + + int testQualifiedDuplicate3(E1 e) { + return switch(e) { + case E1.A -> 1; + case A -> 2; + default -> -1; + }; + } + sealed interface I {} enum E1 implements I { A; } enum E2 { A; } diff --git a/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out index d9519d81e7e..b6a17f910e7 100644 --- a/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out +++ b/test/langtools/tools/javac/switchextra/EnumSwitchQualifiedErrors.out @@ -8,4 +8,7 @@ EnumSwitchQualifiedErrors.java:58:18: compiler.err.enum.label.must.be.enum.const EnumSwitchQualifiedErrors.java:65:18: compiler.err.pattern.or.enum.req EnumSwitchQualifiedErrors.java:66:18: compiler.err.pattern.or.enum.req EnumSwitchQualifiedErrors.java:67:18: compiler.err.pattern.expected -10 errors +EnumSwitchQualifiedErrors.java:75:18: compiler.err.duplicate.case.label +EnumSwitchQualifiedErrors.java:83:18: compiler.err.duplicate.case.label +EnumSwitchQualifiedErrors.java:91:18: compiler.err.duplicate.case.label +13 errors