Skip to content

Commit

Permalink
8346143: add ClearAllFramePops function to speedup debugger single st…
Browse files Browse the repository at this point in the history
…epping in some cases

Reviewed-by: cjplummer, amenkov
  • Loading branch information
Serguei Spitsyn committed Jan 10, 2025
1 parent 6f1f2f2 commit 761774a
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 36 deletions.
33 changes: 32 additions & 1 deletion src/hotspot/share/prims/jvmti.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="jvmti.xsl"?>
<!--
Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -3090,6 +3090,34 @@ err = (*jvmti)-&gt;Deallocate(jvmti, stack_info);
</errors>
</function>

<function id="ClearAllFramePops" num="67" since="25">
<synopsis>Clear Frame Pop</synopsis>
<description>
Clear all frame pop requests so that a <eventlink id="FramePop"></eventlink>
event will not be generated for any frames.
See the <eventlink id="FramePop"></eventlink> event for details.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>new</origin>
<capabilities>
<required id="can_generate_frame_pop_events"></required>
</capabilities>
<parameters>
<param id="thread">
<jthread null="current" impl="noconvert"/>
<description>
The thread for which all the frame pop events will be cleared.
</description>
</param>
</parameters>
<errors>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>

</category>

<category id="ForceEarlyReturn" label="Force Early Return">
Expand Down Expand Up @@ -15481,6 +15509,9 @@ typedef void (JNICALL *jvmtiEventVMInit)
Virtual threads finalized to be a permanent feature.
Agent start-up in the live phase now specified to print a warning.
</change>
<change date="10 January 2025" version="25.0.0">
Add new function ClearAllFramePops. Needed to speedup debugger single stepping.
</change>
</changehistory>

</specification>
Expand Down
29 changes: 28 additions & 1 deletion src/hotspot/share/prims/jvmtiEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1804,12 +1804,39 @@ JvmtiEnv::NotifyFramePop(jthread thread, jint depth) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}

SetFramePopClosure op(this, state, depth);
SetOrClearFramePopClosure op(this, state, true /* set */, depth);
MutexLocker mu(current, JvmtiThreadState_lock);
JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle);
return op.result();
} /* end NotifyFramePop */

// Threads_lock NOT held, java_thread not protected by lock
jvmtiError
JvmtiEnv::ClearAllFramePops(jthread thread) {
ResourceMark rm;
JvmtiVTMSTransitionDisabler disabler(thread);
JavaThread* current = JavaThread::current();
ThreadsListHandle tlh(current);

JavaThread* java_thread = nullptr;
oop thread_obj = nullptr;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, current, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}

HandleMark hm(current);
Handle thread_handle(current, thread_obj);
JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread, thread_handle);
if (state == nullptr) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}

SetOrClearFramePopClosure op(this, state, false /* clear all frame pops*/);
MutexLocker mu(current, JvmtiThreadState_lock);
JvmtiHandshake::execute(&op, &tlh, java_thread, thread_handle);
return op.result();
} /* end ClearAllFramePops */

//
// Force Early Return functions
Expand Down
25 changes: 20 additions & 5 deletions src/hotspot/share/prims/jvmtiEnvBase.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -1367,6 +1367,13 @@ JvmtiEnvBase::set_frame_pop(JvmtiThreadState* state, javaVFrame* jvf, jint depth
return JVMTI_ERROR_NONE;
}

jvmtiError
JvmtiEnvBase::clear_all_frame_pops(JvmtiThreadState* state) {
JvmtiEnvThreadState* ets = state->env_thread_state(this);
ets->clear_all_frame_pops();
return JVMTI_ERROR_NONE;
}

bool
JvmtiEnvBase::is_cthread_with_mounted_vthread(JavaThread* jt) {
oop thread_oop = jt->threadObj();
Expand Down Expand Up @@ -2482,7 +2489,7 @@ UpdateForPopTopFrameClosure::doit(Thread *target) {
}

void
SetFramePopClosure::do_thread(Thread *target) {
SetOrClearFramePopClosure::do_thread(Thread *target) {
Thread* current = Thread::current();
ResourceMark rm(current); // vframes are resource allocated
JavaThread* java_thread = JavaThread::cast(target);
Expand All @@ -2495,6 +2502,10 @@ SetFramePopClosure::do_thread(Thread *target) {
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
return;
}
if (!_set) { // ClearAllFramePops
_result = _env->clear_all_frame_pops(_state);
return;
}
if (!java_thread->has_last_Java_frame()) {
_result = JVMTI_ERROR_NO_MORE_FRAMES;
return;
Expand All @@ -2506,20 +2517,24 @@ SetFramePopClosure::do_thread(Thread *target) {
RegisterMap::ProcessFrames::skip,
RegisterMap::WalkContinuation::include);
javaVFrame* jvf = JvmtiEnvBase::get_cthread_last_java_vframe(java_thread, &reg_map);
_result = ((JvmtiEnvBase*)_env)->set_frame_pop(_state, jvf, _depth);
_result = _env->set_frame_pop(_state, jvf, _depth);
}

void
SetFramePopClosure::do_vthread(Handle target_h) {
SetOrClearFramePopClosure::do_vthread(Handle target_h) {
Thread* current = Thread::current();
ResourceMark rm(current); // vframes are resource allocated

if (!_self && !JvmtiVTSuspender::is_vthread_suspended(target_h())) {
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
return;
}
if (!_set) { // ClearAllFramePops
_result = _env->clear_all_frame_pops(_state);
return;
}
javaVFrame *jvf = JvmtiEnvBase::get_vthread_jvf(target_h());
_result = ((JvmtiEnvBase*)_env)->set_frame_pop(_state, jvf, _depth);
_result = _env->set_frame_pop(_state, jvf, _depth);
}

void
Expand Down
17 changes: 10 additions & 7 deletions src/hotspot/share/prims/jvmtiEnvBase.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -410,6 +410,7 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
jvmtiError get_frame_location(oop vthread_oop, jint depth,
jmethodID* method_ptr, jlocation* location_ptr);
jvmtiError set_frame_pop(JvmtiThreadState* state, javaVFrame* jvf, jint depth);
jvmtiError clear_all_frame_pops(JvmtiThreadState* state);
jvmtiError get_object_monitor_usage(JavaThread* calling_thread,
jobject object, jvmtiMonitorUsage* info_ptr);
jvmtiError get_stack_trace(javaVFrame* jvf,
Expand Down Expand Up @@ -534,17 +535,19 @@ class UpdateForPopTopFrameClosure : public JvmtiUnitedHandshakeClosure {
};

// HandshakeClosure to set frame pop.
class SetFramePopClosure : public JvmtiUnitedHandshakeClosure {
class SetOrClearFramePopClosure : public JvmtiUnitedHandshakeClosure {
private:
JvmtiEnv *_env;
JvmtiEnvBase *_env;
JvmtiThreadState* _state;
jint _depth;
bool _set;
jint _depth; // used for NotiftyFramePop only

public:
SetFramePopClosure(JvmtiEnv *env, JvmtiThreadState* state, jint depth)
: JvmtiUnitedHandshakeClosure("SetFramePopClosure"),
_env(env),
SetOrClearFramePopClosure(JvmtiEnv *env, JvmtiThreadState* state, bool set, jint depth = 0)
: JvmtiUnitedHandshakeClosure("SetOrClearFramePopClosure"),
_env((JvmtiEnvBase*)env),
_state(state),
_set(set),
_depth(depth) {}
void do_thread(Thread *target);
void do_vthread(Handle target_h);
Expand Down
31 changes: 14 additions & 17 deletions src/hotspot/share/prims/jvmtiEnvThreadState.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -71,6 +71,10 @@ JvmtiFramePops::clear(JvmtiFramePop& fp) {
_pops->remove(fp.frame_number());
}

void
JvmtiFramePops::clear_all() {
_pops->clear();
}

int
JvmtiFramePops::clear_to(JvmtiFramePop& fp) {
Expand Down Expand Up @@ -212,10 +216,7 @@ void JvmtiEnvThreadState::compare_and_set_current_location(Method* new_method,


JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() {
#ifdef ASSERT
Thread *current = Thread::current();
#endif
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
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 (_frame_pops == nullptr) {
_frame_pops = new JvmtiFramePops();
Expand All @@ -230,32 +231,28 @@ bool JvmtiEnvThreadState::has_frame_pops() {
}

void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
#ifdef ASSERT
Thread *current = Thread::current();
#endif
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
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");
JvmtiFramePop fpop(frame_number);
JvmtiEventController::set_frame_pop(this, fpop);
}


void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
#ifdef ASSERT
Thread *current = Thread::current();
#endif
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
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");
JvmtiFramePop fpop(frame_number);
JvmtiEventController::clear_frame_pop(this, fpop);
}

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");
JvmtiEventController::clear_all_frame_pops(this);
}

bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
#ifdef ASSERT
Thread *current = Thread::current();
#endif
assert(get_thread() == nullptr || get_thread()->is_handshake_safe_for(current),
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) {
return false;
Expand Down
4 changes: 3 additions & 1 deletion src/hotspot/share/prims/jvmtiEnvThreadState.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -86,6 +86,7 @@ class JvmtiFramePops : public CHeapObj<mtInternal> {
friend class JvmtiEventControllerPrivate;
void set(JvmtiFramePop& fp);
void clear(JvmtiFramePop& fp);
void clear_all();
int clear_to(JvmtiFramePop& fp);

public:
Expand Down Expand Up @@ -184,6 +185,7 @@ class JvmtiEnvThreadState : public CHeapObj<mtInternal> {

void set_frame_pop(int frame_number);
void clear_frame_pop(int frame_number);
void clear_all_frame_pops();

};

Expand Down
18 changes: 17 additions & 1 deletion src/hotspot/share/prims/jvmtiEventController.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -312,6 +312,7 @@ class JvmtiEventControllerPrivate : public AllStatic {

static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
static void clear_all_frame_pops(JvmtiEnvThreadState *env_thread);
static void clear_to_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
static void change_field_watch(jvmtiEvent event_type, bool added);

Expand Down Expand Up @@ -946,6 +947,15 @@ JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFram
recompute_thread_enabled(ets->jvmti_thread_state());
}

void
JvmtiEventControllerPrivate::clear_all_frame_pops(JvmtiEnvThreadState *ets) {
EC_TRACE(("[%s] # clear all frame pops",
JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved())
));

ets->get_frame_pops()->clear_all();
recompute_thread_enabled(ets->jvmti_thread_state());
}

void
JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
Expand Down Expand Up @@ -1125,6 +1135,12 @@ JvmtiEventController::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fp
JvmtiEventControllerPrivate::clear_frame_pop(ets, fpop);
}

void
JvmtiEventController::clear_all_frame_pops(JvmtiEnvThreadState *ets) {
assert(JvmtiThreadState_lock->is_locked(), "Must be locked.");
JvmtiEventControllerPrivate::clear_all_frame_pops(ets);
}

void
JvmtiEventController::change_field_watch(jvmtiEvent event_type, bool added) {
MutexLocker mu(JvmtiThreadState_lock);
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/prims/jvmtiEventController.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -231,6 +231,7 @@ class JvmtiEventController : AllStatic {
static void enter_interp_only_mode(JvmtiThreadState* state);
static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
static void clear_all_frame_pops(JvmtiEnvThreadState *env_thread);

static void change_field_watch(jvmtiEvent event_type, bool added);

Expand Down
7 changes: 5 additions & 2 deletions src/hotspot/share/prims/jvmtiExport.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -1969,7 +1969,10 @@ void JvmtiExport::post_method_exit_inner(JavaThread* thread,
// remove the frame's entry
{
MutexLocker mu(JvmtiThreadState_lock);
ets->clear_frame_pop(cur_frame_number);
// Need to recheck the condition as the JVMTI ClearAllFramePops can do its work at a safepoint.
if (ets->is_frame_pop(cur_frame_number)) {
ets->clear_frame_pop(cur_frame_number);
}
}
}
}
Expand Down
Loading

0 comments on commit 761774a

Please sign in to comment.