6965671: fatal error: acquiring lock JNIGlobalHandle_lock/16 out of order with lock CodeCache_lock/1
authornever
Fri, 02 Jul 2010 15:01:47 -0700
changeset 5913 9dbc3381245b
parent 5903 a2d6fc757bbd
child 5914 8363e7e6915a
6965671: fatal error: acquiring lock JNIGlobalHandle_lock/16 out of order with lock CodeCache_lock/1 Reviewed-by: kvn, dcubed
hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp
--- 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;
 }