8003635: NPG: AsynchGetCallTrace broken by Method* virtual call
authorcoleenp
Wed, 28 Nov 2012 17:50:21 -0500
changeset 14579 7f6ce6e3dd80
parent 14578 d02f669f4da8
child 14585 c8448449bfbb
8003635: NPG: AsynchGetCallTrace broken by Method* virtual call Summary: Make metaspace::contains be lock free and used to see if something is in metaspace, also compare Method* with vtbl pointer. Reviewed-by: dholmes, sspitsyn, dcubed, jmasa
hotspot/src/cpu/sparc/vm/frame_sparc.cpp
hotspot/src/cpu/x86/vm/frame_x86.cpp
hotspot/src/share/vm/gc_interface/collectedHeap.hpp
hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp
hotspot/src/share/vm/memory/allocation.cpp
hotspot/src/share/vm/memory/allocation.hpp
hotspot/src/share/vm/memory/metaspace.cpp
hotspot/src/share/vm/memory/metaspace.hpp
hotspot/src/share/vm/memory/universe.cpp
hotspot/src/share/vm/oops/compiledICHolder.cpp
hotspot/src/share/vm/oops/method.cpp
hotspot/src/share/vm/oops/method.hpp
hotspot/src/share/vm/prims/forte.cpp
hotspot/src/share/vm/utilities/globalDefinitions.hpp
--- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -648,7 +648,7 @@
   Method* m = *interpreter_frame_method_addr();
 
   // validate the method we'd find in this potential sender
-  if (!Universe::heap()->is_valid_method(m)) return false;
+  if (!m->is_valid_method()) return false;
 
   // stack frames shouldn't be much larger than max_stack elements
 
--- a/hotspot/src/cpu/x86/vm/frame_x86.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -534,7 +534,7 @@
   Method* m = *interpreter_frame_method_addr();
 
   // validate the method we'd find in this potential sender
-  if (!Universe::heap()->is_valid_method(m)) return false;
+  if (!m->is_valid_method()) return false;
 
   // stack frames shouldn't be much larger than max_stack elements
 
--- a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp	Wed Nov 28 17:50:21 2012 -0500
@@ -289,11 +289,6 @@
   // (A scavenge is a GC which is not a full GC.)
   virtual bool is_scavengable(const void *p) = 0;
 
-  // Returns "TRUE" if "p" is a method oop in the
-  // current heap, with high probability. This predicate
-  // is not stable, in general.
-  bool is_valid_method(Method* p) const;
-
   void set_gc_cause(GCCause::Cause v) {
      if (UsePerfData) {
        _gc_lastcause = _gc_cause;
--- a/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp	Wed Nov 28 17:50:21 2012 -0500
@@ -242,36 +242,6 @@
   return (oop)obj;
 }
 
-// Returns "TRUE" if "p" is a method oop in the
-// current heap with high probability. NOTE: The main
-// current consumers of this interface are Forte::
-// and ThreadProfiler::. In these cases, the
-// interpreter frame from which "p" came, may be
-// under construction when sampled asynchronously, so
-// the clients want to check that it represents a
-// valid method before using it. Nonetheless since
-// the clients do not typically lock out GC, the
-// predicate is_valid_method() is not stable, so
-// it is possible that by the time "p" is used, it
-// is no longer valid.
-inline bool CollectedHeap::is_valid_method(Method* p) const {
-  return
-    p != NULL &&
-
-    // Check whether "method" is metadata
-    p->is_metadata() &&
-
-    // See if GC is active; however, there is still an
-    // apparently unavoidable window after this call
-    // and before the client of this interface uses "p".
-    // If the client chooses not to lock out GC, then
-    // it's a risk the client must accept.
-    !is_gc_active() &&
-
-    // Check that p is a Method*.
-    p->is_method();
-}
-
 inline void CollectedHeap::oop_iterate_no_header(OopClosure* cl) {
   NoHeaderExtendedOopClosure no_header_cl(cl);
   oop_iterate(&no_header_cl);
--- a/hotspot/src/share/vm/memory/allocation.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/memory/allocation.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -66,10 +66,17 @@
 }
 
 bool MetaspaceObj::is_metadata() const {
-  // ClassLoaderDataGraph::contains((address)this); has lock inversion problems
+  // GC Verify checks use this in guarantees.
+  // TODO: either replace them with is_metaspace_object() or remove them.
+  // is_metaspace_object() is slower than this test.  This test doesn't
+  // seem very useful for metaspace objects anymore though.
   return !Universe::heap()->is_in_reserved(this);
 }
 
+bool MetaspaceObj::is_metaspace_object() const {
+  return Metaspace::contains((void*)this);
+}
+
 void MetaspaceObj::print_address_on(outputStream* st) const {
   st->print(" {"INTPTR_FORMAT"}", this);
 }
--- a/hotspot/src/share/vm/memory/allocation.hpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/memory/allocation.hpp	Wed Nov 28 17:50:21 2012 -0500
@@ -245,6 +245,7 @@
 class MetaspaceObj {
  public:
   bool is_metadata() const;
+  bool is_metaspace_object() const;  // more specific test but slower
   bool is_shared() const;
   void print_address_on(outputStream* st) const;  // nonvirtual address printing
 
--- a/hotspot/src/share/vm/memory/metaspace.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/memory/metaspace.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -36,6 +36,7 @@
 #include "memory/universe.hpp"
 #include "runtime/globals.hpp"
 #include "runtime/mutex.hpp"
+#include "runtime/orderAccess.hpp"
 #include "services/memTracker.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/debug.hpp"
@@ -1007,6 +1008,8 @@
     delete new_entry;
     return false;
   } else {
+    // ensure lock-free iteration sees fully initialized node
+    OrderAccess::storestore();
     link_vs(new_entry, vs_word_size);
     return true;
   }
@@ -1096,7 +1099,6 @@
   }
 }
 
-#ifndef PRODUCT
 bool VirtualSpaceList::contains(const void *ptr) {
   VirtualSpaceNode* list = virtual_space_list();
   VirtualSpaceListIterator iter(list);
@@ -1108,7 +1110,6 @@
   }
   return false;
 }
-#endif // PRODUCT
 
 
 // MetaspaceGC methods
@@ -2739,15 +2740,17 @@
   }
 }
 
-#ifndef PRODUCT
-bool Metaspace::contains(const void * ptr) const {
+bool Metaspace::contains(const void * ptr) {
   if (MetaspaceShared::is_in_shared_space(ptr)) {
     return true;
   }
-  MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);
+  // This is checked while unlocked.  As long as the virtualspaces are added
+  // at the end, the pointer will be in one of them.  The virtual spaces
+  // aren't deleted presently.  When they are, some sort of locking might
+  // be needed.  Note, locking this can cause inversion problems with the
+  // caller in MetaspaceObj::is_metadata() function.
   return space_list()->contains(ptr) || class_space_list()->contains(ptr);
 }
-#endif
 
 void Metaspace::verify() {
   vsm()->verify();
--- a/hotspot/src/share/vm/memory/metaspace.hpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/memory/metaspace.hpp	Wed Nov 28 17:50:21 2012 -0500
@@ -135,11 +135,7 @@
 
   static bool is_initialized() { return _class_space_list != NULL; }
 
-#ifndef PRODUCT
-  bool contains(const void *ptr) const;
-  bool contains_class(const void *ptr) const;
-#endif
-
+  static bool contains(const void *ptr);
   void dump(outputStream* const out) const;
 
   void print_on(outputStream* st) const;
--- a/hotspot/src/share/vm/memory/universe.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/memory/universe.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -425,14 +425,10 @@
 // from MetaspaceObj, because the latter does not have virtual functions.
 // If the metadata type has a vtable, it cannot be shared in the read-only
 // section of the CDS archive, because the vtable pointer is patched.
-static inline void* dereference(void* addr) {
-  return *(void**)addr;
-}
-
 static inline void add_vtable(void** list, int* n, void* o, int count) {
   guarantee((*n) < count, "vtable list too small");
-  void* vtable = dereference(o);
-  assert(dereference(vtable) != NULL, "invalid vtable");
+  void* vtable = dereference_vptr(o);
+  assert(*(void**)(vtable) != NULL, "invalid vtable");
   list[(*n)++] = vtable;
 }
 
--- a/hotspot/src/share/vm/oops/compiledICHolder.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/oops/compiledICHolder.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -48,8 +48,8 @@
 // Verification
 
 void CompiledICHolder::verify_on(outputStream* st) {
-  guarantee(holder_method()->is_metadata(),   "should be in permspace");
+  guarantee(holder_method()->is_metadata(),   "should be in metaspace");
   guarantee(holder_method()->is_method(), "should be method");
-  guarantee(holder_klass()->is_metadata(),    "should be in permspace");
+  guarantee(holder_klass()->is_metadata(),    "should be in metaspace");
   guarantee(holder_klass()->is_klass(),   "should be klass");
 }
--- a/hotspot/src/share/vm/oops/method.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/oops/method.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -1814,6 +1814,23 @@
   loader_data->jmethod_ids()->clear_all_methods();
 }
 
+
+// Check that this pointer is valid by checking that the vtbl pointer matches
+bool Method::is_valid_method() const {
+  if (this == NULL) {
+    return false;
+  } else if (!is_metaspace_object()) {
+    return false;
+  } else {
+    Method m;
+    // This assumes that the vtbl pointer is the first word of a C++ object.
+    // This assumption is also in universe.cpp patch_klass_vtble
+    void* vtbl2 = dereference_vptr((void*)&m);
+    void* this_vtbl = dereference_vptr((void*)this);
+    return vtbl2 == this_vtbl;
+  }
+}
+
 #ifndef PRODUCT
 void Method::print_jmethod_ids(ClassLoaderData* loader_data, outputStream* out) {
   out->print_cr("jni_method_id count = %d", loader_data->jmethod_ids()->count_methods());
@@ -1935,7 +1952,7 @@
   guarantee(constMethod()->is_metadata(), "should be metadata");
   MethodData* md = method_data();
   guarantee(md == NULL ||
-      md->is_metadata(), "should be in permspace");
+      md->is_metadata(), "should be metadata");
   guarantee(md == NULL ||
       md->is_methodData(), "should be method data");
 }
--- a/hotspot/src/share/vm/oops/method.hpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/oops/method.hpp	Wed Nov 28 17:50:21 2012 -0500
@@ -169,7 +169,8 @@
                           ConstMethod::MethodType method_type,
                           TRAPS);
 
-  Method() { assert(DumpSharedSpaces || UseSharedSpaces, "only for CDS"); }
+  // CDS and vtbl checking can create an empty Method to get vtbl pointer.
+  Method(){}
 
   // The Method vtable is restored by this call when the Method is in the
   // shared archive.  See patch_klass_vtables() in metaspaceShared.cpp for
@@ -812,6 +813,9 @@
 
   const char* internal_name() const { return "{method}"; }
 
+  // Check for valid method pointer
+  bool is_valid_method() const;
+
   // Verify
   void verify() { verify_on(tty); }
   void verify_on(outputStream* st);
--- a/hotspot/src/share/vm/prims/forte.cpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/prims/forte.cpp	Wed Nov 28 17:50:21 2012 -0500
@@ -216,10 +216,7 @@
     // not yet valid.
 
     *method_p = method;
-
-    // See if gc may have invalidated method since we validated frame
-
-    if (!Universe::heap()->is_valid_method(method)) return false;
+    if (!method->is_valid_method()) return false;
 
     intptr_t bcx = fr->interpreter_frame_bcx();
 
@@ -394,19 +391,11 @@
   bool fully_decipherable = find_initial_Java_frame(thd, &top_frame, &initial_Java_frame, &method, &bci);
 
   // The frame might not be walkable but still recovered a method
-  // (e.g. an nmethod with no scope info for the pc
+  // (e.g. an nmethod with no scope info for the pc)
 
   if (method == NULL) return;
 
-  CollectedHeap* ch = Universe::heap();
-
-  // The method is not stored GC safe so see if GC became active
-  // after we entered AsyncGetCallTrace() and before we try to
-  // use the Method*.
-  // Yes, there is still a window after this check and before
-  // we use Method* below, but we can't lock out GC so that
-  // has to be an acceptable risk.
-  if (!ch->is_valid_method(method)) {
+  if (!method->is_valid_method()) {
     trace->num_frames = ticks_GC_active; // -2
     return;
   }
@@ -440,13 +429,7 @@
     bci = st.bci();
     method = st.method();
 
-    // The method is not stored GC safe so see if GC became active
-    // after we entered AsyncGetCallTrace() and before we try to
-    // use the Method*.
-    // Yes, there is still a window after this check and before
-    // we use Method* below, but we can't lock out GC so that
-    // has to be an acceptable risk.
-    if (!ch->is_valid_method(method)) {
+    if (!method->is_valid_method()) {
       // we throw away everything we've gathered in this sample since
       // none of it is safe
       trace->num_frames = ticks_GC_active; // -2
--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp	Wed Nov 28 08:43:26 2012 -0800
+++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp	Wed Nov 28 17:50:21 2012 -0500
@@ -1280,4 +1280,12 @@
 
 #define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0]))
 
+// Dereference vptr
+// All C++ compilers that we know of have the vtbl pointer in the first
+// word.  If there are exceptions, this function needs to be made compiler
+// specific.
+static inline void* dereference_vptr(void* addr) {
+  return *(void**)addr;
+}
+
 #endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP