mirror of
https://github.com/openjdk/jdk.git
synced 2026-06-06 10:42:45 +00:00
6960970: Debugger very slow during stepping
Co-authored-by: Patricio Chilano Mateo <pchilanomate@openjdk.org> Reviewed-by: lmesnik, pchilanomate
This commit is contained in:
parent
689800b993
commit
7da2477700
@ -1174,7 +1174,7 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
// Whenever JVMTI is interp_only_mode, method entry/exit events are sent to
|
||||
// track stack depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) {
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
Label L;
|
||||
// Note: frame::interpreter_frame_result has a dependency on how the
|
||||
// method result is saved across the call to post_method_exit. If this
|
||||
@ -1183,8 +1183,15 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
|
||||
// template interpreter will leave the result on the top of the stack.
|
||||
push(state);
|
||||
ldrw(r3, Address(rthread, JavaThread::interp_only_mode_offset()));
|
||||
cbz(r3, L);
|
||||
|
||||
ldr(rscratch1, Address(rthread, JavaThread::jvmti_thread_state_offset()));
|
||||
cbz(rscratch1, L); // if (thread->jvmti_thread_state() == nullptr) exit;
|
||||
|
||||
ldrw(rscratch1, Address(rscratch1, JvmtiThreadState::frame_pop_cnt_offset()));
|
||||
ldrw(rscratch2, Address(rthread, JavaThread::interp_only_mode_offset()));
|
||||
orrw(rscratch1, rscratch1, rscratch2);
|
||||
cbzw(rscratch1, L);
|
||||
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
|
||||
bind(L);
|
||||
|
||||
@ -1576,14 +1576,21 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
// Whenever JVMTI is interp_only_mode, method entry/exit events are sent to
|
||||
// track stack depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && can_post_interpreter_events()) {
|
||||
if (mode == NotifyJVMTI && (can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
Label L;
|
||||
const Register thread_state = R2_tmp;
|
||||
|
||||
// Note: frame::interpreter_frame_result has a dependency on how the
|
||||
// method result is saved across the call to post_method_exit. If this
|
||||
// is changed then the interpreter_frame_result implementation will
|
||||
// need to be updated too.
|
||||
|
||||
ldr(thread_state, Address(Rthread, JavaThread::jvmti_thread_state_offset()));
|
||||
cbz(thread_state, L); // if (thread->jvmti_thread_state() == nullptr) exit;
|
||||
|
||||
ldr_s32(thread_state, Address(thread_state, JvmtiThreadState::frame_pop_cnt_offset()));
|
||||
ldr_s32(Rtemp, Address(Rthread, JavaThread::interp_only_mode_offset()));
|
||||
orr(Rtemp, Rtemp, thread_state);
|
||||
cbz(Rtemp, L);
|
||||
|
||||
if (native) {
|
||||
|
||||
@ -2345,18 +2345,12 @@ void InterpreterMacroAssembler::notify_method_exit(bool is_native_method, TosSta
|
||||
// entry/exit events are sent for that thread to track stack
|
||||
// depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) {
|
||||
Label jvmti_post_done;
|
||||
|
||||
lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread);
|
||||
cmpwi(CR0, R0, 0);
|
||||
beq(CR0, jvmti_post_done);
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
if (!is_native_method) { push(state); } // Expose tos to GC.
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit), check_exceptions);
|
||||
if (!is_native_method) { pop(state); }
|
||||
|
||||
align(32, 12);
|
||||
bind(jvmti_post_done);
|
||||
}
|
||||
|
||||
// Dtrace support not implemented.
|
||||
|
||||
@ -1220,8 +1220,7 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
// Whenever JVMTI is interp_only_mode, method entry/exit events are sent to
|
||||
// track stack depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) {
|
||||
Label L;
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
// Note: frame::interpreter_frame_result has a dependency on how the
|
||||
// method result is saved across the call to post_method_exit. If this
|
||||
// is changed then the interpreter_frame_result implementation will
|
||||
@ -1229,11 +1228,8 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
|
||||
// template interpreter will leave the result on the top of the stack.
|
||||
push(state);
|
||||
lwu(x13, Address(xthread, JavaThread::interp_only_mode_offset()));
|
||||
beqz(x13, L);
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
|
||||
bind(L);
|
||||
pop(state);
|
||||
}
|
||||
|
||||
|
||||
@ -2002,14 +2002,10 @@ void InterpreterMacroAssembler::notify_method_exit(bool native_method,
|
||||
// entry/exit events are sent for that thread to track stack
|
||||
// depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) {
|
||||
Label jvmti_post_done;
|
||||
MacroAssembler::load_and_test_int(Z_R0, Address(Z_thread, JavaThread::interp_only_mode_offset()));
|
||||
z_bre(jvmti_post_done);
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
if (!native_method) push(state); // see frame::interpreter_frame_result()
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
|
||||
if (!native_method) pop(state);
|
||||
bind(jvmti_post_done);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1603,7 +1603,7 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
// the code to check if the event should be sent.
|
||||
Register rthread = r15_thread;
|
||||
Register rarg = c_rarg1;
|
||||
if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) {
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
Label L;
|
||||
// Note: frame::interpreter_frame_result has a dependency on how the
|
||||
// method result is saved across the call to post_method_exit. If this
|
||||
@ -1612,9 +1612,18 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
|
||||
// template interpreter will leave the result on the top of the stack.
|
||||
push(state);
|
||||
movl(rdx, Address(rthread, JavaThread::interp_only_mode_offset()));
|
||||
testl(rdx, rdx);
|
||||
|
||||
movptr(rdx, Address(rthread, JavaThread::jvmti_thread_state_offset()));
|
||||
testptr(rdx, rdx);
|
||||
jcc(Assembler::zero, L); // if (thread->jvmti_thread_state() == nullptr) exit;
|
||||
|
||||
movl(rdx, Address(rdx, JvmtiThreadState::frame_pop_cnt_offset()));
|
||||
movl(rcx, Address(rthread, JavaThread::interp_only_mode_offset()));
|
||||
|
||||
orl(rdx, rcx);
|
||||
testl(rdx,rdx);
|
||||
jcc(Assembler::zero, L);
|
||||
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
|
||||
bind(L);
|
||||
|
||||
@ -1057,6 +1057,10 @@ JRT_ENTRY(nmethod*,
|
||||
|
||||
LastFrameAccessor last_frame(current);
|
||||
assert(last_frame.is_interpreted_frame(), "must come from interpreter");
|
||||
|
||||
if (JvmtiExport::can_post_frame_pop() && JvmtiExport::has_frame_pop_for_top_frame(current)) {
|
||||
return nullptr; // no OSR if there is a FramePop event request for top frame
|
||||
}
|
||||
methodHandle method(current, last_frame.method());
|
||||
const int branch_bci = branch_bcp != nullptr ? method->bci_from(branch_bcp) : InvocationEntryBci;
|
||||
const int bci = branch_bcp != nullptr ? method->bci_from(last_frame.bcp()) : InvocationEntryBci;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -3145,10 +3145,14 @@ run:
|
||||
// Whenever JVMTI puts a thread in interp_only_mode, method
|
||||
// entry/exit events are sent for that thread to track stack depth.
|
||||
|
||||
if (JVMTI_ENABLED && !suppress_exit_event && THREAD->is_interp_only_mode()) {
|
||||
// Prevent any HandleMarkCleaner from freeing our live handles
|
||||
HandleMark __hm(THREAD);
|
||||
CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD));
|
||||
if (JVMTI_ENABLED && !suppress_exit_event) {
|
||||
JvmtiThreadState* state = THREAD->jvmti_thread_state();
|
||||
int frame_pop_cnt = state == nullptr ? 0 : state->frame_pop_cnt();
|
||||
if (THREAD->is_interp_only_mode() || frame_pop_cnt) {
|
||||
// Prevent any HandleMarkCleaner from freeing our live handles
|
||||
HandleMark __hm(THREAD);
|
||||
CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -154,6 +154,7 @@ public:
|
||||
inline void set_has_bitmap(bool value);
|
||||
|
||||
inline bool has_thaw_slowpath_condition() const;
|
||||
inline void force_slow_path();
|
||||
|
||||
inline bool requires_barriers();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -192,6 +192,15 @@ inline void stackChunkOopDesc::set_has_bitmap(bool value) { set_flag(FL
|
||||
|
||||
inline bool stackChunkOopDesc::has_thaw_slowpath_condition() const { return flags() != 0; }
|
||||
|
||||
inline void stackChunkOopDesc::force_slow_path() {
|
||||
#if INCLUDE_ZGC || INCLUDE_SHENANDOAHGC
|
||||
if (UseZGC || UseShenandoahGC) {
|
||||
relativize_derived_pointers_concurrently();
|
||||
}
|
||||
#endif
|
||||
set_flag(FLAG_HAS_INTERPRETED_FRAMES, true);
|
||||
}
|
||||
|
||||
inline bool stackChunkOopDesc::requires_barriers() {
|
||||
return Universe::heap()->requires_barriers(this);
|
||||
}
|
||||
|
||||
@ -1363,6 +1363,27 @@ JvmtiEnvBase::set_frame_pop(JvmtiThreadState* state, javaVFrame* jvf, jint depth
|
||||
if (ets->is_frame_pop(frame_number)) {
|
||||
return JVMTI_ERROR_DUPLICATE;
|
||||
}
|
||||
JavaThread* thread = state->get_thread();
|
||||
frame fr = jvf->fr();
|
||||
|
||||
if (jvf->is_compiled_frame()) {
|
||||
if (!fr.can_be_deoptimized()) {
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
|
||||
if (state->is_virtual() && (thread == nullptr || !thread->is_vthread_mounted())) { // unmounted virtual thread
|
||||
assert(fr.is_heap_frame(), "sanity check");
|
||||
fr = jvf->stack_chunk()->derelativize(fr);
|
||||
jvf->stack_chunk()->force_slow_path();
|
||||
fr.deoptimize(nullptr);
|
||||
} else { // platform thread or mounted virtual thread
|
||||
if (fr.is_heap_frame()) {
|
||||
fr = jvf->stack_chunk()->derelativize(fr);
|
||||
jvf->stack_chunk()->force_slow_path();
|
||||
}
|
||||
Deoptimization::deoptimize(thread, fr);
|
||||
}
|
||||
}
|
||||
ets->set_frame_pop(frame_number);
|
||||
return JVMTI_ERROR_NONE;
|
||||
}
|
||||
@ -2514,6 +2535,8 @@ SetOrClearFramePopClosure::do_vthread(Handle target_h) {
|
||||
_result = _env->clear_all_frame_pops(_state);
|
||||
return;
|
||||
}
|
||||
assert(_state->get_thread() == _target_jt, "sanity check");
|
||||
|
||||
javaVFrame *jvf = JvmtiEnvBase::get_vthread_jvf(target_h());
|
||||
_result = _env->set_frame_pop(_state, jvf, _depth);
|
||||
}
|
||||
|
||||
@ -229,6 +229,7 @@ void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
|
||||
"frame pop data only accessible from same or detached thread or direct handshake");
|
||||
JvmtiFramePop fpop(frame_number);
|
||||
JvmtiEventController::set_frame_pop(this, fpop);
|
||||
_state->incr_frame_pop_cnt();
|
||||
}
|
||||
|
||||
|
||||
@ -237,18 +238,21 @@ void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
|
||||
"frame pop data only accessible from same or detached thread or direct handshake");
|
||||
JvmtiFramePop fpop(frame_number);
|
||||
JvmtiEventController::clear_frame_pop(this, fpop);
|
||||
_state->decr_frame_pop_cnt();
|
||||
}
|
||||
|
||||
void JvmtiEnvThreadState::clear_all_frame_pops() {
|
||||
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
|
||||
"frame pop data only accessible from same or detached thread or direct handshake");
|
||||
int frame_pop_delta = _frame_pops == nullptr ? 0 : _frame_pops->length();
|
||||
_state->decr_frame_pop_cnt(frame_pop_delta);
|
||||
JvmtiEventController::clear_all_frame_pops(this);
|
||||
}
|
||||
|
||||
bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
|
||||
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(Thread::current()),
|
||||
"frame pop data only accessible from same or detached thread or direct handshake");
|
||||
if (!jvmti_thread_state()->is_interp_only_mode() || _frame_pops == nullptr) {
|
||||
if (_frame_pops == nullptr) {
|
||||
return false;
|
||||
}
|
||||
JvmtiFramePop fp(cur_frame_number);
|
||||
|
||||
@ -102,9 +102,9 @@ static const jlong MONITOR_BITS = MONITOR_CONTENDED_ENTER_BIT | MONITOR_CONTEND
|
||||
MONITOR_WAIT_BIT | MONITOR_WAITED_BIT;
|
||||
static const jlong EXCEPTION_BITS = EXCEPTION_THROW_BIT | EXCEPTION_CATCH_BIT;
|
||||
static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT |
|
||||
FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT;
|
||||
FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT;
|
||||
static const jlong THREAD_FILTERED_EVENT_BITS = INTERP_EVENT_BITS | EXCEPTION_BITS | MONITOR_BITS | VTHREAD_FILTERED_EVENT_BITS |
|
||||
BREAKPOINT_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT |
|
||||
BREAKPOINT_BIT | FRAME_POP_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT | THREAD_END_BIT |
|
||||
SAMPLED_OBJECT_ALLOC_BIT;
|
||||
static const jlong NEED_THREAD_LIFE_EVENTS = THREAD_FILTERED_EVENT_BITS | THREAD_START_BIT | VTHREAD_START_BIT;
|
||||
static const jlong EARLY_EVENT_BITS = CLASS_FILE_LOAD_HOOK_BIT | CLASS_LOAD_BIT | CLASS_PREPARE_BIT |
|
||||
@ -592,10 +592,6 @@ JvmtiEventControllerPrivate::recompute_thread_enabled(JvmtiThreadState *state) {
|
||||
}
|
||||
julong was_any_env_enabled = state->thread_event_enable()->_event_enabled.get_bits();
|
||||
julong any_env_enabled = 0;
|
||||
// JVMTI_EVENT_FRAME_POP can be disabled (in the case FRAME_POP_BIT is not set),
|
||||
// but we need to set interp_only if some JvmtiEnvThreadState has frame pop set
|
||||
// to clear the request
|
||||
bool has_frame_pops = false;
|
||||
|
||||
{
|
||||
// This iteration will include JvmtiEnvThreadStates whose environments
|
||||
@ -604,7 +600,6 @@ JvmtiEventControllerPrivate::recompute_thread_enabled(JvmtiThreadState *state) {
|
||||
JvmtiEnvThreadStateIterator it(state);
|
||||
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
|
||||
any_env_enabled |= recompute_env_thread_enabled(ets, state);
|
||||
has_frame_pops |= ets->has_frame_pops();
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,7 +615,7 @@ JvmtiEventControllerPrivate::recompute_thread_enabled(JvmtiThreadState *state) {
|
||||
}
|
||||
}
|
||||
// compute interp_only mode
|
||||
bool should_be_interp = (any_env_enabled & INTERP_EVENT_BITS) != 0 || has_frame_pops;
|
||||
bool should_be_interp = (any_env_enabled & INTERP_EVENT_BITS) != 0;
|
||||
bool is_now_interp = state->is_interp_only_mode() || state->is_pending_interp_only_mode();
|
||||
|
||||
if (should_be_interp != is_now_interp) {
|
||||
|
||||
@ -1362,6 +1362,26 @@ JvmtiThreadState* JvmtiExport::hide_single_stepping(JavaThread *thread) {
|
||||
}
|
||||
}
|
||||
|
||||
bool JvmtiExport::has_frame_pop_for_top_frame(JavaThread *current) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
JvmtiThreadState *state = current->jvmti_thread_state();
|
||||
if (state == nullptr || !state->is_enabled(JVMTI_EVENT_FRAME_POP)) {
|
||||
return false;
|
||||
}
|
||||
if (state->frame_pop_cnt() == 0) {
|
||||
return false;
|
||||
}
|
||||
JvmtiEnvThreadStateIterator it(state);
|
||||
int top_frame_num = state->count_frames();
|
||||
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
|
||||
if (ets->has_frame_pops() && ets->is_frame_pop(top_frame_num)) {
|
||||
assert(ets->is_enabled(JVMTI_EVENT_FRAME_POP), "sanity check");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JvmtiExport::post_class_load(JavaThread *thread, Klass* klass) {
|
||||
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
|
||||
return;
|
||||
@ -1898,12 +1918,13 @@ void JvmtiExport::post_method_exit(JavaThread* thread, Method* method, frame cur
|
||||
}
|
||||
JvmtiThreadState* state; // should be initialized in vm state only
|
||||
JavaThread* current = thread; // for JRT_BLOCK
|
||||
bool interp_only; // might be changed in JRT_BLOCK_END
|
||||
|
||||
JRT_BLOCK
|
||||
state = get_jvmti_thread_state(thread);
|
||||
interp_only = state != nullptr && state->is_interp_only_mode();
|
||||
if (interp_only) {
|
||||
if (state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) {
|
||||
bool interp_only = thread->is_interp_only_mode();
|
||||
// Avoid calls to get_jvmti_thread_state if is_interp_only_mode was not enabled.
|
||||
state = interp_only ? get_jvmti_thread_state(thread) : thread->jvmti_thread_state();
|
||||
if (state != nullptr) {
|
||||
if (interp_only && state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) {
|
||||
// Deferred saving Object result into value.
|
||||
if (is_reference_type(type)) {
|
||||
value.l = JNIHandles::make_local(thread, result());
|
||||
@ -1917,7 +1938,7 @@ void JvmtiExport::post_method_exit(JavaThread* thread, Method* method, frame cur
|
||||
post_method_exit_inner(thread, mh, state, false /* not exception exit */, current_frame, value);
|
||||
}
|
||||
JRT_BLOCK_END
|
||||
if (interp_only) {
|
||||
if (state != nullptr) {
|
||||
// The JRT_BLOCK_END can safepoint in ThreadInVMfromJava destructor. Now it is safe to allow
|
||||
// adding FramePop event requests as no safepoint can happen before removing activation.
|
||||
state->clr_top_frame_is_exiting();
|
||||
@ -1943,7 +1964,8 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread,
|
||||
(mh() == nullptr) ? "null" : mh()->klass_name()->as_C_string(),
|
||||
(mh() == nullptr) ? "null" : mh()->name()->as_C_string() ));
|
||||
|
||||
if (state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) {
|
||||
// Need to check is_interp_only_mode to consistently post method exit event for all frames.
|
||||
if (thread->is_interp_only_mode() && state->is_enabled(JVMTI_EVENT_METHOD_EXIT)) {
|
||||
JvmtiEnvThreadStateIterator it(state);
|
||||
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
|
||||
if (ets->is_enabled(JVMTI_EVENT_METHOD_EXIT)) {
|
||||
@ -2154,21 +2176,14 @@ void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, Method* met
|
||||
|
||||
if (state->is_exception_detected()) {
|
||||
|
||||
// The cached cur_stack_depth might have changed from the operations of frame pop or method exit.
|
||||
// We are not 100% sure the cached cur_stack_depth is still valid depth so invalidate it.
|
||||
state->invalidate_cur_stack_depth();
|
||||
if (!in_handler_frame) {
|
||||
// Not in exception handler.
|
||||
if(state->is_interp_only_mode()) {
|
||||
// method exit and frame pop events are posted only in interp mode.
|
||||
// When these events are enabled code should be in running in interp mode.
|
||||
jvalue no_value;
|
||||
no_value.j = 0L;
|
||||
JvmtiExport::post_method_exit_inner(thread, mh, state, true, thread->last_frame(), no_value);
|
||||
// The cached cur_stack_depth might have changed from the
|
||||
// operations of frame pop or method exit. We are not 100% sure
|
||||
// the cached cur_stack_depth is still valid depth so invalidate
|
||||
// it.
|
||||
state->invalidate_cur_stack_depth();
|
||||
}
|
||||
jvalue no_value;
|
||||
no_value.j = 0L;
|
||||
JvmtiExport::post_method_exit_inner(thread, mh, state, true, thread->last_frame(), no_value);
|
||||
} else {
|
||||
// In exception handler frame. Report exception catch.
|
||||
assert(location != nullptr, "must be a known location");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -320,6 +320,9 @@ class JvmtiExport : public AllStatic {
|
||||
static void expose_single_stepping(JvmtiThreadState* state) NOT_JVMTI_RETURN;
|
||||
static JvmtiThreadState* hide_single_stepping(JavaThread *thread) NOT_JVMTI_RETURN_(nullptr);
|
||||
|
||||
// frame pop management
|
||||
static bool has_frame_pop_for_top_frame(JavaThread *current);
|
||||
|
||||
// Methods that notify the debugger that something interesting has happened in the VM.
|
||||
static void post_early_vm_start () NOT_JVMTI_RETURN;
|
||||
static void post_vm_start () NOT_JVMTI_RETURN;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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,9 @@
|
||||
#include "runtime/vmOperations.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
// Forward Declaration
|
||||
|
||||
class JvmtiEnv;
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@ -327,7 +327,6 @@ void JvmtiManageCapabilities::update() {
|
||||
avail.can_generate_field_access_events ||
|
||||
avail.can_generate_field_modification_events ||
|
||||
avail.can_generate_single_step_events ||
|
||||
avail.can_generate_frame_pop_events ||
|
||||
avail.can_generate_method_entry_events ||
|
||||
avail.can_generate_method_exit_events;
|
||||
#ifdef ZERO
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "prims/jvmtiEventController.inline.hpp"
|
||||
#include "prims/jvmtiImpl.hpp"
|
||||
#include "prims/jvmtiThreadState.inline.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/jniHandles.inline.hpp"
|
||||
@ -61,6 +62,7 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop)
|
||||
_hide_single_stepping = false;
|
||||
_pending_interp_only_mode = false;
|
||||
_hide_level = 0;
|
||||
_frame_pop_cnt = 0;
|
||||
_pending_step_for_popframe = false;
|
||||
_class_being_redefined = nullptr;
|
||||
_class_load_kind = jvmti_class_load_kind_load;
|
||||
@ -470,21 +472,19 @@ void JvmtiThreadState::process_pending_step_for_popframe() {
|
||||
// Called by: PopFrame
|
||||
//
|
||||
void JvmtiThreadState::update_for_pop_top_frame() {
|
||||
if (is_interp_only_mode()) {
|
||||
// remove any frame pop notification request for the top frame
|
||||
// in any environment
|
||||
int popframe_number = cur_stack_depth();
|
||||
{
|
||||
JvmtiEnvThreadStateIterator it(this);
|
||||
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
|
||||
if (ets->is_frame_pop(popframe_number)) {
|
||||
ets->clear_frame_pop(popframe_number);
|
||||
}
|
||||
// remove any frame pop notification request for the top frame
|
||||
// in any environment
|
||||
int popframe_number = cur_stack_depth();
|
||||
{
|
||||
JvmtiEnvThreadStateIterator it(this);
|
||||
for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
|
||||
if (ets->is_frame_pop(popframe_number)) {
|
||||
ets->clear_frame_pop(popframe_number);
|
||||
}
|
||||
}
|
||||
// force stack depth to be recalculated
|
||||
invalidate_cur_stack_depth();
|
||||
}
|
||||
// force stack depth to be recalculated
|
||||
invalidate_cur_stack_depth();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
#include "memory/allocation.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/oopHandle.hpp"
|
||||
#include "prims/jvmtiEventController.hpp"
|
||||
#include "prims/jvmtiEventController.inline.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
@ -139,6 +139,7 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
|
||||
bool _top_frame_is_exiting;
|
||||
bool _saved_interp_only_mode;
|
||||
int _hide_level;
|
||||
volatile int _frame_pop_cnt;
|
||||
|
||||
public:
|
||||
enum ExceptionState {
|
||||
@ -241,6 +242,29 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
|
||||
return _next;
|
||||
}
|
||||
|
||||
// Optimizations for FramePop support.
|
||||
static ByteSize frame_pop_cnt_offset() { return byte_offset_of(JvmtiThreadState, _frame_pop_cnt); }
|
||||
|
||||
int frame_pop_cnt() { return AtomicAccess::load(&_frame_pop_cnt); }
|
||||
|
||||
void incr_frame_pop_cnt() {
|
||||
assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check");
|
||||
AtomicAccess::inc(&_frame_pop_cnt);
|
||||
assert(_frame_pop_cnt > 0, "Unexpected count: %d", _frame_pop_cnt);
|
||||
}
|
||||
|
||||
void decr_frame_pop_cnt() {
|
||||
assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check");
|
||||
AtomicAccess::dec(&_frame_pop_cnt);
|
||||
assert(_frame_pop_cnt >= 0, "Unexpected count: %d", _frame_pop_cnt);
|
||||
}
|
||||
|
||||
void decr_frame_pop_cnt(int delta) {
|
||||
assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check");
|
||||
AtomicAccess::store(&_frame_pop_cnt, AtomicAccess::load(&_frame_pop_cnt) - delta);
|
||||
assert(_frame_pop_cnt >= 0, "Unexpected count: %d", _frame_pop_cnt);
|
||||
}
|
||||
|
||||
// Current stack depth is only valid when is_interp_only_mode() returns true.
|
||||
// These functions should only be called at a safepoint - usually called from same thread.
|
||||
// Returns the number of Java activations on the stack.
|
||||
|
||||
@ -366,17 +366,23 @@ void frame::deoptimize(JavaThread* thread) {
|
||||
|
||||
#ifdef ASSERT
|
||||
if (thread != nullptr) {
|
||||
frame check = thread->last_frame();
|
||||
if (is_older(check.id())) {
|
||||
RegisterMap map(thread,
|
||||
RegisterMap::UpdateMap::skip,
|
||||
RegisterMap::ProcessFrames::include,
|
||||
RegisterMap::WalkContinuation::skip);
|
||||
while (id() != check.id()) {
|
||||
check = check.sender(&map);
|
||||
frame fr = thread->last_frame();
|
||||
RegisterMap map(thread,
|
||||
RegisterMap::UpdateMap::skip,
|
||||
RegisterMap::ProcessFrames::include,
|
||||
!is_heap_frame() ? RegisterMap::WalkContinuation::skip : RegisterMap::WalkContinuation::include);
|
||||
intptr_t* fr_id = fr.id();
|
||||
while (id() != fr_id) {
|
||||
fr = fr.sender(&map);
|
||||
if (fr.is_heap_frame()) {
|
||||
assert(is_heap_frame(), "");
|
||||
frame derel_fr = map.stack_chunk()->derelativize(fr);
|
||||
fr_id = derel_fr.id();
|
||||
} else {
|
||||
fr_id = fr.id();
|
||||
}
|
||||
assert(check.is_deoptimized_frame(), "missed deopt");
|
||||
}
|
||||
assert(fr.is_deoptimized_frame(), "missed deopt");
|
||||
}
|
||||
#endif // ASSERT
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -50,7 +50,7 @@ public class NotifyFramePopTest {
|
||||
}
|
||||
|
||||
// Sanity testing that FRAME_POP works.
|
||||
test("sanity", true, () -> {
|
||||
test("sanity", 2, () -> { // FramePop events expected: 2
|
||||
setFramePopNotificationMode(true);
|
||||
notifyFramePop(null);
|
||||
});
|
||||
@ -58,14 +58,14 @@ public class NotifyFramePopTest {
|
||||
// Request notification and then disable FRAME_POP event notification.
|
||||
// This should not prevent the notification for the frame being cleared
|
||||
// when we return from the method.
|
||||
test("requestAndDisable", false, () -> {
|
||||
test("requestAndDisable", 0, () -> { // FramePop events expected: 0
|
||||
setFramePopNotificationMode(true);
|
||||
notifyFramePop(null);
|
||||
setFramePopNotificationMode(false);
|
||||
});
|
||||
|
||||
// Ensure there is no pending event
|
||||
test("ensureCleared", false, () -> {
|
||||
test("ensureCleared", 0, () -> { // FramePop events expected: 0
|
||||
setFramePopNotificationMode(true);
|
||||
});
|
||||
|
||||
@ -75,7 +75,7 @@ public class NotifyFramePopTest {
|
||||
private native static boolean canGenerateFramePopEvents();
|
||||
private native static void setFramePopNotificationMode(boolean enabled);
|
||||
private native static void notifyFramePop(Thread thread);
|
||||
private native static boolean framePopReceived();
|
||||
private native static int framePopsReceived();
|
||||
|
||||
private static void log(String msg) {
|
||||
System.out.println(msg);
|
||||
@ -85,16 +85,16 @@ public class NotifyFramePopTest {
|
||||
void test();
|
||||
}
|
||||
|
||||
private static void test(String name, boolean framePopExpected, Test theTest) {
|
||||
private static void test(String name, int framePopExpected, Test theTest) {
|
||||
log("test: " + name);
|
||||
theTest.test();
|
||||
boolean actual = framePopReceived();
|
||||
int actual = framePopsReceived();
|
||||
if (framePopExpected != actual) {
|
||||
throw new RuntimeException("unexpected notification:"
|
||||
+ " FramePop expected: " + (framePopExpected ? "yes" : "no")
|
||||
+ ", actually received: " + (actual ? "yes" : "no"));
|
||||
+ " FramePop expected: " + framePopExpected
|
||||
+ ", actually received: " + actual);
|
||||
}
|
||||
log(" - OK (" + (actual ? "received" : "NOT received") + ")");
|
||||
log(" - OK (received: " + actual + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -44,7 +44,7 @@ extern "C" {
|
||||
static jvmtiEnv *jvmti = NULL;
|
||||
static jvmtiCapabilities caps;
|
||||
static jvmtiEventCallbacks callbacks;
|
||||
static jboolean framePopReceived = JNI_FALSE;
|
||||
static jint framePopsReceived = 0;
|
||||
|
||||
static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
|
||||
|
||||
@ -80,7 +80,7 @@ FramePop(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread,
|
||||
char* name = NULL;
|
||||
char* sign = NULL;
|
||||
|
||||
framePopReceived = JNI_TRUE;
|
||||
++framePopsReceived;
|
||||
|
||||
err = (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method, &cls);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
@ -155,16 +155,21 @@ Java_NotifyFramePopTest_setFramePopNotificationMode(JNIEnv *env, jclass cl, jboo
|
||||
JNIEXPORT void JNICALL
|
||||
Java_NotifyFramePopTest_notifyFramePop(JNIEnv *env, jclass cls, jthread thread)
|
||||
{
|
||||
jvmtiError err= (*jvmti)->NotifyFramePop(jvmti, thread, 1);
|
||||
// Request two FramePop events at depth 1 and 2.
|
||||
jvmtiError err = (*jvmti)->NotifyFramePop(jvmti, thread, 1);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
reportError("NotifyFramePop failed", err);
|
||||
reportError("NotifyFramePop at depth 1 failed", err);
|
||||
}
|
||||
err = (*jvmti)->NotifyFramePop(jvmti, thread, 2);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
reportError("NotifyFramePop at depth 2 failed", err);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_NotifyFramePopTest_framePopReceived(JNIEnv *env, jclass cls) {
|
||||
jboolean result = framePopReceived;
|
||||
framePopReceived = JNI_FALSE;
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_NotifyFramePopTest_framePopsReceived(JNIEnv *env, jclass cls) {
|
||||
jint result = framePopsReceived;
|
||||
framePopsReceived = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -156,7 +156,9 @@ Java_NotifyFramePopStressTest_notifyFramePop(JNIEnv *jni, jclass cls, jthread th
|
||||
return JNI_FALSE;
|
||||
}
|
||||
check_jvmti_status(jni, err, "notifyFramePop: Failed in JVMTI notifyFramePop");
|
||||
LOG("\nNotifyFramePop called for method %s\n", name);
|
||||
|
||||
LOG("\nnotifyFramePop: requested FramePop event for frame with method: %s\n", name);
|
||||
print_stack_trace(jvmti, jni, thread);
|
||||
|
||||
if (isMain) {
|
||||
LOG("notifyFramePop not counting main method\n");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -44,6 +44,7 @@ public class ThreadStateTest {
|
||||
|
||||
private static native void setSingleSteppingMode(boolean enable);
|
||||
private static native void setMonitorContendedMode(boolean enable);
|
||||
private static native void setFramePopEvent(Thread thread);
|
||||
private static native void testGetThreadState(Thread thread);
|
||||
private static native void testGetThreadListStackTraces(Thread thread);
|
||||
|
||||
@ -51,6 +52,10 @@ public class ThreadStateTest {
|
||||
testGetThreadState(Thread.currentThread());
|
||||
testGetThreadListStackTraces(Thread.currentThread());
|
||||
Thread.yield();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
testGetThreadListStackTraces(Thread.currentThread());
|
||||
Thread.yield();
|
||||
}
|
||||
};
|
||||
|
||||
private void runTest() throws Exception {
|
||||
@ -75,7 +80,13 @@ public class ThreadStateTest {
|
||||
}
|
||||
|
||||
// Give some time for vthreads to finish.
|
||||
Thread.sleep(10);
|
||||
Thread.sleep(50);
|
||||
|
||||
for (Thread t : virtualThreads) {
|
||||
if (tryCount % 4 == 0) {
|
||||
setFramePopEvent(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger race of JvmtiThreadState creation with terminating vthreads.
|
||||
setMonitorContendedMode(false);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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 @@
|
||||
|
||||
// set by Agent_OnLoad
|
||||
static jvmtiEnv* jvmti = nullptr;
|
||||
static jrawMonitorID agent_event_lock = nullptr;
|
||||
static int frame_pops_cnt = 0;
|
||||
static const jint EXP_VT_STATE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE;
|
||||
static const jint EXP_CT_STATE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING |
|
||||
JVMTI_THREAD_STATE_WAITING_INDEFINITELY;
|
||||
@ -41,6 +43,18 @@ SingleStep(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
|
||||
jmethodID method, jlocation location) {
|
||||
}
|
||||
|
||||
static void JNICALL
|
||||
FramePop(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
|
||||
jmethodID method, jboolean by_exception) {
|
||||
const char* tname = get_thread_name(jvmti, jni, thread);
|
||||
const char* mname = get_method_name(jvmti, jni, method);
|
||||
|
||||
RawMonitorLocker event_locker(jvmti, jni, agent_event_lock);
|
||||
LOG("FramePop event #%d: thread: %s method: %s\n", ++frame_pops_cnt, tname, mname);
|
||||
deallocate(jvmti, jni, (void*)tname);
|
||||
deallocate(jvmti, jni, (void*)mname);
|
||||
}
|
||||
|
||||
static void JNICALL
|
||||
MonitorContended(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread,
|
||||
jobject object) {
|
||||
@ -48,7 +62,7 @@ MonitorContended(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread,
|
||||
|
||||
static void JNICALL
|
||||
check_thread_state(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jint state, jint exp_state, const char* msg) {
|
||||
if (state != exp_state) {
|
||||
if ((state & ~JVMTI_THREAD_STATE_SUSPENDED) != exp_state) {
|
||||
const char* tname = get_thread_name(jvmti, jni, thread);
|
||||
|
||||
LOG("FAILED: %p: %s: thread state: %x expected state: %x\n",
|
||||
@ -59,6 +73,33 @@ check_thread_state(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jint state, jin
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ThreadStateTest_setFramePopEvent(JNIEnv* jni, jclass klass, jthread thread) {
|
||||
RawMonitorLocker event_locker(jvmti, jni, agent_event_lock);
|
||||
|
||||
jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thread);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
if (err == JVMTI_ERROR_THREAD_NOT_ALIVE || err == JVMTI_ERROR_NO_MORE_FRAMES) {
|
||||
return;
|
||||
} else {
|
||||
check_jvmti_status(jni, err, "setFramePopEvent error in JVMTI SetEventNotificationMode for JVMTI_EVENT_FRAME_POP");
|
||||
}
|
||||
}
|
||||
err = jvmti->SuspendThread(thread);
|
||||
if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) {
|
||||
return;
|
||||
}
|
||||
check_jvmti_status(jni, err, "setFramePopEvent error in JVMTI SuspendThread");
|
||||
|
||||
err = jvmti->NotifyFramePop(thread, 4);
|
||||
if (err != JVMTI_ERROR_NO_MORE_FRAMES && err != JVMTI_ERROR_OPAQUE_FRAME) {
|
||||
check_jvmti_status(jni, err, "setFramePopEvent error in JVMTI NotifyFramePop");
|
||||
}
|
||||
|
||||
err = jvmti->ResumeThread(thread);
|
||||
check_jvmti_status(jni, err, "setFramePopEvent error in JVMTI ResumeThread");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ThreadStateTest_setSingleSteppingMode(JNIEnv* jni, jclass klass, jboolean enable) {
|
||||
jvmtiError err = jvmti->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, nullptr);
|
||||
@ -111,6 +152,8 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved)
|
||||
|
||||
memset(&caps, 0, sizeof(caps));
|
||||
caps.can_generate_single_step_events = 1;
|
||||
caps.can_generate_frame_pop_events = 1;
|
||||
caps.can_suspend = 1;
|
||||
caps.can_support_virtual_threads = 1;
|
||||
caps.can_generate_monitor_events = 1;
|
||||
|
||||
@ -120,12 +163,14 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved)
|
||||
}
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.SingleStep = &SingleStep;
|
||||
callbacks.SingleStep = &SingleStep;
|
||||
callbacks.FramePop = &FramePop;
|
||||
callbacks.MonitorContendedEnter = &MonitorContended;
|
||||
err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
LOG("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
|
||||
}
|
||||
agent_event_lock = create_raw_monitor(jvmti, "agent_event_lock");
|
||||
printf("Agent_OnLoad: finished\n");
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2953,6 +2953,8 @@ class EAForceEarlyReturnNotInlined extends EATestCaseBaseDebugger {
|
||||
// frame[3]: EATestCaseBaseTarget.run()
|
||||
// frame[4]: EATestsTarget.main(java.lang.String[])
|
||||
|
||||
env.stepOverLine(thread); // needed to keep target thread interp-only, so dontinline_brkpt_iret is not inlined
|
||||
|
||||
msg("Step out");
|
||||
env.stepOut(thread); // return from dontinline_brkpt
|
||||
printStack(thread);
|
||||
|
||||
183
test/jdk/com/sun/jdi/StepOverStressTest.java
Normal file
183
test/jdk/com/sun/jdi/StepOverStressTest.java
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// THIS TEST IS LINE NUMBER SENSITIVE
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 6960970
|
||||
* @summary Avoid interp-only mode during stepping over method calls.
|
||||
*
|
||||
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||
* @run compile -g StepOverStressTest.java
|
||||
* @run driver/timeout=600 StepOverStressTest
|
||||
*/
|
||||
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.event.*;
|
||||
import com.sun.jdi.request.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class TestTarg {
|
||||
public final static int BKPT_LINE1 = 54;
|
||||
public final static int BKPT_LINE2 = 56;
|
||||
|
||||
static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
static int busyWork() {
|
||||
// When we enter this method we hit a breakpoint on the BKPT_LINE1 line below.
|
||||
// The debugger enables single step events and then resume. We time the compute
|
||||
// intensive task in busyWork1 method. Single step events are disabled when
|
||||
// second breakpoint on the BKPT_LINE2 line is hit. This will check if the
|
||||
// thread continues execution in interpOnly mode in the busyWork1 method.
|
||||
int x = 1; // <-- BKPT_LINE1
|
||||
intensiveWork();
|
||||
return x; // <-- BKPT_LINE2
|
||||
}
|
||||
|
||||
// Do something compute intensive and time it.
|
||||
static void intensiveWork() {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int iter = 0; iter < 10; iter++) {
|
||||
LinkedList<Integer> list = new LinkedList<Integer>();
|
||||
for (int i = 0; i < 100 * 1024; i++) {
|
||||
list.addFirst(Integer.valueOf(i));
|
||||
}
|
||||
Collections.sort(list);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
log("Total time #1: " + (end - start));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
long start = System.currentTimeMillis();
|
||||
busyWork();
|
||||
long end = System.currentTimeMillis();
|
||||
log("Total time #2: " + (end - start));
|
||||
}
|
||||
}
|
||||
|
||||
/********** test program **********/
|
||||
|
||||
public class StepOverStressTest extends TestScaffold {
|
||||
ClassType targetClass;
|
||||
ThreadReference mainThread;
|
||||
|
||||
static void log(String msg) { System.out.println(msg); }
|
||||
|
||||
StepOverStressTest (String args[]) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new StepOverStressTest(args).startTests();
|
||||
}
|
||||
|
||||
/********** event handlers **********/
|
||||
|
||||
EventRequestManager erm;
|
||||
BreakpointRequest bkptReq1;
|
||||
BreakpointRequest bkptReq2;
|
||||
StepRequest stepRequest;
|
||||
static boolean wasBreakpointHit = false;
|
||||
|
||||
public void breakpointReached(BreakpointEvent event) {
|
||||
if (!wasBreakpointHit) {
|
||||
log("Got BreakpointEvent #1: " + event);
|
||||
stepRequest = erm.createStepRequest(mainThread,
|
||||
StepRequest.STEP_LINE,
|
||||
StepRequest.STEP_OVER);
|
||||
wasBreakpointHit = true;
|
||||
stepRequest.enable();
|
||||
bkptReq1.disable();
|
||||
} else {
|
||||
log("Got BreakpointEvent #2: " + event);
|
||||
stepRequest.disable();
|
||||
bkptReq2.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public void stepCompleted(StepEvent event) {
|
||||
log("Got StepEvent: " + event);
|
||||
}
|
||||
|
||||
public void eventSetComplete(EventSet set) {
|
||||
set.resume();
|
||||
}
|
||||
|
||||
public void vmDisconnected(VMDisconnectEvent event) {
|
||||
log("Got VMDisconnectEvent");
|
||||
}
|
||||
|
||||
/********** test core **********/
|
||||
|
||||
protected void runTests() throws Exception {
|
||||
// Get to the top of main() to determine targetClass and mainThread.
|
||||
BreakpointEvent bpe = startToMain("TestTarg");
|
||||
targetClass = (ClassType)bpe.location().declaringType();
|
||||
mainThread = bpe.thread();
|
||||
erm = vm().eventRequestManager();
|
||||
|
||||
Location loc1 = findLocation(
|
||||
targetClass,
|
||||
TestTarg.BKPT_LINE1);
|
||||
|
||||
Location loc2 = findLocation(
|
||||
targetClass,
|
||||
TestTarg.BKPT_LINE2);
|
||||
|
||||
bkptReq1 = erm.createBreakpointRequest(loc1);
|
||||
bkptReq2 = erm.createBreakpointRequest(loc2);
|
||||
bkptReq1.enable();
|
||||
bkptReq2.enable();
|
||||
|
||||
try {
|
||||
addListener(this);
|
||||
} catch (Exception ex){
|
||||
ex.printStackTrace();
|
||||
failure("failure: Could not add listener");
|
||||
throw new Exception("StepOverStressTest: failed");
|
||||
}
|
||||
|
||||
vm().resume();
|
||||
while (!vmDisconnected) {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException ee) {
|
||||
}
|
||||
}
|
||||
|
||||
println("done with loop");
|
||||
removeListener(this);
|
||||
|
||||
// Deal with results of test if anything has called failure("foo")
|
||||
// testFailed will be true.
|
||||
if (!testFailed) {
|
||||
println("StepOverStressTest: passed");
|
||||
} else {
|
||||
throw new Exception("StepOverStressTest: failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,6 +279,15 @@ get_frame_count(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) {
|
||||
return frame_count;
|
||||
}
|
||||
|
||||
static jmethodID
|
||||
get_frame_method(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, jint depth) {
|
||||
jmethodID method;
|
||||
jlocation loc;
|
||||
jvmtiError err = jvmti->GetFrameLocation(thread, depth, &method, &loc);
|
||||
check_jvmti_status(jni, err, "notifyFramePop: Failed in JVMTI GetFrameLocation");
|
||||
return method;
|
||||
}
|
||||
|
||||
static jvmtiThreadInfo
|
||||
get_thread_info(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) {
|
||||
jvmtiThreadInfo thr_info;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user