8212160: JVMTI agent crashes with "assert(_value != 0LL) failed: resolving NULL _value"

Add local deferred event list to thread to post events outside CodeCache_lock.

Reviewed-by: eosterlund, dholmes, sspitsyn
This commit is contained in:
Coleen Phillimore 2019-12-05 16:57:17 -05:00
parent 730d0ecf19
commit 8846a8043d
15 changed files with 297 additions and 90 deletions

View file

@ -49,6 +49,7 @@
#include "oops/methodData.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "runtime/atomic.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/flags/flagSetting.hpp"
@ -58,6 +59,7 @@
#include "runtime/orderAccess.hpp"
#include "runtime/os.hpp"
#include "runtime/safepointVerifiers.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/sweeper.hpp"
#include "runtime/vmThread.hpp"
@ -428,7 +430,8 @@ void nmethod::init_defaults() {
_has_flushed_dependencies = 0;
_lock_count = 0;
_stack_traversal_mark = 0;
_unload_reported = false; // jvmti state
_load_reported = false; // jvmti state
_unload_reported = false;
_is_far_code = false; // nmethods are located in CodeCache
#ifdef ASSERT
@ -436,7 +439,6 @@ void nmethod::init_defaults() {
#endif
_oops_do_mark_link = NULL;
_jmethod_id = NULL;
_osr_link = NULL;
#if INCLUDE_RTM_OPT
_rtm_state = NoRTM;
@ -1563,11 +1565,11 @@ void nmethod::flush_dependencies(bool delete_immediately) {
// post_compiled_method_load_event
// new method for install_code() path
// Transfer information from compilation to jvmti
void nmethod::post_compiled_method_load_event() {
void nmethod::post_compiled_method_load_event(JvmtiThreadState* state) {
// This is a bad time for a safepoint. We don't want
// this nmethod to get unloaded while we're queueing the event.
NoSafepointVerifier nsv;
// This is a bad time for a safepoint. We don't want
// this nmethod to get unloaded while we're queueing the event.
NoSafepointVerifier nsv;
Method* m = method();
HOTSPOT_COMPILED_METHOD_LOAD(
@ -1579,26 +1581,22 @@ void nmethod::post_compiled_method_load_event() {
m->signature()->utf8_length(),
insts_begin(), insts_size());
if (JvmtiExport::should_post_compiled_method_load() ||
JvmtiExport::should_post_compiled_method_unload()) {
get_and_cache_jmethod_id();
}
if (JvmtiExport::should_post_compiled_method_load()) {
// Let the Service thread (which is a real Java thread) post the event
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
JvmtiDeferredEventQueue::enqueue(
JvmtiDeferredEvent::compiled_method_load_event(this));
}
}
// Only post unload events if load events are found.
set_load_reported();
// Keep sweeper from turning this into zombie until it is posted.
mark_as_seen_on_stack();
jmethodID nmethod::get_and_cache_jmethod_id() {
if (_jmethod_id == NULL) {
// Cache the jmethod_id since it can no longer be looked up once the
// method itself has been marked for unloading.
_jmethod_id = method()->jmethod_id();
// If a JavaThread hasn't been passed in, let the Service thread
// (which is a real Java thread) post the event
JvmtiDeferredEvent event = JvmtiDeferredEvent::compiled_method_load_event(this);
if (state == NULL) {
ServiceThread::enqueue_deferred_event(&event);
} else {
state->enqueue_event(&event);
}
}
return _jmethod_id;
}
void nmethod::post_compiled_method_unload() {
@ -1614,17 +1612,17 @@ void nmethod::post_compiled_method_unload() {
// If a JVMTI agent has enabled the CompiledMethodUnload event then
// post the event. Sometime later this nmethod will be made a zombie
// by the sweeper but the Method* will not be valid at that point.
// If the _jmethod_id is null then no load event was ever requested
// so don't bother posting the unload. The main reason for this is
// that the jmethodID is a weak reference to the Method* so if
// The jmethodID is a weak reference to the Method* so if
// it's being unloaded there's no way to look it up since the weak
// ref will have been cleared.
if (_jmethod_id != NULL && JvmtiExport::should_post_compiled_method_unload()) {
// Don't bother posting the unload if the load event wasn't posted.
if (load_reported() && JvmtiExport::should_post_compiled_method_unload()) {
assert(!unload_reported(), "already unloaded");
JvmtiDeferredEvent event =
JvmtiDeferredEvent::compiled_method_unload_event(_jmethod_id, insts_begin());
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
JvmtiDeferredEventQueue::enqueue(event);
JvmtiDeferredEvent::compiled_method_unload_event(
method()->jmethod_id(), insts_begin());
ServiceThread::enqueue_deferred_event(&event);
}
// The JVMTI CompiledMethodUnload event can be enabled or disabled at