mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-13 12:38:07 +00:00
Merge
This commit is contained in:
commit
2a6ec88cd0
@ -230,30 +230,41 @@ frame frame::sender_for_interpreter_frame(RegisterMap *map) const {
|
||||
return frame(sender_sp(), sender_pc(), (intptr_t*)(ijava_state()->sender_sp));
|
||||
}
|
||||
|
||||
intptr_t* frame::compiled_sender_sp(CodeBlob* cb) const {
|
||||
return sender_sp();
|
||||
}
|
||||
|
||||
address* frame::compiled_sender_pc_addr(CodeBlob* cb) const {
|
||||
return sender_pc_addr();
|
||||
}
|
||||
|
||||
void frame::patch_pc(Thread* thread, address pc) {
|
||||
assert(_cb == CodeCache::find_blob(pc), "unexpected pc");
|
||||
address* pc_addr = (address*)&(own_abi()->return_pc);
|
||||
|
||||
if (TracePcPatching) {
|
||||
tty->print_cr("patch_pc at address " PTR_FORMAT " [" PTR_FORMAT " -> " PTR_FORMAT "] ",
|
||||
p2i(&((address*) _sp)[-1]), p2i(((address*) _sp)[-1]), p2i(pc));
|
||||
}
|
||||
assert(!Continuation::is_return_barrier_entry(*pc_addr), "return barrier");
|
||||
assert(_pc == *pc_addr || pc == *pc_addr || 0 == *pc_addr,
|
||||
"must be (pc: " INTPTR_FORMAT " _pc: " INTPTR_FORMAT " pc_addr: " INTPTR_FORMAT
|
||||
" *pc_addr: " INTPTR_FORMAT " sp: " INTPTR_FORMAT ")",
|
||||
p2i(pc), p2i(_pc), p2i(pc_addr), p2i(*pc_addr), p2i(sp()));
|
||||
DEBUG_ONLY(address old_pc = _pc;)
|
||||
own_abi()->return_pc = (uint64_t)pc;
|
||||
_pc = pc; // must be set before call to get_deopt_original_pc
|
||||
address original_pc = CompiledMethod::get_deopt_original_pc(this);
|
||||
if (original_pc != NULL) {
|
||||
assert(original_pc == _pc, "expected original to be stored before patching");
|
||||
// assert(original_pc == _pc, "expected original to be stored before patching");
|
||||
_deopt_state = is_deoptimized;
|
||||
// Leave _pc as is.
|
||||
_pc = original_pc;
|
||||
} else {
|
||||
_deopt_state = not_deoptimized;
|
||||
_pc = pc;
|
||||
}
|
||||
assert(!is_compiled_frame() || !_cb->as_compiled_method()->is_deopt_entry(_pc), "must be");
|
||||
|
||||
#ifdef ASSERT
|
||||
{
|
||||
frame f(this->sp(), pc, this->unextended_sp());
|
||||
assert(f.is_deoptimized_frame() == this->is_deoptimized_frame() && f.pc() == this->pc() && f.raw_pc() == this->raw_pc(),
|
||||
"must be (f.is_deoptimized_frame(): %d this->is_deoptimized_frame(): %d "
|
||||
"f.pc(): " INTPTR_FORMAT " this->pc(): " INTPTR_FORMAT " f.raw_pc(): " INTPTR_FORMAT " this->raw_pc(): " INTPTR_FORMAT ")",
|
||||
f.is_deoptimized_frame(), this->is_deoptimized_frame(), p2i(f.pc()), p2i(this->pc()), p2i(f.raw_pc()), p2i(this->raw_pc()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
|
||||
|
||||
@ -460,15 +460,15 @@
|
||||
|
||||
private:
|
||||
|
||||
inline void find_codeblob_and_set_pc_and_deopt_state(address pc);
|
||||
// Initialize frame members (_pc and _sp must be given)
|
||||
inline void setup();
|
||||
const ImmutableOopMap* get_oop_map() const;
|
||||
|
||||
// Constructors
|
||||
|
||||
public:
|
||||
// To be used, if sp was not extended to match callee's calling convention.
|
||||
inline frame(intptr_t* sp, address pc);
|
||||
inline frame(intptr_t* sp, address pc, intptr_t* unextended_sp);
|
||||
inline frame(intptr_t* sp, address pc, intptr_t* unextended_sp = nullptr, intptr_t* fp = nullptr, CodeBlob* cb = nullptr);
|
||||
|
||||
// Access frame via stack pointer.
|
||||
inline intptr_t* sp_addr_at(int index) const { return &sp()[index]; }
|
||||
@ -479,10 +479,6 @@
|
||||
inline z_abi_160* callers_abi() const { return (z_abi_160*) fp(); }
|
||||
|
||||
private:
|
||||
|
||||
intptr_t* compiled_sender_sp(CodeBlob* cb) const;
|
||||
address* compiled_sender_pc_addr(CodeBlob* cb) const;
|
||||
|
||||
address* sender_pc_addr(void) const;
|
||||
|
||||
public:
|
||||
|
||||
@ -28,71 +28,71 @@
|
||||
|
||||
#include "code/codeCache.hpp"
|
||||
#include "code/vmreg.inline.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
|
||||
// Inline functions for z/Architecture frames:
|
||||
|
||||
inline void frame::find_codeblob_and_set_pc_and_deopt_state(address pc) {
|
||||
assert(pc != NULL, "precondition: must have PC");
|
||||
|
||||
_cb = CodeCache::find_blob(pc);
|
||||
_pc = pc; // Must be set for get_deopt_original_pc().
|
||||
|
||||
_fp = (intptr_t *) own_abi()->callers_sp;
|
||||
|
||||
address original_pc = CompiledMethod::get_deopt_original_pc(this);
|
||||
if (original_pc != NULL) {
|
||||
_pc = original_pc;
|
||||
_deopt_state = is_deoptimized;
|
||||
} else {
|
||||
_deopt_state = not_deoptimized;
|
||||
// Initialize frame members (_sp must be given)
|
||||
inline void frame::setup() {
|
||||
if (_pc == nullptr) {
|
||||
_pc = (address)own_abi()->return_pc;
|
||||
assert(_pc != nullptr, "must have PC");
|
||||
}
|
||||
|
||||
assert(((uint64_t)_sp & 0x7) == 0, "SP must be 8-byte aligned");
|
||||
if (_cb == nullptr) {
|
||||
_cb = CodeCache::find_blob(_pc);
|
||||
}
|
||||
|
||||
if (_fp == nullptr) {
|
||||
_fp = (intptr_t*)own_abi()->callers_sp;
|
||||
}
|
||||
|
||||
if (_unextended_sp == nullptr) {
|
||||
_unextended_sp = _sp;
|
||||
}
|
||||
|
||||
// When thawing continuation frames the _unextended_sp passed to the constructor is not aligend
|
||||
assert(_on_heap || (is_aligned(_sp, alignment_in_bytes) && is_aligned(_fp, alignment_in_bytes)),
|
||||
"invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp));
|
||||
|
||||
address original_pc = CompiledMethod::get_deopt_original_pc(this);
|
||||
if (original_pc != nullptr) {
|
||||
_pc = original_pc;
|
||||
_deopt_state = is_deoptimized;
|
||||
assert(_cb == nullptr || _cb->as_compiled_method()->insts_contains_inclusive(_pc),
|
||||
"original PC must be in the main code section of the the compiled method (or must be immediately following it)");
|
||||
} else {
|
||||
if (_cb == SharedRuntime::deopt_blob()) {
|
||||
_deopt_state = is_deoptimized;
|
||||
} else {
|
||||
_deopt_state = not_deoptimized;
|
||||
}
|
||||
}
|
||||
|
||||
// assert(_on_heap || is_aligned(_sp, frame::frame_alignment), "SP must be 8-byte aligned");
|
||||
}
|
||||
|
||||
// Constructors
|
||||
|
||||
// Initialize all fields, _unextended_sp will be adjusted in find_codeblob_and_set_pc_and_deopt_state.
|
||||
inline frame::frame() : _sp(NULL), _pc(NULL), _cb(NULL), _deopt_state(unknown), _on_heap(false),
|
||||
#ifdef ASSERT
|
||||
_frame_index(-1),
|
||||
#endif
|
||||
_unextended_sp(NULL), _fp(NULL) {}
|
||||
// Initialize all fields
|
||||
inline frame::frame() : _sp(nullptr), _pc(nullptr), _cb(nullptr), _oop_map(nullptr), _deopt_state(unknown),
|
||||
_on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(nullptr), _fp(nullptr) {}
|
||||
|
||||
inline frame::frame(intptr_t* sp) : _sp(sp), _on_heap(false),
|
||||
#ifdef ASSERT
|
||||
_frame_index(-1),
|
||||
#endif
|
||||
_unextended_sp(sp) {
|
||||
find_codeblob_and_set_pc_and_deopt_state((address)own_abi()->return_pc);
|
||||
inline frame::frame(intptr_t* sp, address pc, intptr_t* unextended_sp, intptr_t* fp, CodeBlob* cb)
|
||||
: _sp(sp), _pc(pc), _cb(cb), _oop_map(nullptr),
|
||||
_on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) {
|
||||
setup();
|
||||
}
|
||||
|
||||
inline frame::frame(intptr_t* sp, address pc) : _sp(sp), _on_heap(false),
|
||||
#ifdef ASSERT
|
||||
_frame_index(-1),
|
||||
#endif
|
||||
_unextended_sp(sp) {
|
||||
find_codeblob_and_set_pc_and_deopt_state(pc); // Also sets _fp and adjusts _unextended_sp.
|
||||
}
|
||||
|
||||
inline frame::frame(intptr_t* sp, address pc, intptr_t* unextended_sp) : _sp(sp), _on_heap(false),
|
||||
#ifdef ASSERT
|
||||
_frame_index(-1),
|
||||
#endif
|
||||
_unextended_sp(unextended_sp) {
|
||||
find_codeblob_and_set_pc_and_deopt_state(pc); // Also sets _fp and adjusts _unextended_sp.
|
||||
}
|
||||
inline frame::frame(intptr_t* sp) : frame(sp, nullptr) {}
|
||||
|
||||
// Generic constructor. Used by pns() in debug.cpp only
|
||||
#ifndef PRODUCT
|
||||
inline frame::frame(void* sp, void* pc, void* unextended_sp) :
|
||||
_sp((intptr_t*)sp), _pc(NULL), _cb(NULL), _on_heap(false),
|
||||
#ifdef ASSERT
|
||||
_frame_index(-1),
|
||||
#endif
|
||||
_unextended_sp((intptr_t*)unextended_sp) {
|
||||
find_codeblob_and_set_pc_and_deopt_state((address)pc); // Also sets _fp and adjusts _unextended_sp.
|
||||
inline frame::frame(void* sp, void* pc, void* unextended_sp)
|
||||
: _sp((intptr_t*)sp), _pc((address)pc), _cb(nullptr), _oop_map(nullptr),
|
||||
_on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp((intptr_t*)unextended_sp) {
|
||||
setup();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -304,7 +304,16 @@ inline intptr_t* frame::real_fp() const {
|
||||
}
|
||||
|
||||
inline const ImmutableOopMap* frame::get_oop_map() const {
|
||||
Unimplemented();
|
||||
if (_cb == NULL) return NULL;
|
||||
if (_cb->oop_maps() != NULL) {
|
||||
NativePostCallNop* nop = nativePostCallNop_at(_pc);
|
||||
if (nop != NULL && nop->displacement() != 0) {
|
||||
int slot = ((nop->displacement() >> 24) & 0xff);
|
||||
return _cb->oop_map_for_slot(slot, _pc);
|
||||
}
|
||||
const ImmutableOopMap* oop_map = OopMapSet::find_map(this);
|
||||
return oop_map;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -355,7 +364,7 @@ inline frame frame::sender(RegisterMap* map) const {
|
||||
return sender_for_interpreter_frame(map);
|
||||
}
|
||||
assert(_cb == CodeCache::find_blob(pc()),"Must be the same");
|
||||
if (_cb != NULL) return sender_for_compiled_frame(map);
|
||||
if (_cb != nullptr) return sender_for_compiled_frame(map);
|
||||
|
||||
// Must be native-compiled frame, i.e. the marshaling code for native
|
||||
// methods that exists in the core system.
|
||||
@ -363,24 +372,21 @@ inline frame frame::sender(RegisterMap* map) const {
|
||||
}
|
||||
|
||||
inline frame frame::sender_for_compiled_frame(RegisterMap *map) const {
|
||||
assert(map != NULL, "map must be set");
|
||||
// Frame owned by compiler.
|
||||
assert(map != nullptr, "map must be set");
|
||||
|
||||
address pc = *compiled_sender_pc_addr(_cb);
|
||||
frame caller(compiled_sender_sp(_cb), pc);
|
||||
intptr_t* sender_sp = this->sender_sp();
|
||||
address sender_pc = this->sender_pc();
|
||||
|
||||
// Now adjust the map.
|
||||
|
||||
// Get the rest.
|
||||
if (map->update_map()) {
|
||||
// Tell GC to use argument oopmaps for some runtime stubs that need it.
|
||||
map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread()));
|
||||
if (_cb->oop_maps() != NULL) {
|
||||
if (_cb->oop_maps() != nullptr) {
|
||||
OopMapSet::update_register_map(this, map);
|
||||
}
|
||||
}
|
||||
|
||||
return caller;
|
||||
return frame(sender_sp, sender_pc);
|
||||
}
|
||||
|
||||
template <typename RegisterMapT>
|
||||
|
||||
@ -657,13 +657,13 @@ class NativeGeneralJump: public NativeInstruction {
|
||||
class NativePostCallNop: public NativeInstruction {
|
||||
public:
|
||||
bool check() const { Unimplemented(); return false; }
|
||||
int displacement() const { Unimplemented(); return 0; }
|
||||
int displacement() const { return 0; }
|
||||
void patch(jint diff) { Unimplemented(); }
|
||||
void make_deopt() { Unimplemented(); }
|
||||
};
|
||||
|
||||
inline NativePostCallNop* nativePostCallNop_at(address address) {
|
||||
Unimplemented();
|
||||
// Unimplemented();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -675,7 +675,7 @@ public:
|
||||
void verify() { Unimplemented(); }
|
||||
|
||||
static bool is_deopt_at(address instr) {
|
||||
Unimplemented();
|
||||
// Unimplemented();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -2857,6 +2857,44 @@ class StubGenerator: public StubCodeGenerator {
|
||||
return start;
|
||||
}
|
||||
|
||||
RuntimeStub* generate_cont_doYield() {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
address generate_cont_thaw(bool return_barrier, bool exception) {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
address generate_cont_thaw() {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
address generate_cont_returnBarrier() {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
address generate_cont_returnBarrier_exception() {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if INCLUDE_JFR
|
||||
RuntimeStub* generate_jfr_write_checkpoint() {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
#endif // INCLUD_JFR
|
||||
|
||||
void generate_initial() {
|
||||
// Generates all stubs and initializes the entry points.
|
||||
|
||||
@ -2895,6 +2933,20 @@ class StubGenerator: public StubCodeGenerator {
|
||||
StubRoutines::zarch::_trot_table_addr = (address)StubRoutines::zarch::_trot_table;
|
||||
}
|
||||
|
||||
void generate_phase1() {
|
||||
if (!Continuations::enabled()) return;
|
||||
|
||||
// Continuation stubs:
|
||||
StubRoutines::_cont_thaw = generate_cont_thaw();
|
||||
StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier();
|
||||
StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception();
|
||||
StubRoutines::_cont_doYield_stub = generate_cont_doYield();
|
||||
StubRoutines::_cont_doYield = StubRoutines::_cont_doYield_stub == nullptr ? nullptr
|
||||
: StubRoutines::_cont_doYield_stub->entry_point();
|
||||
|
||||
JFR_ONLY(StubRoutines::_jfr_write_checkpoint_stub = generate_jfr_write_checkpoint();)
|
||||
JFR_ONLY(StubRoutines::_jfr_write_checkpoint = StubRoutines::_jfr_write_checkpoint_stub->entry_point();)
|
||||
}
|
||||
|
||||
void generate_all() {
|
||||
// Generates all stubs and initializes the entry points.
|
||||
@ -2971,12 +3023,14 @@ class StubGenerator: public StubCodeGenerator {
|
||||
}
|
||||
|
||||
public:
|
||||
StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) {
|
||||
_stub_count = !all ? 0x100 : 0x200;
|
||||
if (all) {
|
||||
generate_all();
|
||||
} else {
|
||||
StubGenerator(CodeBuffer* code, int phase) : StubCodeGenerator(code) {
|
||||
_stub_count = (phase == 0) ? 0x100 : 0x200;
|
||||
if (phase == 0) {
|
||||
generate_initial();
|
||||
} else if (phase == 1) {
|
||||
generate_phase1(); // stubs that must be available for the interpreter
|
||||
} else {
|
||||
generate_all();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -483,6 +483,7 @@ address TemplateInterpreterGenerator::generate_abstract_entry(void) {
|
||||
}
|
||||
|
||||
address TemplateInterpreterGenerator::generate_Continuation_doYield_entry(void) {
|
||||
if (!Continuations::enabled()) return nullptr;
|
||||
Unimplemented();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -165,7 +165,6 @@ Node* compress_expand_identity(PhaseGVN* phase, Node* n) {
|
||||
if(phase->type(n->in(2))->higher_equal(TypeInteger::zero(bt))) return n->in(2);
|
||||
// compress(x, -1) == x, expand(x, -1) == x
|
||||
if(phase->type(n->in(2))->higher_equal(TypeInteger::minus_1(bt))) return n->in(1);
|
||||
return n;
|
||||
// expand(-1, x) == x
|
||||
if(n->Opcode() == Op_ExpandBits &&
|
||||
phase->type(n->in(1))->higher_equal(TypeInteger::minus_1(bt))) return n->in(2);
|
||||
@ -259,7 +258,9 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg
|
||||
} else {
|
||||
// Case 3) Mask value range only includes +ve values.
|
||||
assert(mask_type->lo_as_long() >= 0, "");
|
||||
mask_max_bw = max_bw - count_leading_zeros(mask_type->hi_as_long());
|
||||
jlong clz = count_leading_zeros(mask_type->hi_as_long());
|
||||
clz = bt == T_INT ? clz - 32 : clz;
|
||||
mask_max_bw = max_bw - clz;
|
||||
}
|
||||
if ( opc == Op_CompressBits) {
|
||||
lo = mask_max_bw == max_bw ? lo : 0L;
|
||||
@ -270,6 +271,8 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg
|
||||
} else {
|
||||
assert(opc == Op_ExpandBits, "");
|
||||
jlong max_mask = mask_type->hi_as_long();
|
||||
// Since mask here a range and not a constant value, hence being
|
||||
// conservative in determining the value range of result.
|
||||
lo = mask_type->lo_as_long() >= 0L ? 0L : lo;
|
||||
hi = mask_type->lo_as_long() >= 0L ? max_mask : hi;
|
||||
}
|
||||
|
||||
@ -12892,15 +12892,21 @@ myInit() {
|
||||
id="ThreadStart" const="JVMTI_EVENT_THREAD_START" num="52" phase="start">
|
||||
<description>
|
||||
A thread start event is generated by a new thread before its initial
|
||||
method executes. The capability
|
||||
<internallink id="jvmtiCapabilities.can_support_virtual_threads">
|
||||
<code>can_support_virtual_threads</code></internallink> determines
|
||||
if a new virtual thread generates a <code>ThreadStart</code> event or
|
||||
a <eventlink id="VirtualThreadStart"/> event. If disabled, a virtual
|
||||
thread generates a <code>ThreadStart</code> event. If enabled, a virtual
|
||||
thread generates a <code>VirtualThreadStart</code> event.
|
||||
method executes.
|
||||
<p/>
|
||||
A thread may be listed in the array returned by
|
||||
This event is generated by platform threads. It is also generated by
|
||||
virtual threads when the capability
|
||||
<internallink id="jvmtiCapabilities.can_support_virtual_threads">
|
||||
<code>can_support_virtual_threads</code></internallink> is not enabled.
|
||||
Agents without support for virtual threads that enable this event will
|
||||
therefore be notified by all newly started threads.
|
||||
<p/>
|
||||
If the capability <code>can_support_virtual_threads</code> is enabled then
|
||||
this event is not generated by virtual threads. Agents with support for
|
||||
virtual threads can enable <eventlink id="VirtualThreadStart"></eventlink>
|
||||
to be notified by newly started virtual threads.
|
||||
<p/>
|
||||
A platform thread may be listed in the array returned by
|
||||
<functionlink id="GetAllThreads"></functionlink>
|
||||
before its thread start event is generated.
|
||||
It is possible for other events to be generated
|
||||
@ -12934,21 +12940,26 @@ myInit() {
|
||||
<description>
|
||||
A thread end event is generated by a terminating thread after its
|
||||
initial method has finished execution.
|
||||
The capability
|
||||
<internallink id="jvmtiCapabilities.can_support_virtual_threads">
|
||||
<code>can_support_virtual_threads</code></internallink> determines
|
||||
if a terminating virtual thread generates a <code>ThreadEnd</code>
|
||||
event or a <eventlink id="VirtualThreadEnd"/> event. If disabled, a
|
||||
virtual thread generates a <code>ThreadEnd</code> event. If enabled,
|
||||
a virtual thread generates a <code>VirtualThreadEnd</code> event.
|
||||
<p/>
|
||||
A thread may be listed in the array returned by
|
||||
This event is generated by platform threads. It is also generated by
|
||||
virtual threads when the capability
|
||||
<internallink id="jvmtiCapabilities.can_support_virtual_threads">
|
||||
<code>can_support_virtual_threads</code></internallink> is not enabled.
|
||||
Agents without support for virtual threads that enable this event for
|
||||
all threads will therefore be notified by all terminating threads.
|
||||
<p/>
|
||||
If the capability <code>can_support_virtual_threads</code> is enabled then
|
||||
this event is not generated by virtual threads. Agents with support for
|
||||
virtual threads can enable <eventlink id="VirtualThreadEnd"></eventlink>
|
||||
to be notified by terminating virtual threads.
|
||||
<p/>
|
||||
A platform thread may be listed in the array returned by
|
||||
<functionlink id="GetAllThreads"></functionlink>
|
||||
after its thread end event is generated.
|
||||
No events are generated on a thread
|
||||
after its thread end event.
|
||||
<p/>
|
||||
The event is sent on the dying <paramlink id="thread"></paramlink>.
|
||||
The event is sent on the terminating <paramlink id="thread"></paramlink>.
|
||||
</description>
|
||||
<origin>jvmdi</origin>
|
||||
<capabilities>
|
||||
@ -12980,7 +12991,7 @@ myInit() {
|
||||
<p/>
|
||||
A virtual thread start event is generated before its initial method executes.
|
||||
<p/>
|
||||
The event is sent on the <paramlink id="virtual_thread"></paramlink>.
|
||||
The event is sent on the newly started <paramlink id="virtual_thread"></paramlink>.
|
||||
</description>
|
||||
<origin>new</origin>
|
||||
<capabilities>
|
||||
@ -13013,7 +13024,7 @@ myInit() {
|
||||
<p/>
|
||||
A virtual thread end event is generated after its initial method has finished execution.
|
||||
<p/>
|
||||
The event is sent on the <paramlink id="virtual_thread"></paramlink>.
|
||||
The event is sent on the terminating <paramlink id="virtual_thread"></paramlink>.
|
||||
</description>
|
||||
<origin>new</origin>
|
||||
<capabilities>
|
||||
|
||||
@ -115,6 +115,7 @@ enum {
|
||||
JVMTI_VERSION_1_2 = 0x30010200,
|
||||
JVMTI_VERSION_9 = 0x30090000,
|
||||
JVMTI_VERSION_11 = 0x300B0000,
|
||||
JVMTI_VERSION_19 = 0x30130000,
|
||||
|
||||
JVMTI_VERSION = 0x30000000 + (</xsl:text>
|
||||
<xsl:value-of select="$majorversion"/>
|
||||
|
||||
@ -160,6 +160,10 @@ void JavaThread::set_threadOopHandles(oop p) {
|
||||
}
|
||||
|
||||
oop JavaThread::threadObj() const {
|
||||
Thread* current = Thread::current_or_null_safe();
|
||||
assert(current != nullptr, "cannot be called by a detached thread");
|
||||
guarantee(current != this || JavaThread::cast(current)->is_oop_safe(),
|
||||
"current cannot touch oops after its GC barrier is detached.");
|
||||
return _threadObj.resolve();
|
||||
}
|
||||
|
||||
@ -1510,9 +1514,15 @@ void JavaThread::print_name_on_error(outputStream* st, char *buf, int buflen) co
|
||||
// JavaThread::print() is that we can't grab lock or allocate memory.
|
||||
void JavaThread::print_on_error(outputStream* st, char *buf, int buflen) const {
|
||||
st->print("%s \"%s\"", type_name(), get_thread_name_string(buf, buflen));
|
||||
oop thread_obj = threadObj();
|
||||
if (thread_obj != NULL) {
|
||||
if (java_lang_Thread::is_daemon(thread_obj)) st->print(" daemon");
|
||||
Thread* current = Thread::current_or_null_safe();
|
||||
assert(current != nullptr, "cannot be called by a detached thread");
|
||||
if (!current->is_Java_thread() || JavaThread::cast(current)->is_oop_safe()) {
|
||||
// Only access threadObj() if current thread is not a JavaThread
|
||||
// or if it is a JavaThread that can safely access oops.
|
||||
oop thread_obj = threadObj();
|
||||
if (thread_obj != nullptr) {
|
||||
if (java_lang_Thread::is_daemon(thread_obj)) st->print(" daemon");
|
||||
}
|
||||
}
|
||||
st->print(" [");
|
||||
st->print("%s", _get_thread_state_name(_thread_state));
|
||||
@ -1571,23 +1581,43 @@ const char* JavaThread::name() const {
|
||||
// descriptive string if there is no set name.
|
||||
const char* JavaThread::get_thread_name_string(char* buf, int buflen) const {
|
||||
const char* name_str;
|
||||
oop thread_obj = threadObj();
|
||||
if (thread_obj != NULL) {
|
||||
oop name = java_lang_Thread::name(thread_obj);
|
||||
if (name != NULL) {
|
||||
if (buf == NULL) {
|
||||
name_str = java_lang_String::as_utf8_string(name);
|
||||
#ifdef ASSERT
|
||||
Thread* current = Thread::current_or_null_safe();
|
||||
assert(current != nullptr, "cannot be called by a detached thread");
|
||||
if (!current->is_Java_thread() || JavaThread::cast(current)->is_oop_safe()) {
|
||||
// Only access threadObj() if current thread is not a JavaThread
|
||||
// or if it is a JavaThread that can safely access oops.
|
||||
#endif
|
||||
oop thread_obj = threadObj();
|
||||
if (thread_obj != NULL) {
|
||||
oop name = java_lang_Thread::name(thread_obj);
|
||||
if (name != NULL) {
|
||||
if (buf == NULL) {
|
||||
name_str = java_lang_String::as_utf8_string(name);
|
||||
} else {
|
||||
name_str = java_lang_String::as_utf8_string(name, buf, buflen);
|
||||
}
|
||||
} else if (is_attaching_via_jni()) { // workaround for 6412693 - see 6404306
|
||||
name_str = "<no-name - thread is attaching>";
|
||||
} else {
|
||||
name_str = java_lang_String::as_utf8_string(name, buf, buflen);
|
||||
name_str = "<un-named>";
|
||||
}
|
||||
} else if (is_attaching_via_jni()) { // workaround for 6412693 - see 6404306
|
||||
name_str = "<no-name - thread is attaching>";
|
||||
} else {
|
||||
name_str = "<un-named>";
|
||||
name_str = Thread::name();
|
||||
}
|
||||
#ifdef ASSERT
|
||||
} else {
|
||||
name_str = Thread::name();
|
||||
// Current JavaThread has exited...
|
||||
if (current == this) {
|
||||
// ... and is asking about itself:
|
||||
name_str = "<no-name - current JavaThread has exited>";
|
||||
} else {
|
||||
// ... and it can't safely determine this JavaThread's name so
|
||||
// use the default thread name.
|
||||
name_str = Thread::name();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
assert(name_str != NULL, "unexpected NULL thread name");
|
||||
return name_str;
|
||||
}
|
||||
|
||||
@ -997,9 +997,6 @@ JRT_END
|
||||
|
||||
jlong SharedRuntime::get_java_tid(Thread* thread) {
|
||||
if (thread != NULL && thread->is_Java_thread()) {
|
||||
Thread* current = Thread::current();
|
||||
guarantee(current != thread || JavaThread::cast(thread)->is_oop_safe(),
|
||||
"current cannot touch oops after its GC barrier is detached.");
|
||||
oop obj = JavaThread::cast(thread)->threadObj();
|
||||
return (obj == NULL) ? 0 : java_lang_Thread::thread_id(obj);
|
||||
}
|
||||
|
||||
@ -235,14 +235,14 @@ void Fingerprinter::do_type_calling_convention(BasicType type) {
|
||||
case T_BYTE:
|
||||
case T_SHORT:
|
||||
case T_INT:
|
||||
#if defined(PPC64)
|
||||
#if defined(PPC64) || defined(S390)
|
||||
if (_int_args < Argument::n_int_register_parameters_j) {
|
||||
_int_args++;
|
||||
} else {
|
||||
_stack_arg_slots += 1;
|
||||
}
|
||||
break;
|
||||
#endif // defined(PPC64)
|
||||
#endif // defined(PPC64) || defined(S390)
|
||||
case T_LONG:
|
||||
case T_OBJECT:
|
||||
case T_ARRAY:
|
||||
@ -251,23 +251,25 @@ void Fingerprinter::do_type_calling_convention(BasicType type) {
|
||||
_int_args++;
|
||||
} else {
|
||||
PPC64_ONLY(_stack_arg_slots = align_up(_stack_arg_slots, 2));
|
||||
S390_ONLY(_stack_arg_slots = align_up(_stack_arg_slots, 2));
|
||||
_stack_arg_slots += 2;
|
||||
}
|
||||
break;
|
||||
case T_FLOAT:
|
||||
#if defined(PPC64)
|
||||
#if defined(PPC64) || defined(S390)
|
||||
if (_fp_args < Argument::n_float_register_parameters_j) {
|
||||
_fp_args++;
|
||||
} else {
|
||||
_stack_arg_slots += 1;
|
||||
}
|
||||
break;
|
||||
#endif // defined(PPC64)
|
||||
#endif // defined(PPC64) || defined(S390)
|
||||
case T_DOUBLE:
|
||||
if (_fp_args < Argument::n_float_register_parameters_j) {
|
||||
_fp_args++;
|
||||
} else {
|
||||
PPC64_ONLY(_stack_arg_slots = align_up(_stack_arg_slots, 2));
|
||||
S390_ONLY(_stack_arg_slots = align_up(_stack_arg_slots, 2));
|
||||
_stack_arg_slots += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -504,7 +504,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
excName = htmlWriter.getLink(new HtmlLinkInfo(configuration, HtmlLinkInfo.Kind.MEMBER,
|
||||
substituteType));
|
||||
} else if (exception == null) {
|
||||
excName = new RawHtml(ch.getExceptionName(throwsTag).toString());
|
||||
excName = new RawHtml(throwsTag.getExceptionName().toString());
|
||||
} else if (exception.asType() == null) {
|
||||
excName = new RawHtml(utils.getFullyQualifiedName(exception));
|
||||
} else {
|
||||
|
||||
@ -48,9 +48,7 @@ import com.sun.source.doctree.ThrowsTree;
|
||||
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
import jdk.javadoc.internal.doclets.toolkit.Content;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* A taglet that processes {@link ThrowsTree}, which represents
|
||||
@ -64,14 +62,14 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
|
||||
@Override
|
||||
public void inherit(DocFinder.Input input, DocFinder.Output output) {
|
||||
Utils utils = input.utils;
|
||||
var utils = input.utils;
|
||||
Element target;
|
||||
CommentHelper ch = utils.getCommentHelper(input.element);
|
||||
var ch = utils.getCommentHelper(input.element);
|
||||
if (input.tagId == null) {
|
||||
var tag = (ThrowsTree) input.docTreeInfo.docTree();
|
||||
target = ch.getException(tag);
|
||||
input.tagId = target == null
|
||||
? ch.getExceptionName(tag).getSignature()
|
||||
? tag.getExceptionName().getSignature()
|
||||
: utils.getFullyQualifiedName(target);
|
||||
} else {
|
||||
target = input.utils.findClass(input.element, input.tagId);
|
||||
@ -97,20 +95,20 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
|
||||
@Override
|
||||
public Content getAllBlockTagOutput(Element holder, TagletWriter writer) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
var utils = writer.configuration().utils;
|
||||
var executable = (ExecutableElement) holder;
|
||||
ExecutableType instantiatedType = utils.asInstantiatedMethodType(
|
||||
writer.getCurrentPageElement(), executable);
|
||||
List<? extends TypeMirror> thrownTypes = instantiatedType.getThrownTypes();
|
||||
Map<String, TypeMirror> typeSubstitutions = getSubstitutedThrownTypes(
|
||||
writer.configuration().utils.typeUtils,
|
||||
utils.typeUtils,
|
||||
executable.getThrownTypes(),
|
||||
thrownTypes);
|
||||
Map<List<ThrowsTree>, ExecutableElement> tagsMap = new LinkedHashMap<>();
|
||||
tagsMap.put(utils.getThrowsTrees(executable), executable);
|
||||
Map<ThrowsTree, ExecutableElement> tagsMap = new LinkedHashMap<>();
|
||||
utils.getThrowsTrees(executable).forEach(t -> tagsMap.put(t, executable));
|
||||
Content result = writer.getOutputInstance();
|
||||
Set<String> alreadyDocumented = new HashSet<>();
|
||||
result.add(throwsTagsOutput(tagsMap, writer, alreadyDocumented, typeSubstitutions, true));
|
||||
result.add(throwsTagsOutput(tagsMap, alreadyDocumented, typeSubstitutions, writer));
|
||||
result.add(inheritThrowsDocumentation(executable, thrownTypes, alreadyDocumented, typeSubstitutions, writer));
|
||||
result.add(linkToUndocumentedDeclaredExceptions(thrownTypes, alreadyDocumented, writer));
|
||||
return result;
|
||||
@ -145,45 +143,48 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
/**
|
||||
* Returns the generated content for a collection of {@code @throws} tags.
|
||||
*
|
||||
* @param throwsTags the collection of tags to be converted
|
||||
* @param throwsTags the tags to be converted; each tag is mapped to
|
||||
* a method it appears on
|
||||
* @param alreadyDocumented the set of exceptions that have already been
|
||||
* documented and thus must not be documented by
|
||||
* this method. All exceptions documented by this
|
||||
* method will be added to this set upon the
|
||||
* method's return.
|
||||
* @param writer the taglet-writer used by the doclet
|
||||
* @param alreadyDocumented the set of exceptions that have already been documented
|
||||
* @param allowDuplicates {@code true} if we allow duplicate tags to be documented
|
||||
* @return the generated content for the tags
|
||||
*/
|
||||
protected Content throwsTagsOutput(Map<List<ThrowsTree>, ExecutableElement> throwsTags,
|
||||
TagletWriter writer,
|
||||
Set<String> alreadyDocumented,
|
||||
Map<String, TypeMirror> typeSubstitutions,
|
||||
boolean allowDuplicates) {
|
||||
Utils utils = writer.configuration().utils;
|
||||
private Content throwsTagsOutput(Map<ThrowsTree, ExecutableElement> throwsTags,
|
||||
Set<String> alreadyDocumented,
|
||||
Map<String, TypeMirror> typeSubstitutions,
|
||||
TagletWriter writer) {
|
||||
var utils = writer.configuration().utils;
|
||||
Content result = writer.getOutputInstance();
|
||||
for (Entry<List<ThrowsTree>, ExecutableElement> entry : throwsTags.entrySet()) {
|
||||
var documentedInThisCall = new HashSet<String>();
|
||||
for (Entry<ThrowsTree, ExecutableElement> entry : throwsTags.entrySet()) {
|
||||
Element e = entry.getValue();
|
||||
CommentHelper ch = utils.getCommentHelper(e);
|
||||
for (ThrowsTree tag : entry.getKey()) {
|
||||
Element te = ch.getException(tag);
|
||||
String excName = ch.getExceptionName(tag).toString();
|
||||
TypeMirror substituteType = typeSubstitutions.get(excName);
|
||||
if ((!allowDuplicates) &&
|
||||
(alreadyDocumented.contains(excName) ||
|
||||
(te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))) ||
|
||||
(substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
|
||||
continue;
|
||||
}
|
||||
if (alreadyDocumented.isEmpty()) {
|
||||
result.add(writer.getThrowsHeader());
|
||||
}
|
||||
result.add(writer.throwsTagOutput(e, tag, substituteType));
|
||||
if (substituteType != null) {
|
||||
alreadyDocumented.add(substituteType.toString());
|
||||
} else {
|
||||
alreadyDocumented.add(te != null
|
||||
? utils.getFullyQualifiedName(te, false)
|
||||
: excName);
|
||||
}
|
||||
var ch = utils.getCommentHelper(e);
|
||||
ThrowsTree tag = entry.getKey();
|
||||
Element te = ch.getException(tag);
|
||||
String excName = tag.getExceptionName().toString();
|
||||
TypeMirror substituteType = typeSubstitutions.get(excName);
|
||||
if (alreadyDocumented.contains(excName)
|
||||
|| (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))
|
||||
|| (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
|
||||
continue;
|
||||
}
|
||||
if (alreadyDocumented.isEmpty() && documentedInThisCall.isEmpty()) {
|
||||
result.add(writer.getThrowsHeader());
|
||||
}
|
||||
result.add(writer.throwsTagOutput(e, tag, substituteType));
|
||||
if (substituteType != null) {
|
||||
documentedInThisCall.add(substituteType.toString());
|
||||
} else {
|
||||
documentedInThisCall.add(te != null
|
||||
? utils.getFullyQualifiedName(te, false)
|
||||
: excName);
|
||||
}
|
||||
}
|
||||
alreadyDocumented.addAll(documentedInThisCall);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -205,8 +206,8 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
assert holder.getKind() == ElementKind.CONSTRUCTOR : holder.getKind();
|
||||
return result;
|
||||
}
|
||||
Utils utils = writer.configuration().utils;
|
||||
Map<List<ThrowsTree>, ExecutableElement> declaredExceptionTags = new LinkedHashMap<>();
|
||||
var utils = writer.configuration().utils;
|
||||
Map<ThrowsTree, ExecutableElement> declaredExceptionTags = new LinkedHashMap<>();
|
||||
for (TypeMirror declaredExceptionType : declaredExceptionTypes) {
|
||||
var input = new DocFinder.Input(utils, holder, this,
|
||||
utils.getTypeName(declaredExceptionType, false));
|
||||
@ -220,14 +221,12 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
if (inheritedDoc.holder == null) {
|
||||
inheritedDoc.holder = holder;
|
||||
}
|
||||
List<ThrowsTree> inheritedTags = inheritedDoc.tagList.stream()
|
||||
.map(t -> (ThrowsTree) t)
|
||||
.toList();
|
||||
declaredExceptionTags.put(inheritedTags, (ExecutableElement) inheritedDoc.holder);
|
||||
var h = (ExecutableElement) inheritedDoc.holder;
|
||||
inheritedDoc.tagList.forEach(t -> declaredExceptionTags.put((ThrowsTree) t, h));
|
||||
}
|
||||
}
|
||||
result.add(throwsTagsOutput(declaredExceptionTags, writer, alreadyDocumented,
|
||||
typeSubstitutions, false));
|
||||
result.add(throwsTagsOutput(declaredExceptionTags, alreadyDocumented, typeSubstitutions,
|
||||
writer));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -235,7 +234,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet {
|
||||
Set<String> alreadyDocumented,
|
||||
TagletWriter writer) {
|
||||
// TODO: assert declaredExceptionTypes are instantiated
|
||||
Utils utils = writer.configuration().utils;
|
||||
var utils = writer.configuration().utils;
|
||||
Content result = writer.getOutputInstance();
|
||||
for (TypeMirror declaredExceptionType : declaredExceptionTypes) {
|
||||
TypeElement te = utils.asTypeElement(declaredExceptionType);
|
||||
|
||||
@ -547,10 +547,6 @@ public class CommentHelper {
|
||||
return dtree.getKind() == SEE ? ((SeeTree)dtree).getReference() : null;
|
||||
}
|
||||
|
||||
public ReferenceTree getExceptionName(ThrowsTree tt) {
|
||||
return tt.getExceptionName();
|
||||
}
|
||||
|
||||
public IdentifierTree getName(DocTree dtree) {
|
||||
switch (dtree.getKind()) {
|
||||
case PARAM:
|
||||
|
||||
@ -412,6 +412,12 @@ insertThread(JNIEnv *env, ThreadList *list, jthread thread)
|
||||
if (error != JVMTI_ERROR_NONE) {
|
||||
EXIT_ERROR(error, "getting vthread state");
|
||||
}
|
||||
if ((vthread_state & JVMTI_THREAD_STATE_ALIVE) == 0) {
|
||||
// Thread not alive so put on otherThreads list instead of runningVThreads.
|
||||
// It might not have started yet or might have terminated. Either way,
|
||||
// otherThreads is the place for it.
|
||||
list = &otherThreads;
|
||||
}
|
||||
if (suspendAllCount > 0) {
|
||||
// Assume the suspendAllCount, just like the regular thread case above.
|
||||
node->suspendCount = suspendAllCount;
|
||||
@ -419,7 +425,6 @@ insertThread(JNIEnv *env, ThreadList *list, jthread thread)
|
||||
// If state == 0, then this is a new vthread that has not been started yet.
|
||||
// Need to suspendOnStart in that case, just like the regular thread case above.
|
||||
node->suspendOnStart = JNI_TRUE;
|
||||
list = &otherThreads; // Put on otherThreads list instead of runningVThreads
|
||||
}
|
||||
}
|
||||
if (vthread_state != 0) {
|
||||
|
||||
@ -54,7 +54,7 @@ public final class RecordedThread extends RecordedObject {
|
||||
/**
|
||||
* Returns the thread ID used by the operating system.
|
||||
*
|
||||
* @return The Java thread ID, or {@code -1} if doesn't exist
|
||||
* @return the OS thread ID, or {@code -1} if doesn't exist
|
||||
*/
|
||||
public long getOSThreadId() {
|
||||
Long l = getTyped("osThreadId", Long.class, -1L);
|
||||
@ -86,6 +86,8 @@ public final class RecordedThread extends RecordedObject {
|
||||
* Returns the Java thread ID, or {@code -1} if it's not a Java thread.
|
||||
*
|
||||
* @return the Java thread ID, or {@code -1} if it's not a Java thread
|
||||
*
|
||||
* @see java.lang.Thread#threadId()
|
||||
*/
|
||||
public long getJavaThreadId() {
|
||||
Long l = getTyped("javaThreadId", Long.class, -1L);
|
||||
@ -97,7 +99,10 @@ public final class RecordedThread extends RecordedObject {
|
||||
* reused within the lifespan of the JVM.
|
||||
* <p>
|
||||
* See {@link #getJavaThreadId()} for the ID that is returned by
|
||||
* {@code java.lang.Thread.getId()}
|
||||
* {@code java.lang.Thread.threadId()}.
|
||||
* <p>
|
||||
* See {@link #getOSThreadId()} for the ID that is returned by
|
||||
* the operating system.
|
||||
*
|
||||
* @return a unique ID for the thread
|
||||
*/
|
||||
|
||||
@ -84,8 +84,6 @@ public class SelfSuspendDisablerTest {
|
||||
|
||||
resume(t1);
|
||||
|
||||
testJvmtiThreadState(t1, RUNNABLE);
|
||||
|
||||
suspendAllVirtualThreads();
|
||||
});
|
||||
|
||||
|
||||
@ -495,7 +495,6 @@ java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java 8151492 generic-
|
||||
java/lang/invoke/LFCaching/LFGarbageCollectedTest.java 8078602 generic-all
|
||||
java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-x64
|
||||
java/lang/invoke/RicochetTest.java 8251969 generic-all
|
||||
java/lang/CompressExpandTest.java 8287851 generic-all
|
||||
java/lang/ref/OOMEInReferenceHandler.java 8066859 generic-all
|
||||
|
||||
############################################################################
|
||||
|
||||
@ -494,6 +494,7 @@ jdk_jdi_sanity = \
|
||||
com/sun/jdi/ResumeOneThreadTest.java \
|
||||
com/sun/jdi/RunToExit.java \
|
||||
com/sun/jdi/SourceNameFilterTest.java \
|
||||
com/sun/jdi/SuspendAfterDeath.java \
|
||||
com/sun/jdi/VarargsTest.java \
|
||||
com/sun/jdi/Vars.java \
|
||||
com/sun/jdi/redefineMethod/RedefineTest.java \
|
||||
|
||||
167
test/jdk/com/sun/jdi/SuspendAfterDeath.java
Normal file
167
test/jdk/com/sun/jdi/SuspendAfterDeath.java
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=platform_thread
|
||||
* @bug 8287847
|
||||
* @summary Test suspending a platform thread after it has terminated.
|
||||
* @enablePreview
|
||||
* @requires vm.continuations
|
||||
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||
* @run compile SuspendAfterDeath.java
|
||||
* @run main/othervm SuspendAfterDeath
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=virtual_thread
|
||||
* @bug 8287847
|
||||
* @summary Test suspending a virtual thread after it has terminated.
|
||||
* @enablePreview
|
||||
* @requires vm.continuations
|
||||
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||
* @run compile SuspendAfterDeath.java
|
||||
* @run main/othervm SuspendAfterDeath Virtual
|
||||
*/
|
||||
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.event.*;
|
||||
import com.sun.jdi.request.*;
|
||||
import java.util.*;
|
||||
|
||||
class SuspendAfterDeathTarg {
|
||||
static final String THREAD_NAME = "duke";
|
||||
|
||||
// breakpoint here
|
||||
static void done() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean useVirtualThread = ((args.length > 0) && args[0].equals("Virtual"));
|
||||
Thread thread;
|
||||
System.out.println("Starting debuggee " + (useVirtualThread ? "virtual" : "platform") + " thread.");
|
||||
if (useVirtualThread) {
|
||||
thread = Thread.ofVirtual().name(THREAD_NAME).start(() -> { });
|
||||
} else {
|
||||
thread = Thread.ofPlatform().name(THREAD_NAME).start(() -> { });
|
||||
}
|
||||
thread.join();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
public class SuspendAfterDeath extends TestScaffold {
|
||||
private volatile ThreadReference targetThread;
|
||||
private volatile boolean breakpointReached;
|
||||
private static boolean useVirtualThread = false;
|
||||
|
||||
SuspendAfterDeath() {
|
||||
super(new String[0]); // no args to pass along to debuggee
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length == 1) {
|
||||
if ("Virtual".equals(args[0])) {
|
||||
useVirtualThread = true; // see connect() below for how this is handled
|
||||
} else {
|
||||
throw new RuntimeException("SuspendAfterDeath: invalid argument: " + args[0]);
|
||||
}
|
||||
} else if (args.length != 0) {
|
||||
throw new RuntimeException("SuspendAfterDeath: incorrect number of arguments: " + args.length);
|
||||
}
|
||||
new SuspendAfterDeath().startTests();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadDied(ThreadDeathEvent event) {
|
||||
ThreadReference eventThread = event.thread();
|
||||
if (eventThread.name().equals(SuspendAfterDeathTarg.THREAD_NAME)) {
|
||||
System.out.println("Target thread died, thread=" + eventThread +
|
||||
", state=" + eventThread.status());
|
||||
targetThread = eventThread;
|
||||
if (targetThread.status() != ThreadReference.THREAD_STATUS_RUNNING) {
|
||||
failure("FAILED: wrong state for thread: " + targetThread.status());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakpointReached(BreakpointEvent event) {
|
||||
ThreadReference eventThread = event.thread();
|
||||
System.out.println("Breakpoint, thread=" + eventThread);
|
||||
if (targetThread == null) {
|
||||
failure("FAILED: got Breakpoint event before ThreadDeath event.");
|
||||
} else {
|
||||
System.out.println("Target thread status at breakpoint: thread=" + targetThread +
|
||||
", state=" + targetThread.status());
|
||||
if (targetThread.status() != ThreadReference.THREAD_STATUS_ZOMBIE) {
|
||||
failure("FAILED: wrong state for thread: " + targetThread.status());
|
||||
}
|
||||
breakpointReached = true;
|
||||
/* Suspend the thread. This is being done after the thread has exited. */
|
||||
targetThread.suspend();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(String args[]) {
|
||||
if (useVirtualThread) {
|
||||
/* Append the "Virtual" argument to the arguments used for the debuggee. */
|
||||
List<String> argList = new ArrayList(Arrays.asList(args));
|
||||
argList.add("Virtual");
|
||||
args = argList.toArray(args);
|
||||
}
|
||||
super.connect(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runTests() throws Exception {
|
||||
BreakpointEvent bpe = startToMain("SuspendAfterDeathTarg");
|
||||
EventRequestManager erm = vm().eventRequestManager();
|
||||
|
||||
// listener for ThreadDeathEvent captures reference to the thread
|
||||
ThreadDeathRequest request1 = erm.createThreadDeathRequest();
|
||||
request1.enable();
|
||||
|
||||
// listener for BreakpointEvent attempts to suspend the thread
|
||||
ReferenceType targetClass = bpe.location().declaringType();
|
||||
Location loc = findMethod(targetClass, "done", "()V").location();
|
||||
BreakpointRequest request2 = erm.createBreakpointRequest(loc);
|
||||
request2.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
|
||||
request2.enable();
|
||||
|
||||
listenUntilVMDisconnect();
|
||||
|
||||
if (targetThread == null) {
|
||||
failure("FAILED: never got ThreadDeath event for target thread.");
|
||||
}
|
||||
if (!breakpointReached) {
|
||||
failure("FAILED: never got Breakpoint event for target thread.");
|
||||
}
|
||||
|
||||
if (!testFailed) {
|
||||
println("SuspendAfterDeath: passed");
|
||||
} else {
|
||||
throw new Exception("SuspendAfterDeath: failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,6 +66,7 @@ abstract public class TestScaffold extends TargetAdapter {
|
||||
final String[] args;
|
||||
protected boolean testFailed = false;
|
||||
protected long startTime;
|
||||
public static final String OLD_MAIN_THREAD_NAME = "old-m-a-i-n";
|
||||
|
||||
static private class ArgInfo {
|
||||
String targetVMArgs = "";
|
||||
@ -359,10 +360,10 @@ abstract public class TestScaffold extends TargetAdapter {
|
||||
}
|
||||
|
||||
protected void startUp(String targetName) {
|
||||
List argList = new ArrayList(Arrays.asList(args));
|
||||
List<String> argList = new ArrayList(Arrays.asList(args));
|
||||
argList.add(targetName);
|
||||
println("run args: " + argList);
|
||||
connect((String[]) argList.toArray(args));
|
||||
connect(argList.toArray(args));
|
||||
waitForVMStart();
|
||||
}
|
||||
|
||||
@ -461,6 +462,9 @@ abstract public class TestScaffold extends TargetAdapter {
|
||||
if ("Virtual".equals(mainWrapper)) {
|
||||
argInfo.targetAppCommandLine = TestScaffold.class.getName() + " " + mainWrapper + " ";
|
||||
argInfo.targetVMArgs += "--enable-preview ";
|
||||
} else if ("true".equals(System.getProperty("test.enable.preview"))) {
|
||||
// the test specified @enablePreview.
|
||||
argInfo.targetVMArgs += "--enable-preview ";
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
@ -969,6 +973,8 @@ abstract public class TestScaffold extends TargetAdapter {
|
||||
tg.uncaughtThrowable = error;
|
||||
}
|
||||
});
|
||||
Thread.currentThread().setName(OLD_MAIN_THREAD_NAME);
|
||||
vthread.setName("main");
|
||||
vthread.join();
|
||||
} else if (wrapper.equals("Kernel")) {
|
||||
MainThreadGroup tg = new MainThreadGroup();
|
||||
|
||||
@ -69,7 +69,7 @@ public class TestThrowsTagInheritance extends JavadocTester {
|
||||
checkExit(Exit.OK);
|
||||
|
||||
// The method should not inherit the IOOBE throws tag from the abstract class,
|
||||
// for now keep keep this bug compatible, should fix this correctly in
|
||||
// for now keep this bug compatible, should fix this correctly in
|
||||
// the future.
|
||||
checkOutput("pkg/Extender.html", false, "java.lang.IndexOutOfBoundsException");
|
||||
}
|
||||
|
||||
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8067757
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* @build toolbox.ToolBox javadoc.tester.*
|
||||
* @run main TestOneToMany
|
||||
*/
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class TestOneToMany extends JavadocTester {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
var tester = new TestOneToMany();
|
||||
tester.runTests(m -> new Object[]{Paths.get(m.getName())});
|
||||
}
|
||||
|
||||
private final ToolBox tb = new ToolBox();
|
||||
|
||||
// These tests:
|
||||
//
|
||||
// - Use own exceptions to not depend on platform links or a setup with
|
||||
// the no-platform-links option
|
||||
// - Enclose files in a package to exercise a typical source layout and
|
||||
// avoid enumerating separate files in the javadoc command
|
||||
|
||||
@Test
|
||||
public void testUncheckedException(Path base) throws Exception {
|
||||
var src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
package x;
|
||||
|
||||
public class MyRuntimeException extends RuntimeException { }
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I {
|
||||
|
||||
/**
|
||||
* @throws MyRuntimeException if this
|
||||
* @throws MyRuntimeException if that
|
||||
*/
|
||||
void m();
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I1 extends I {
|
||||
|
||||
@Override
|
||||
void m() throws MyRuntimeException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class IImpl implements I {
|
||||
|
||||
@Override
|
||||
public void m() throws MyRuntimeException { }
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class C {
|
||||
|
||||
/**
|
||||
* @throws MyRuntimeException if this
|
||||
* @throws MyRuntimeException if that
|
||||
*/
|
||||
public void m();
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class C1 extends C {
|
||||
|
||||
@Override
|
||||
public void m() throws MyRuntimeException { }
|
||||
}
|
||||
""");
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"x");
|
||||
checkExit(Exit.OK);
|
||||
checkOutput("x/IImpl.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
checkOutput("x/I1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
checkOutput("x/C1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Overrides:</dt>
|
||||
<dd><code><a href="C.html#m()">m</a></code> in class <code><a href="C.html" title="class in x">C</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUncheckedExceptionWithRedundantThrows(Path base) throws Exception {
|
||||
var src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
package x;
|
||||
|
||||
public class MyRuntimeException extends RuntimeException { }
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I {
|
||||
|
||||
/**
|
||||
* @throws MyRuntimeException if this
|
||||
* @throws MyRuntimeException if that
|
||||
*/
|
||||
void m() throws MyRuntimeException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I1 extends I {
|
||||
|
||||
@Override
|
||||
void m() throws MyRuntimeException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class IImpl implements I {
|
||||
|
||||
@Override
|
||||
public void m() throws MyRuntimeException { }
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class C {
|
||||
|
||||
/**
|
||||
* @throws MyRuntimeException if this
|
||||
* @throws MyRuntimeException if that
|
||||
*/
|
||||
public void m() throws MyRuntimeException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class C1 extends C {
|
||||
|
||||
@Override
|
||||
public void m() throws MyRuntimeException { }
|
||||
}
|
||||
""");
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"x");
|
||||
checkExit(Exit.OK);
|
||||
checkOutput("x/IImpl.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
checkOutput("x/I1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
checkOutput("x/C1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Overrides:</dt>
|
||||
<dd><code><a href="C.html#m()">m</a></code> in class <code><a href="C.html" title="class in x">C</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyRuntimeException.html" title="class in x">MyRuntimeException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckedException(Path base) throws Exception {
|
||||
var src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
package x;
|
||||
|
||||
public class MyCheckedException extends Exception { }
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I {
|
||||
|
||||
/**
|
||||
* @throws MyCheckedException if this
|
||||
* @throws MyCheckedException if that
|
||||
*/
|
||||
void m() throws MyCheckedException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I1 extends I {
|
||||
|
||||
@Override
|
||||
void m() throws MyCheckedException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class IImpl implements I {
|
||||
|
||||
@Override
|
||||
public void m() throws MyCheckedException { }
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class C {
|
||||
|
||||
/**
|
||||
* @throws MyCheckedException if this
|
||||
* @throws MyCheckedException if that
|
||||
*/
|
||||
public void m() throws MyCheckedException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class C1 extends C {
|
||||
|
||||
@Override
|
||||
public void m() throws MyCheckedException { }
|
||||
}
|
||||
""");
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"x");
|
||||
checkExit(Exit.OK);
|
||||
checkOutput("x/IImpl.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyCheckedException.html" title="class in x">MyCheckedException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyCheckedException.html" title="class in x">MyCheckedException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
checkOutput("x/I1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyCheckedException.html" title="class in x">MyCheckedException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyCheckedException.html" title="class in x">MyCheckedException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
checkOutput("x/C1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Overrides:</dt>
|
||||
<dd><code><a href="C.html#m()">m</a></code> in class <code><a href="C.html" title="class in x">C</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyCheckedException.html" title="class in x">MyCheckedException</a></code> - if this</dd>
|
||||
<dd><code><a href="MyCheckedException.html" title="class in x">MyCheckedException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubExceptionDoubleInheritance(Path base) throws Exception {
|
||||
var src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
package x;
|
||||
|
||||
public class MyException extends Exception { }
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public class MySubException extends MyException { }
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I {
|
||||
|
||||
/**
|
||||
* @throws MyException if this
|
||||
* @throws MySubException if that
|
||||
*/
|
||||
void m() throws MyException, MySubException;
|
||||
}
|
||||
""", """
|
||||
package x;
|
||||
|
||||
public interface I1 extends I {
|
||||
|
||||
@Override
|
||||
void m() throws MyException, MySubException;
|
||||
}
|
||||
""");
|
||||
javadoc("-d", base.resolve("out").toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"x");
|
||||
checkExit(Exit.OK);
|
||||
checkOutput("x/I1.html", true, """
|
||||
<dl class="notes">
|
||||
<dt>Specified by:</dt>
|
||||
<dd><code><a href="I.html#m()">m</a></code> in interface <code><a href="I.html" title="interface in x">I</a></code></dd>
|
||||
<dt>Throws:</dt>
|
||||
<dd><code><a href="MyException.html" title="class in x">MyException</a></code> - if this</dd>
|
||||
<dd><code><a href="MySubException.html" title="class in x">MySubException</a></code> - if that</dd>
|
||||
</dl>""");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user