8242427: JVMTI frame pop operations should use Thread-Local Handshakes

Reviewed-by: dcubed, dholmes, pchilanomate, rehn, sspitsyn
This commit is contained in:
Yasumasa Suenaga 2020-09-05 12:42:05 +09:00
parent 5d2e79e2c3
commit 3e81b3ad48
11 changed files with 107 additions and 87 deletions

View File

@ -1714,15 +1714,18 @@ JvmtiEnv::PopFrame(JavaThread* java_thread) {
// shall be posted for this PopFrame. // shall be posted for this PopFrame.
// It is only safe to perform the direct operation on the current // It is only safe to perform the direct operation on the current
// thread. All other usage needs to use a vm-safepoint-op for safety. // thread. All other usage needs to use a handshake for safety.
if (java_thread == JavaThread::current()) { {
state->update_for_pop_top_frame(); MutexLocker mu(JvmtiThreadState_lock);
} else { if (java_thread == JavaThread::current()) {
VM_UpdateForPopTopFrame op(state); state->update_for_pop_top_frame();
VMThread::execute(&op); } else {
jvmtiError err = op.result(); UpdateForPopTopFrameClosure op(state);
if (err != JVMTI_ERROR_NONE) { bool executed = Handshake::execute_direct(&op, java_thread);
return err; jvmtiError err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
if (err != JVMTI_ERROR_NONE) {
return err;
}
} }
} }
@ -1796,13 +1799,14 @@ JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
// It is only safe to perform the direct operation on the current // It is only safe to perform the direct operation on the current
// thread. All other usage needs to use a vm-safepoint-op for safety. // thread. All other usage needs to use a vm-safepoint-op for safety.
MutexLocker mu(JvmtiThreadState_lock);
if (java_thread == JavaThread::current()) { if (java_thread == JavaThread::current()) {
int frame_number = state->count_frames() - depth; int frame_number = state->count_frames() - depth;
state->env_thread_state(this)->set_frame_pop(frame_number); state->env_thread_state(this)->set_frame_pop(frame_number);
} else { } else {
VM_SetFramePop op(this, state, depth); SetFramePopClosure op(this, state, depth);
VMThread::execute(&op); bool executed = Handshake::execute_direct(&op, java_thread);
err = op.result(); err = executed ? op.result() : JVMTI_ERROR_THREAD_NOT_ALIVE;
} }
return err; return err;
} /* end NotifyFramePop */ } /* end NotifyFramePop */

View File

@ -1504,25 +1504,23 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje
} }
void void
VM_UpdateForPopTopFrame::doit() { UpdateForPopTopFrameClosure::do_thread(Thread *target) {
JavaThread* jt = _state->get_thread(); JavaThread* jt = _state->get_thread();
ThreadsListHandle tlh; assert(jt == target, "just checking");
if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { if (!jt->is_exiting() && jt->threadObj() != NULL) {
_state->update_for_pop_top_frame(); _state->update_for_pop_top_frame();
} else { _result = JVMTI_ERROR_NONE;
_result = JVMTI_ERROR_THREAD_NOT_ALIVE;
} }
} }
void void
VM_SetFramePop::doit() { SetFramePopClosure::do_thread(Thread *target) {
JavaThread* jt = _state->get_thread(); JavaThread* jt = _state->get_thread();
ThreadsListHandle tlh; assert(jt == target, "just checking");
if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { if (!jt->is_exiting() && jt->threadObj() != NULL) {
int frame_number = _state->count_frames() - _depth; int frame_number = _state->count_frames() - _depth;
_state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number); _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
} else { _result = JVMTI_ERROR_NONE;
_result = JVMTI_ERROR_THREAD_NOT_ALIVE;
} }
} }

View File

@ -336,24 +336,23 @@ class JvmtiEnvIterator : public StackObj {
JvmtiEnv* next(JvmtiEnvBase* env) { return env->next_environment(); } JvmtiEnv* next(JvmtiEnvBase* env) { return env->next_environment(); }
}; };
// VM operation to update for pop top frame. // HandshakeClosure to update for pop top frame.
class VM_UpdateForPopTopFrame : public VM_Operation { class UpdateForPopTopFrameClosure : public HandshakeClosure {
private: private:
JvmtiThreadState* _state; JvmtiThreadState* _state;
jvmtiError _result; jvmtiError _result;
public: public:
VM_UpdateForPopTopFrame(JvmtiThreadState* state) { UpdateForPopTopFrameClosure(JvmtiThreadState* state)
_state = state; : HandshakeClosure("UpdateForPopTopFrame"),
_result = JVMTI_ERROR_NONE; _state(state),
} _result(JVMTI_ERROR_THREAD_NOT_ALIVE) {}
VMOp_Type type() const { return VMOp_UpdateForPopTopFrame; }
jvmtiError result() { return _result; } jvmtiError result() { return _result; }
void doit(); void do_thread(Thread *target);
}; };
// VM operation to set frame pop. // HandshakeClosure to set frame pop.
class VM_SetFramePop : public VM_Operation { class SetFramePopClosure : public HandshakeClosure {
private: private:
JvmtiEnv *_env; JvmtiEnv *_env;
JvmtiThreadState* _state; JvmtiThreadState* _state;
@ -361,15 +360,14 @@ private:
jvmtiError _result; jvmtiError _result;
public: public:
VM_SetFramePop(JvmtiEnv *env, JvmtiThreadState* state, jint depth) { SetFramePopClosure(JvmtiEnv *env, JvmtiThreadState* state, jint depth)
_env = env; : HandshakeClosure("SetFramePop"),
_state = state; _env(env),
_depth = depth; _state(state),
_result = JVMTI_ERROR_NONE; _depth(depth),
} _result(JVMTI_ERROR_THREAD_NOT_ALIVE) {}
VMOp_Type type() const { return VMOp_SetFramePop; }
jvmtiError result() { return _result; } jvmtiError result() { return _result; }
void doit(); void do_thread(Thread *target);
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -191,8 +191,11 @@ void JvmtiEnvThreadState::compare_and_set_current_location(Method* new_method,
JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() { JvmtiFramePops* JvmtiEnvThreadState::get_frame_pops() {
assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(), #ifdef ASSERT
"frame pop data only accessible from same thread or at safepoint"); Thread *current = Thread::current();
#endif
assert(get_thread() == current || current == get_thread()->active_handshaker(),
"frame pop data only accessible from same thread or direct handshake");
if (_frame_pops == NULL) { if (_frame_pops == NULL) {
_frame_pops = new JvmtiFramePops(); _frame_pops = new JvmtiFramePops();
assert(_frame_pops != NULL, "_frame_pops != NULL"); assert(_frame_pops != NULL, "_frame_pops != NULL");
@ -206,32 +209,44 @@ bool JvmtiEnvThreadState::has_frame_pops() {
} }
void JvmtiEnvThreadState::set_frame_pop(int frame_number) { void JvmtiEnvThreadState::set_frame_pop(int frame_number) {
assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(), #ifdef ASSERT
"frame pop data only accessible from same thread or at safepoint"); Thread *current = Thread::current();
#endif
assert(get_thread() == current || current == get_thread()->active_handshaker(),
"frame pop data only accessible from same thread or direct handshake");
JvmtiFramePop fpop(frame_number); JvmtiFramePop fpop(frame_number);
JvmtiEventController::set_frame_pop(this, fpop); JvmtiEventController::set_frame_pop(this, fpop);
} }
void JvmtiEnvThreadState::clear_frame_pop(int frame_number) { void JvmtiEnvThreadState::clear_frame_pop(int frame_number) {
assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(), #ifdef ASSERT
"frame pop data only accessible from same thread or at safepoint"); Thread *current = Thread::current();
#endif
assert(get_thread() == current || current == get_thread()->active_handshaker(),
"frame pop data only accessible from same thread or direct handshake");
JvmtiFramePop fpop(frame_number); JvmtiFramePop fpop(frame_number);
JvmtiEventController::clear_frame_pop(this, fpop); JvmtiEventController::clear_frame_pop(this, fpop);
} }
void JvmtiEnvThreadState::clear_to_frame_pop(int frame_number) { void JvmtiEnvThreadState::clear_to_frame_pop(int frame_number) {
assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(), #ifdef ASSERT
"frame pop data only accessible from same thread or at safepoint"); Thread *current = Thread::current();
#endif
assert(get_thread() == current || current == get_thread()->active_handshaker(),
"frame pop data only accessible from same thread or direct handshake");
JvmtiFramePop fpop(frame_number); JvmtiFramePop fpop(frame_number);
JvmtiEventController::clear_to_frame_pop(this, fpop); JvmtiEventController::clear_to_frame_pop(this, fpop);
} }
bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) { bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
assert(get_thread() == Thread::current() || SafepointSynchronize::is_at_safepoint(), #ifdef ASSERT
"frame pop data only accessible from same thread or at safepoint"); Thread *current = Thread::current();
#endif
assert(get_thread() == current || current == get_thread()->active_handshaker(),
"frame pop data only accessible from same thread or direct handshake");
if (!get_thread()->is_interp_only_mode() || _frame_pops == NULL) { if (!get_thread()->is_interp_only_mode() || _frame_pops == NULL) {
return false; return false;
} }
@ -240,25 +255,25 @@ bool JvmtiEnvThreadState::is_frame_pop(int cur_frame_number) {
} }
class VM_GetCurrentLocation : public VM_Operation { class GetCurrentLocationClosure : public HandshakeClosure {
private: private:
JavaThread *_thread;
jmethodID _method_id; jmethodID _method_id;
int _bci; int _bci;
public: public:
VM_GetCurrentLocation(JavaThread *thread) { GetCurrentLocationClosure()
_thread = thread; : HandshakeClosure("GetCurrentLocation"),
} _method_id(NULL),
VMOp_Type type() const { return VMOp_GetCurrentLocation; } _bci(0) {}
void doit() { void do_thread(Thread *target) {
ResourceMark rmark; // _thread != Thread::current() JavaThread *jt = (JavaThread *)target;
RegisterMap rm(_thread, false); ResourceMark rmark; // jt != Thread::current()
RegisterMap rm(jt, false);
// There can be a race condition between a VM_Operation reaching a safepoint // There can be a race condition between a VM_Operation reaching a safepoint
// and the target thread exiting from Java execution. // and the target thread exiting from Java execution.
// We must recheck the last Java frame still exists. // We must recheck the last Java frame still exists.
if (!_thread->is_exiting() && _thread->has_last_Java_frame()) { if (!jt->is_exiting() && jt->has_last_Java_frame()) {
javaVFrame* vf = _thread->last_java_vframe(&rm); javaVFrame* vf = jt->last_java_vframe(&rm);
assert(vf != NULL, "must have last java frame"); assert(vf != NULL, "must have last java frame");
Method* method = vf->method(); Method* method = vf->method();
_method_id = method->jmethod_id(); _method_id = method->jmethod_id();
@ -307,9 +322,15 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena
jmethodID method_id; jmethodID method_id;
int bci; int bci;
// The java thread stack may not be walkable for a running thread // The java thread stack may not be walkable for a running thread
// so get current location at safepoint. // so get current location with direct handshake.
VM_GetCurrentLocation op(_thread); GetCurrentLocationClosure op;
VMThread::execute(&op); Thread *current = Thread::current();
if (current == _thread || _thread->active_handshaker() == current) {
op.do_thread(_thread);
} else {
bool executed = Handshake::execute_direct(&op, _thread);
guarantee(executed, "Direct handshake failed. Target thread is not alive?");
}
op.get_current_location(&method_id, &bci); op.get_current_location(&method_id, &bci);
set_current_location(method_id, bci); set_current_location(method_id, bci);
} }

View File

@ -331,10 +331,14 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state
EC_TRACE(("[%s] # Entering interpreter only mode", EC_TRACE(("[%s] # Entering interpreter only mode",
JvmtiTrace::safe_get_thread_name(state->get_thread()))); JvmtiTrace::safe_get_thread_name(state->get_thread())));
EnterInterpOnlyModeClosure hs; EnterInterpOnlyModeClosure hs;
if (SafepointSynchronize::is_at_safepoint()) { assert(state->get_thread()->is_Java_thread(), "just checking");
hs.do_thread(state->get_thread()); JavaThread *target = (JavaThread *)state->get_thread();
Thread *current = Thread::current();
if (target == current || target->active_handshaker() == current) {
hs.do_thread(target);
} else { } else {
Handshake::execute_direct(&hs, state->get_thread()); bool executed = Handshake::execute_direct(&hs, target);
guarantee(executed, "Direct handshake failed. Target thread is not alive?");
} }
} }
@ -980,21 +984,21 @@ JvmtiEventController::set_extension_event_callback(JvmtiEnvBase *env,
void void
JvmtiEventController::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { JvmtiEventController::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
MutexLocker mu(SafepointSynchronize::is_at_safepoint() ? NULL : JvmtiThreadState_lock); assert_lock_strong(JvmtiThreadState_lock);
JvmtiEventControllerPrivate::set_frame_pop(ets, fpop); JvmtiEventControllerPrivate::set_frame_pop(ets, fpop);
} }
void void
JvmtiEventController::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { JvmtiEventController::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
MutexLocker mu(SafepointSynchronize::is_at_safepoint() ? NULL : JvmtiThreadState_lock); assert_lock_strong(JvmtiThreadState_lock);
JvmtiEventControllerPrivate::clear_frame_pop(ets, fpop); JvmtiEventControllerPrivate::clear_frame_pop(ets, fpop);
} }
void void
JvmtiEventController::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) { JvmtiEventController::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
MutexLocker mu(SafepointSynchronize::is_at_safepoint() ? NULL : JvmtiThreadState_lock); assert_lock_strong(JvmtiThreadState_lock);
JvmtiEventControllerPrivate::clear_to_frame_pop(ets, fpop); JvmtiEventControllerPrivate::clear_to_frame_pop(ets, fpop);
} }

View File

@ -1645,7 +1645,10 @@ void JvmtiExport::post_method_exit(JavaThread *thread, Method* method, frame cur
} }
} }
// remove the frame's entry // remove the frame's entry
ets->clear_frame_pop(cur_frame_number); {
MutexLocker mu(JvmtiThreadState_lock);
ets->clear_frame_pop(cur_frame_number);
}
} }
} }
} }

View File

@ -272,9 +272,9 @@ void JvmtiThreadState::decr_cur_stack_depth() {
} }
int JvmtiThreadState::cur_stack_depth() { int JvmtiThreadState::cur_stack_depth() {
guarantee(SafepointSynchronize::is_at_safepoint() || Thread *current = Thread::current();
(JavaThread *)Thread::current() == get_thread(), guarantee(current == get_thread() || current == get_thread()->active_handshaker(),
"must be current thread or at safepoint"); "must be current thread or direct handshake");
if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) { if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) {
_cur_stack_depth = count_frames(); _cur_stack_depth = count_frames();

View File

@ -383,9 +383,9 @@ HandshakeState::HandshakeState() :
_operation_direct(NULL), _operation_direct(NULL),
_handshake_turn_sem(1), _handshake_turn_sem(1),
_processing_sem(1), _processing_sem(1),
_thread_in_process_handshake(false) _thread_in_process_handshake(false),
_active_handshaker(NULL)
{ {
DEBUG_ONLY(_active_handshaker = NULL;)
} }
void HandshakeState::set_operation(HandshakeOperation* op) { void HandshakeState::set_operation(HandshakeOperation* op) {
@ -510,9 +510,9 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* op
if (can_process_handshake()) { if (can_process_handshake()) {
guarantee(!_processing_sem.trywait(), "we should already own the semaphore"); guarantee(!_processing_sem.trywait(), "we should already own the semaphore");
log_trace(handshake)("Processing handshake by %s", Thread::current()->is_VM_thread() ? "VMThread" : "Handshaker"); log_trace(handshake)("Processing handshake by %s", Thread::current()->is_VM_thread() ? "VMThread" : "Handshaker");
DEBUG_ONLY(_active_handshaker = Thread::current();) _active_handshaker = Thread::current();
op->do_handshake(_handshakee); op->do_handshake(_handshakee);
DEBUG_ONLY(_active_handshaker = NULL;) _active_handshaker = NULL;
// Disarm after we have executed the operation. // Disarm after we have executed the operation.
clear_handshake(is_direct); clear_handshake(is_direct);
pr = _success; pr = _success;

View File

@ -106,11 +106,8 @@ public:
}; };
ProcessResult try_process(HandshakeOperation* op); ProcessResult try_process(HandshakeOperation* op);
#ifdef ASSERT
Thread* _active_handshaker; Thread* _active_handshaker;
Thread* active_handshaker() const { return _active_handshaker; } Thread* active_handshaker() const { return _active_handshaker; }
#endif
}; };
#endif // SHARE_RUNTIME_HANDSHAKE_HPP #endif // SHARE_RUNTIME_HANDSHAKE_HPP

View File

@ -1365,11 +1365,9 @@ class JavaThread: public Thread {
return _handshake.try_process(op); return _handshake.try_process(op);
} }
#ifdef ASSERT
Thread* active_handshaker() const { Thread* active_handshaker() const {
return _handshake.active_handshaker(); return _handshake.active_handshaker();
} }
#endif
// Suspend/resume support for JavaThread // Suspend/resume support for JavaThread
private: private:

View File

@ -76,14 +76,11 @@
template(PopulateDumpSharedSpace) \ template(PopulateDumpSharedSpace) \
template(JNIFunctionTableCopier) \ template(JNIFunctionTableCopier) \
template(RedefineClasses) \ template(RedefineClasses) \
template(UpdateForPopTopFrame) \
template(SetFramePop) \
template(GetObjectMonitorUsage) \ template(GetObjectMonitorUsage) \
template(GetAllStackTraces) \ template(GetAllStackTraces) \
template(GetThreadListStackTraces) \ template(GetThreadListStackTraces) \
template(ChangeBreakpoints) \ template(ChangeBreakpoints) \
template(GetOrSetLocal) \ template(GetOrSetLocal) \
template(GetCurrentLocation) \
template(ChangeSingleStep) \ template(ChangeSingleStep) \
template(HeapWalkOperation) \ template(HeapWalkOperation) \
template(HeapIterateOperation) \ template(HeapIterateOperation) \