6965671: fatal error: acquiring lock JNIGlobalHandle_lock/16 out of order with lock CodeCache_lock/1
Reviewed-by: kvn, dcubed
--- a/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Tue Jun 29 16:09:57 2010 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Fri Jul 02 15:01:47 2010 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2010, 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
@@ -118,34 +118,13 @@
for (int i=0; i<_global_code_blobs->length(); i++) {
JvmtiCodeBlobDesc* scb = _global_code_blobs->at(i);
if (addr == scb->code_begin()) {
+ ShouldNotReachHere();
return;
}
}
- // we must name the CodeBlob - some CodeBlobs already have names :-
- // - stubs used by compiled code to call a (static) C++ runtime routine
- // - non-relocatable machine code such as the interpreter, stubroutines, etc.
- // - various singleton blobs
- //
- // others are unnamed so we create a name :-
- // - OSR adapter (interpreter frame that has been on-stack replaced)
- // - I2C and C2I adapters
- const char* name = NULL;
- if (cb->is_runtime_stub()) {
- name = ((RuntimeStub*)cb)->name();
- }
- if (cb->is_buffer_blob()) {
- name = ((BufferBlob*)cb)->name();
- }
- if (cb->is_deoptimization_stub() || cb->is_safepoint_stub()) {
- name = ((SingletonBlob*)cb)->name();
- }
- if (cb->is_uncommon_trap_stub() || cb->is_exception_stub()) {
- name = ((SingletonBlob*)cb)->name();
- }
-
// record the CodeBlob details as a JvmtiCodeBlobDesc
- JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(name, cb->instructions_begin(),
+ JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(cb->name(), cb->instructions_begin(),
cb->instructions_end());
_global_code_blobs->append(scb);
}
@@ -197,7 +176,10 @@
jvmtiError JvmtiCodeBlobEvents::generate_dynamic_code_events(JvmtiEnv* env) {
CodeBlobCollector collector;
- // first collect all the code blobs
+ // First collect all the code blobs. This has to be done in a
+ // single pass over the code cache with CodeCache_lock held because
+ // there isn't any safe way to iterate over regular CodeBlobs since
+ // they can be freed at any point.
{
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
collector.collect();
@@ -213,166 +195,28 @@
}
-// Support class to describe a nmethod in the CodeCache
-
-class nmethodDesc: public CHeapObj {
- private:
- jmethodID _jmethod_id;
- address _code_begin;
- address _code_end;
- jvmtiAddrLocationMap* _map;
- jint _map_length;
- public:
- nmethodDesc(jmethodID jmethod_id, address code_begin, address code_end,
- jvmtiAddrLocationMap* map, jint map_length) {
- _jmethod_id = jmethod_id;
- _code_begin = code_begin;
- _code_end = code_end;
- _map = map;
- _map_length = map_length;
- }
- jmethodID jmethod_id() const { return _jmethod_id; }
- address code_begin() const { return _code_begin; }
- address code_end() const { return _code_end; }
- jvmtiAddrLocationMap* map() const { return _map; }
- jint map_length() const { return _map_length; }
-};
-
-
-// Support class to collect a list of the nmethod CodeBlobs in
-// the CodeCache.
-//
-// Usage :-
-//
-// nmethodCollector collector;
-//
-// collector.collect();
-// JvmtiCodeBlobDesc* blob = collector.first();
-// while (blob != NULL) {
-// :
-// blob = collector.next();
-// }
-//
-class nmethodCollector : StackObj {
- private:
- GrowableArray<nmethodDesc*>* _nmethods; // collect nmethods
- int _pos; // iteration support
-
- // used during a collection
- static GrowableArray<nmethodDesc*>* _global_nmethods;
- static void do_nmethod(nmethod* nm);
- public:
- nmethodCollector() {
- _nmethods = NULL;
- _pos = -1;
- }
- ~nmethodCollector() {
- if (_nmethods != NULL) {
- for (int i=0; i<_nmethods->length(); i++) {
- nmethodDesc* blob = _nmethods->at(i);
- if (blob->map()!= NULL) {
- FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, blob->map());
- }
- }
- delete _nmethods;
- }
- }
-
- // collect list of nmethods in the cache
- void collect();
-
- // iteration support - return first code blob
- nmethodDesc* first() {
- assert(_nmethods != NULL, "not collected");
- if (_nmethods->length() == 0) {
- return NULL;
- }
- _pos = 0;
- return _nmethods->at(0);
- }
-
- // iteration support - return next code blob
- nmethodDesc* next() {
- assert(_pos >= 0, "iteration not started");
- if (_pos+1 >= _nmethods->length()) {
- return NULL;
- }
- return _nmethods->at(++_pos);
- }
-};
-
-// used during collection
-GrowableArray<nmethodDesc*>* nmethodCollector::_global_nmethods;
-
-
-// called for each nmethod in the CodeCache
-//
-// This function simply adds a descriptor for each nmethod to the global list.
-
-void nmethodCollector::do_nmethod(nmethod* nm) {
- // ignore zombies
- if (!nm->is_alive()) {
- return;
- }
-
- assert(nm->method() != NULL, "checking");
-
- // create the location map for the nmethod.
- jvmtiAddrLocationMap* map;
- jint map_length;
- JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &map, &map_length);
-
- // record the nmethod details
- nmethodDesc* snm = new nmethodDesc(nm->get_and_cache_jmethod_id(),
- nm->code_begin(),
- nm->code_end(),
- map,
- map_length);
- _global_nmethods->append(snm);
-}
-
-// collects a list of nmethod in the CodeCache.
-//
-// The created list is growable array of nmethodDesc - each one describes
-// a nmethod and includs its JVMTI address location map.
-
-void nmethodCollector::collect() {
- assert_locked_or_safepoint(CodeCache_lock);
- assert(_global_nmethods == NULL, "checking");
-
- // create the list
- _global_nmethods = new (ResourceObj::C_HEAP) GrowableArray<nmethodDesc*>(100,true);
-
- // any a descriptor for each nmethod to the list.
- CodeCache::nmethods_do(do_nmethod);
-
- // make the list the instance list
- _nmethods = _global_nmethods;
- _global_nmethods = NULL;
-}
-
// Generate a COMPILED_METHOD_LOAD event for each nnmethod
-
jvmtiError JvmtiCodeBlobEvents::generate_compiled_method_load_events(JvmtiEnv* env) {
HandleMark hm;
- nmethodCollector collector;
-
- // first collect all nmethods
- {
- MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
- collector.collect();
- }
- // iterate over the list and post an event for each nmethod
- nmethodDesc* nm_desc = collector.first();
- while (nm_desc != NULL) {
- jmethodID mid = nm_desc->jmethod_id();
- assert(mid != NULL, "checking");
- JvmtiExport::post_compiled_method_load(env, mid,
- (jint)(nm_desc->code_end() - nm_desc->code_begin()),
- nm_desc->code_begin(), nm_desc->map_length(),
- nm_desc->map());
- nm_desc = collector.next();
+ // Walk the CodeCache notifying for live nmethods. The code cache
+ // may be changing while this is happening which is ok since newly
+ // created nmethod will notify normally and nmethods which are freed
+ // can be safely skipped.
+ MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ nmethod* current = CodeCache::first_nmethod();
+ while (current != NULL) {
+ // Lock the nmethod so it can't be freed
+ nmethodLocker nml(current);
+
+ // Only notify for live nmethods
+ if (current->is_alive()) {
+ // Don't hold the lock over the notify or jmethodID creation
+ MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ current->get_and_cache_jmethod_id();
+ JvmtiExport::post_compiled_method_load(current);
+ }
+ current = CodeCache::next_nmethod(current);
}
return JVMTI_ERROR_NONE;
}