changeset 4190:87cf402c32e4

Merge
author hseigel
date Wed, 06 Feb 2013 08:41:14 -0500
parents 02b3e25dcc6b 2793d96a0acf
children 62e7d37fe255
files
diffstat 29 files changed, 1023 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/tools/whitebox/sun/hotspot/WhiteBox.java	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/tools/whitebox/sun/hotspot/WhiteBox.java	Wed Feb 06 08:41:14 2013 -0500
@@ -69,4 +69,9 @@
   public native long    g1NumFreeRegions();
   public native int     g1RegionSize();
   public native Object[]    parseCommandLine(String commandline, DiagnosticCommand[] args);
+
+  // NMT
+  public native boolean NMTAllocTest();
+  public native boolean NMTFreeTestMemory();
+  public native boolean NMTWaitForDataMerge();
 }
--- a/src/share/vm/memory/allocation.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/memory/allocation.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -135,10 +135,11 @@
   mtChunk             = 0x0B00,  // chunk that holds content of arenas
   mtJavaHeap          = 0x0C00,  // Java heap
   mtClassShared       = 0x0D00,  // class data sharing
-  mtTracing           = 0x0E00,  // memory used for Tracing
-  mt_number_of_types  = 0x000E,  // number of memory types (mtDontTrack
+  mtTest              = 0x0E00,  // Test type for verifying NMT
+  mtTracing           = 0x0F00,  // memory used for Tracing
+  mt_number_of_types  = 0x000F,  // number of memory types (mtDontTrack
                                  // is not included as validate type)
-  mtDontTrack         = 0x0F00,  // memory we do not or cannot track
+  mtDontTrack         = 0x1000,  // memory we do not or cannot track
   mt_masks            = 0x7F00,
 
   // object type mask
--- a/src/share/vm/oops/cpCacheOop.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/oops/cpCacheOop.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, 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
@@ -575,6 +575,24 @@
   return false;
 }
 
+// a constant pool cache entry should never contain old or obsolete methods
+bool ConstantPoolCacheEntry::check_no_old_or_obsolete_entries() {
+  if (is_vfinal()) {
+    // virtual and final so _f2 contains method ptr instead of vtable index
+    methodOop m = (methodOop)_f2;
+    // Return false if _f2 refers to an old or an obsolete method.
+    // _f2 == NULL || !m->is_method() are just as unexpected here.
+    return (m != NULL && m->is_method() && !m->is_old() && !m->is_obsolete());
+  } else if ((oop)_f1 == NULL || !((oop)_f1)->is_method()) {
+    // _f1 == NULL || !_f1->is_method() are OK here
+    return true;
+  }
+
+  methodOop m = (methodOop)_f1;
+  // return false if _f1 refers to an old or an obsolete method
+  return (!m->is_old() && !m->is_obsolete());
+}
+
 bool ConstantPoolCacheEntry::is_interesting_method_entry(klassOop k) {
   if (!is_method_entry()) {
     // not a method entry so not interesting by default
@@ -598,7 +616,7 @@
   }
 
   assert(m != NULL && m->is_method(), "sanity check");
-  if (m == NULL || !m->is_method() || m->method_holder() != k) {
+  if (m == NULL || !m->is_method() || (k != NULL && m->method_holder() != k)) {
     // robustness for above sanity checks or method is not in
     // the interesting class
     return false;
@@ -683,3 +701,22 @@
     }
   }
 }
+
+// the constant pool cache should never contain old or obsolete methods
+bool constantPoolCacheOopDesc::check_no_old_or_obsolete_entries() {
+  for (int i = 1; i < length(); i++) {
+    if (entry_at(i)->is_interesting_method_entry(NULL) &&
+        !entry_at(i)->check_no_old_or_obsolete_entries()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void constantPoolCacheOopDesc::dump_cache() {
+  for (int i = 1; i < length(); i++) {
+    if (entry_at(i)->is_interesting_method_entry(NULL)) {
+      entry_at(i)->print(tty, i);
+    }
+  }
+}
--- a/src/share/vm/oops/cpCacheOop.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/oops/cpCacheOop.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2013, 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
@@ -363,6 +363,7 @@
   // group don't print the klass name.
   bool adjust_method_entry(methodOop old_method, methodOop new_method,
          bool * trace_name_printed);
+  bool check_no_old_or_obsolete_entries();
   bool is_interesting_method_entry(klassOop k);
 
   // Debugging & Printing
@@ -492,6 +493,8 @@
   // group don't print the klass name.
   void adjust_method_entries(methodOop* old_methods, methodOop* new_methods,
                              int methods_length, bool * trace_name_printed);
+  bool check_no_old_or_obsolete_entries();
+  void dump_cache();
 };
 
 #endif // SHARE_VM_OOPS_CPCACHEOOP_HPP
--- a/src/share/vm/oops/klassVtable.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/oops/klassVtable.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -640,11 +640,37 @@
                                 new_method->name()->as_C_string(),
                                 new_method->signature()->as_C_string()));
         }
+        // cannot 'break' here; see for-loop comment above.
       }
     }
   }
 }
 
+// a vtable should never contain old or obsolete methods
+bool klassVtable::check_no_old_or_obsolete_entries() {
+  for (int i = 0; i < length(); i++) {
+    methodOop m = unchecked_method_at(i);
+    if (m != NULL && (m->is_old() || m->is_obsolete())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void klassVtable::dump_vtable() {
+  tty->print_cr("vtable dump --");
+  for (int i = 0; i < length(); i++) {
+    methodOop m = unchecked_method_at(i);
+    if (m != NULL) {
+      tty->print("      (%5d)  ", i);
+      m->access_flags().print_on(tty);
+      tty->print(" --  ");
+      m->print_name(tty);
+      tty->cr();
+    }
+  }
+}
+
 // CDS/RedefineClasses support - clear vtables so they can be reinitialized
 void klassVtable::clear_vtable() {
   for (int i = 0; i < _length; i++) table()[i].clear();
@@ -994,13 +1020,42 @@
             new_method->name()->as_C_string(),
             new_method->signature()->as_C_string()));
         }
-        break;
+        // cannot 'break' here; see for-loop comment above.
       }
       ime++;
     }
   }
 }
 
+// an itable should never contain old or obsolete methods
+bool klassItable::check_no_old_or_obsolete_entries() {
+  itableMethodEntry* ime = method_entry(0);
+  for (int i = 0; i < _size_method_table; i++) {
+    methodOop m = ime->method();
+    if (m != NULL && (m->is_old() || m->is_obsolete())) {
+      return false;
+    }
+    ime++;
+  }
+  return true;
+}
+
+void klassItable::dump_itable() {
+  itableMethodEntry* ime = method_entry(0);
+  tty->print_cr("itable dump --");
+  for (int i = 0; i < _size_method_table; i++) {
+    methodOop m = ime->method();
+    if (m != NULL) {
+      tty->print("      (%5d)  ", i);
+      m->access_flags().print_on(tty);
+      tty->print(" --  ");
+      m->print_name(tty);
+      tty->cr();
+    }
+    ime++;
+  }
+}
+
 
 // Setup
 class InterfaceVisiterClosure : public StackObj {
@@ -1287,33 +1342,6 @@
   tty->print_cr("%6d bytes total", total);
 }
 
-bool klassVtable::check_no_old_entries() {
-  // Check that there really is no entry
-  for (int i = 0; i < length(); i++) {
-    methodOop m = unchecked_method_at(i);
-    if (m != NULL) {
-        if (m->is_old()) {
-            return false;
-        }
-    }
-  }
-  return true;
-}
-
-void klassVtable::dump_vtable() {
-  tty->print_cr("vtable dump --");
-  for (int i = 0; i < length(); i++) {
-    methodOop m = unchecked_method_at(i);
-    if (m != NULL) {
-      tty->print("      (%5d)  ", i);
-      m->access_flags().print_on(tty);
-      tty->print(" --  ");
-      m->print_name(tty);
-      tty->cr();
-    }
-  }
-}
-
 int  klassItable::_total_classes;   // Total no. of classes with itables
 long klassItable::_total_size;      // Total no. of bytes used for itables
 
--- a/src/share/vm/oops/klassVtable.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/oops/klassVtable.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -98,6 +98,8 @@
   // group don't print the klass name.
   void adjust_method_entries(methodOop* old_methods, methodOop* new_methods,
                              int methods_length, bool * trace_name_printed);
+  bool check_no_old_or_obsolete_entries();
+  void dump_vtable();
 
   // Garbage collection
   void oop_follow_contents();
@@ -118,11 +120,6 @@
   void verify(outputStream* st, bool force = false);
   static void print_statistics()                            PRODUCT_RETURN;
 
-#ifndef PRODUCT
-  bool check_no_old_entries();
-  void dump_vtable();
-#endif
-
  protected:
   friend class vtableEntry;
  private:
@@ -292,6 +289,8 @@
   // group don't print the klass name.
   void adjust_method_entries(methodOop* old_methods, methodOop* new_methods,
                              int methods_length, bool * trace_name_printed);
+  bool check_no_old_or_obsolete_entries();
+  void dump_itable();
 
   // Garbage collection
   void oop_follow_contents();
--- a/src/share/vm/oops/methodOop.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/oops/methodOop.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -1328,10 +1328,6 @@
 }
 
 
-//-----------------------------------------------------------------------------------
-// Non-product code
-
-#ifndef PRODUCT
 class SignatureTypePrinter : public SignatureTypeNames {
  private:
   outputStream* _st;
@@ -1368,6 +1364,10 @@
 }
 
 
+//-----------------------------------------------------------------------------------
+// Non-product code
+
+#ifndef PRODUCT
 void methodOopDesc::print_codes_on(outputStream* st) const {
   print_codes_on(0, code_size(), st);
 }
--- a/src/share/vm/oops/methodOop.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/oops/methodOop.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -714,8 +714,8 @@
   static bool has_unloaded_classes_in_signature(methodHandle m, TRAPS);
 
   // Printing
-  void print_short_name(outputStream* st = tty)  /*PRODUCT_RETURN*/; // prints as klassname::methodname; Exposed so field engineers can debug VM
-  void print_name(outputStream* st = tty)        PRODUCT_RETURN; // prints as "virtual void foo(int)"
+  void print_short_name(outputStream* st = tty); // prints as klassname::methodname; Exposed so field engineers can debug VM
+  void print_name(outputStream* st = tty); // prints as "virtual void foo(int)"; exposed for TraceRedefineClasses
 
   // Helper routine used for method sorting
   static void sort_methods(objArrayOop methods,
--- a/src/share/vm/prims/jvmtiRedefineClasses.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -129,8 +129,15 @@
   // See jvmtiExport.hpp for detailed explanation.
   JvmtiExport::set_has_redefined_a_class();
 
-#ifdef ASSERT
-  SystemDictionary::classes_do(check_class, thread);
+// check_class() is optionally called for product bits, but is
+// always called for non-product bits.
+#ifdef PRODUCT
+  if (RC_TRACE_ENABLED(0x00004000)) {
+#endif
+    RC_TRACE_WITH_THREAD(0x00004000, thread, ("calling check_class"));
+    SystemDictionary::classes_do(check_class, thread);
+#ifdef PRODUCT
+  }
 #endif
 }
 
@@ -3349,76 +3356,116 @@
   }
 }
 
-#ifndef PRODUCT
 void VM_RedefineClasses::check_class(klassOop k_oop,
        oop initiating_loader, TRAPS) {
   Klass *k = k_oop->klass_part();
   if (k->oop_is_instance()) {
     HandleMark hm(THREAD);
     instanceKlass *ik = (instanceKlass *) k;
-
-    if (ik->vtable_length() > 0) {
-      ResourceMark rm(THREAD);
-      if (!ik->vtable()->check_no_old_entries()) {
-        tty->print_cr("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name());
+    bool no_old_methods = true;  // be optimistic
+    ResourceMark rm(THREAD);
+
+    // a vtable should never contain old or obsolete methods
+    if (ik->vtable_length() > 0 &&
+        !ik->vtable()->check_no_old_or_obsolete_entries()) {
+      if (RC_TRACE_ENABLED(0x00004000)) {
+        RC_TRACE_WITH_THREAD(0x00004000, THREAD,
+          ("klassVtable::check_no_old_or_obsolete_entries failure"
+           " -- OLD or OBSOLETE method found -- class: %s",
+           ik->signature_name()));
         ik->vtable()->dump_vtable();
+      }
+      no_old_methods = false;
+    }
+
+    // an itable should never contain old or obsolete methods
+    if (ik->itable_length() > 0 &&
+        !ik->itable()->check_no_old_or_obsolete_entries()) {
+      if (RC_TRACE_ENABLED(0x00004000)) {
+        RC_TRACE_WITH_THREAD(0x00004000, THREAD,
+          ("klassItable::check_no_old_or_obsolete_entries failure"
+           " -- OLD or OBSOLETE method found -- class: %s",
+           ik->signature_name()));
+        ik->itable()->dump_itable();
+      }
+      no_old_methods = false;
+    }
+
+    // the constant pool cache should never contain old or obsolete methods
+    if (ik->constants() != NULL &&
+        ik->constants()->cache() != NULL &&
+        !ik->constants()->cache()->check_no_old_or_obsolete_entries()) {
+      if (RC_TRACE_ENABLED(0x00004000)) {
+        RC_TRACE_WITH_THREAD(0x00004000, THREAD,
+          ("cp-cache::check_no_old_or_obsolete_entries failure"
+           " -- OLD or OBSOLETE method found -- class: %s",
+           ik->signature_name()));
+        ik->constants()->cache()->dump_cache();
+      }
+      no_old_methods = false;
+    }
+
+    if (!no_old_methods) {
+      if (RC_TRACE_ENABLED(0x00004000)) {
         dump_methods();
-        assert(false, "OLD method found");
+      } else {
+        tty->print_cr("INFO: use the '-XX:TraceRedefineClasses=16384' option "
+          "to see more info about the following guarantee() failure.");
       }
+      guarantee(false, "OLD and/or OBSOLETE method(s) found");
     }
   }
 }
 
 void VM_RedefineClasses::dump_methods() {
-        int j;
-        tty->print_cr("_old_methods --");
-        for (j = 0; j < _old_methods->length(); ++j) {
-          methodOop m = (methodOop) _old_methods->obj_at(j);
-          tty->print("%4d  (%5d)  ", j, m->vtable_index());
-          m->access_flags().print_on(tty);
-          tty->print(" --  ");
-          m->print_name(tty);
-          tty->cr();
-        }
-        tty->print_cr("_new_methods --");
-        for (j = 0; j < _new_methods->length(); ++j) {
-          methodOop m = (methodOop) _new_methods->obj_at(j);
-          tty->print("%4d  (%5d)  ", j, m->vtable_index());
-          m->access_flags().print_on(tty);
-          tty->print(" --  ");
-          m->print_name(tty);
-          tty->cr();
-        }
-        tty->print_cr("_matching_(old/new)_methods --");
-        for (j = 0; j < _matching_methods_length; ++j) {
-          methodOop m = _matching_old_methods[j];
-          tty->print("%4d  (%5d)  ", j, m->vtable_index());
-          m->access_flags().print_on(tty);
-          tty->print(" --  ");
-          m->print_name(tty);
-          tty->cr();
-          m = _matching_new_methods[j];
-          tty->print("      (%5d)  ", m->vtable_index());
-          m->access_flags().print_on(tty);
-          tty->cr();
-        }
-        tty->print_cr("_deleted_methods --");
-        for (j = 0; j < _deleted_methods_length; ++j) {
-          methodOop m = _deleted_methods[j];
-          tty->print("%4d  (%5d)  ", j, m->vtable_index());
-          m->access_flags().print_on(tty);
-          tty->print(" --  ");
-          m->print_name(tty);
-          tty->cr();
-        }
-        tty->print_cr("_added_methods --");
-        for (j = 0; j < _added_methods_length; ++j) {
-          methodOop m = _added_methods[j];
-          tty->print("%4d  (%5d)  ", j, m->vtable_index());
-          m->access_flags().print_on(tty);
-          tty->print(" --  ");
-          m->print_name(tty);
-          tty->cr();
-        }
+  int j;
+  RC_TRACE(0x00004000, ("_old_methods --"));
+  for (j = 0; j < _old_methods->length(); ++j) {
+    methodOop m = (methodOop) _old_methods->obj_at(j);
+    RC_TRACE_NO_CR(0x00004000, ("%4d  (%5d)  ", j, m->vtable_index()));
+    m->access_flags().print_on(tty);
+    tty->print(" --  ");
+    m->print_name(tty);
+    tty->cr();
+  }
+  RC_TRACE(0x00004000, ("_new_methods --"));
+  for (j = 0; j < _new_methods->length(); ++j) {
+    methodOop m = (methodOop) _new_methods->obj_at(j);
+    RC_TRACE_NO_CR(0x00004000, ("%4d  (%5d)  ", j, m->vtable_index()));
+    m->access_flags().print_on(tty);
+    tty->print(" --  ");
+    m->print_name(tty);
+    tty->cr();
+  }
+  RC_TRACE(0x00004000, ("_matching_(old/new)_methods --"));
+  for (j = 0; j < _matching_methods_length; ++j) {
+    methodOop m = _matching_old_methods[j];
+    RC_TRACE_NO_CR(0x00004000, ("%4d  (%5d)  ", j, m->vtable_index()));
+    m->access_flags().print_on(tty);
+    tty->print(" --  ");
+    m->print_name(tty);
+    tty->cr();
+    m = _matching_new_methods[j];
+    RC_TRACE_NO_CR(0x00004000, ("      (%5d)  ", m->vtable_index()));
+    m->access_flags().print_on(tty);
+    tty->cr();
+  }
+  RC_TRACE(0x00004000, ("_deleted_methods --"));
+  for (j = 0; j < _deleted_methods_length; ++j) {
+    methodOop m = _deleted_methods[j];
+    RC_TRACE_NO_CR(0x00004000, ("%4d  (%5d)  ", j, m->vtable_index()));
+    m->access_flags().print_on(tty);
+    tty->print(" --  ");
+    m->print_name(tty);
+    tty->cr();
+  }
+  RC_TRACE(0x00004000, ("_added_methods --"));
+  for (j = 0; j < _added_methods_length; ++j) {
+    methodOop m = _added_methods[j];
+    RC_TRACE_NO_CR(0x00004000, ("%4d  (%5d)  ", j, m->vtable_index()));
+    m->access_flags().print_on(tty);
+    tty->print(" --  ");
+    m->print_name(tty);
+    tty->cr();
+  }
 }
-#endif
--- a/src/share/vm/prims/jvmtiRedefineClasses.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -478,9 +478,8 @@
 
   void flush_dependent_code(instanceKlassHandle k_h, TRAPS);
 
-  static void check_class(klassOop k_oop, oop initiating_loader, TRAPS) PRODUCT_RETURN;
-
-  static void dump_methods()   PRODUCT_RETURN;
+  static void check_class(klassOop k_oop, oop initiating_loader, TRAPS);
+  static void dump_methods();
 
  public:
   VM_RedefineClasses(jint class_count,
--- a/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2013, 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
@@ -54,7 +54,7 @@
 //    0x00000800 |       2048 - previous class breakpoint mgmt
 //    0x00001000 |       4096 - detect calls to obsolete methods
 //    0x00002000 |       8192 - fail a guarantee() in addition to detection
-//    0x00004000 |      16384 - unused
+//    0x00004000 |      16384 - detect old/obsolete methods in metadata
 //    0x00008000 |      32768 - old/new method matching/add/delete
 //    0x00010000 |      65536 - impl details: CP size info
 //    0x00020000 |     131072 - impl details: CP merge pass info
@@ -82,6 +82,13 @@
     tty->print_cr args; \
   } while (0)
 
+#define RC_TRACE_NO_CR(level, args) \
+  if ((TraceRedefineClasses & level) != 0) { \
+    ResourceMark rm; \
+    tty->print("RedefineClasses-0x%x: ", level); \
+    tty->print args; \
+  } while (0)
+
 #define RC_TRACE_WITH_THREAD(level, thread, args) \
   if ((TraceRedefineClasses & level) != 0) { \
     ResourceMark rm(thread); \
--- a/src/share/vm/prims/whitebox.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/prims/whitebox.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -42,6 +42,8 @@
 #include "gc_implementation/g1/heapRegionRemSet.hpp"
 #endif // !SERIALGC
 
+#include "services/memTracker.hpp"
+
 bool WhiteBox::_used = false;
 
 WB_ENTRY(jlong, WB_GetObjectAddress(JNIEnv* env, jobject o, jobject obj))
@@ -77,6 +79,57 @@
 WB_END
 #endif // !SERIALGC
 
+// Keep track of the 3 allocations in NMTAllocTest so we can free them later
+// on and verify that they're not visible anymore
+static void* nmtMtTest1 = NULL, *nmtMtTest2 = NULL, *nmtMtTest3 = NULL;
+
+// Alloc memory using the test memory type so that we can use that to see if
+// NMT picks it up correctly
+WB_ENTRY(jboolean, WB_NMTAllocTest(JNIEnv* env))
+  void *mem;
+
+  if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) {
+    return false;
+  }
+
+  // Allocate 2 * 128k + 256k + 1024k and free the 1024k one to make sure we track
+  // everything correctly. Total should be 512k held alive.
+  nmtMtTest1 = os::malloc(128 * 1024, mtTest);
+  mem = os::malloc(1024 * 1024, mtTest);
+  nmtMtTest2 = os::malloc(256 * 1024, mtTest);
+  os::free(mem, mtTest);
+  nmtMtTest3 = os::malloc(128 * 1024, mtTest);
+
+  return true;
+WB_END
+
+// Free the memory allocated by NMTAllocTest
+WB_ENTRY(jboolean, WB_NMTFreeTestMemory(JNIEnv* env))
+
+  if (nmtMtTest1 == NULL || nmtMtTest2 == NULL || nmtMtTest3 == NULL) {
+    return false;
+  }
+
+  os::free(nmtMtTest1, mtTest);
+  nmtMtTest1 = NULL;
+  os::free(nmtMtTest2, mtTest);
+  nmtMtTest2 = NULL;
+  os::free(nmtMtTest3, mtTest);
+  nmtMtTest3 = NULL;
+
+  return true;
+WB_END
+
+// Block until the current generation of NMT data to be merged, used to reliably test the NMT feature
+WB_ENTRY(jboolean, WB_NMTWaitForDataMerge(JNIEnv* env))
+
+  if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) {
+    return false;
+  }
+
+  return MemTracker::wbtest_wait_for_data_merge();
+WB_END
+
 //Some convenience methods to deal with objects from java
 int WhiteBox::offset_for_field(const char* field_name, oop object,
     Symbol* signature_symbol) {
@@ -143,6 +196,9 @@
   {CC"g1NumFreeRegions",   CC"()J",                   (void*)&WB_G1NumFreeRegions  },
   {CC"g1RegionSize",       CC"()I",                   (void*)&WB_G1RegionSize      },
 #endif // !SERIALGC
+  {CC"NMTAllocTest",       CC"()Z",                   (void*)&WB_NMTAllocTest      },
+  {CC"NMTFreeTestMemory",  CC"()Z",                   (void*)&WB_NMTFreeTestMemory },
+  {CC"NMTWaitForDataMerge",CC"()Z",                   (void*)&WB_NMTWaitForDataMerge},
 };
 
 #undef CC
--- a/src/share/vm/services/memBaseline.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memBaseline.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -41,6 +41,7 @@
   {mtTracing,    "Tracing"},
   {mtChunk,      "Pooled Free Chunks"},
   {mtClassShared,"Shared spaces for classes"},
+  {mtTest,       "Test"},
   {mtNone,       "Unknown"}  // It can happen when type tagging records are lagging
                              // behind
 };
--- a/src/share/vm/services/memPtr.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memPtr.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -27,8 +27,8 @@
 #include "services/memTracker.hpp"
 
 volatile jint SequenceGenerator::_seq_number = 1;
+volatile unsigned long SequenceGenerator::_generation = 1;
 NOT_PRODUCT(jint SequenceGenerator::_max_seq_number = 1;)
-DEBUG_ONLY(volatile unsigned long SequenceGenerator::_generation = 0;)
 
 jint SequenceGenerator::next() {
   jint seq = Atomic::add(1, &_seq_number);
--- a/src/share/vm/services/memPtr.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memPtr.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -47,16 +47,16 @@
   static void reset() {
     assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required");
     _seq_number = 1;
-    DEBUG_ONLY(_generation ++;)
+    _generation ++;
   };
 
-  DEBUG_ONLY(static unsigned long current_generation() { return (unsigned long)_generation; })
+  static unsigned long current_generation() { return _generation; }
   NOT_PRODUCT(static jint max_seq_num() { return _max_seq_number; })
 
  private:
-  static volatile jint _seq_number;
-  NOT_PRODUCT(static jint _max_seq_number; )
-  DEBUG_ONLY(static volatile unsigned long _generation; )
+  static volatile jint             _seq_number;
+  static volatile unsigned long    _generation;
+  NOT_PRODUCT(static jint          _max_seq_number; )
 };
 
 /*
--- a/src/share/vm/services/memRecorder.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memRecorder.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -55,7 +55,7 @@
 MemRecorder::MemRecorder() {
   assert(MemTracker::is_on(), "Native memory tracking is off");
   Atomic::inc(&_instance_count);
-  debug_only(set_generation();)
+  set_generation();
 
   if (MemTracker::track_callsite()) {
     _pointer_records = new (std::nothrow)FixedSizeMemPointerArray<SeqMemPointerRecordEx,
@@ -151,11 +151,12 @@
 }
 
 
-#ifdef ASSERT
 void MemRecorder::set_generation() {
   _generation = SequenceGenerator::current_generation();
 }
 
+#ifdef ASSERT
+
 void MemRecorder::check_dup_seq(jint seq) const {
   MemPointerArrayIteratorImpl itr(_pointer_records);
   MemPointerRecord* rc = (MemPointerRecord*)itr.current();
--- a/src/share/vm/services/memRecorder.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memRecorder.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -213,7 +213,7 @@
   // used for linked list
   MemRecorder*             _next;
   // active recorder can only record a certain generation data
-  debug_only(unsigned long _generation;)
+  unsigned long            _generation;
 
  protected:
   _NOINLINE_ MemRecorder();
@@ -251,6 +251,8 @@
 
   SequencedRecordIterator pointer_itr();
 
+  // return the generation of this recorder which it belongs to
+  unsigned long get_generation() const { return _generation; }
  protected:
   // number of MemRecorder instance
   static volatile jint _instance_count;
@@ -263,7 +265,7 @@
   static int sort_record_fn(const void* e1, const void* e2);
 
   debug_only(void check_dup_seq(jint seq) const;)
-  debug_only(void set_generation();)
+  void set_generation();
 };
 
 #endif // SHARE_VM_SERVICES_MEM_RECORDER_HPP
--- a/src/share/vm/services/memTrackWorker.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memTrackWorker.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -91,6 +91,8 @@
   MemSnapshot* snapshot = MemTracker::get_snapshot();
   assert(snapshot != NULL, "Worker should not be started");
   MemRecorder* rec;
+  unsigned long processing_generation = 0;
+  bool          worker_idle = false;
 
   while (!MemTracker::shutdown_in_progress()) {
     NOT_PRODUCT(_last_gen_in_use = generations_in_use();)
@@ -100,6 +102,12 @@
       rec = _gen[_head].next_recorder();
     }
     if (rec != NULL) {
+      if (rec->get_generation() != processing_generation || worker_idle) {
+        processing_generation = rec->get_generation();
+        worker_idle = false;
+        MemTracker::set_current_processing_generation(processing_generation);
+      }
+
       // merge the recorder into staging area
       if (!snapshot->merge(rec)) {
         MemTracker::shutdown(MemTracker::NMT_out_of_memory);
@@ -129,6 +137,9 @@
           MemTracker::shutdown(MemTracker::NMT_out_of_memory);
         }
       } else {
+        // worker thread is idle
+        worker_idle = true;
+        MemTracker::report_worker_idle();
         snapshot->wait(1000);
         ThreadCritical tc;
         // check if more data arrived
--- a/src/share/vm/services/memTrackWorker.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memTrackWorker.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -107,6 +107,7 @@
   NOT_PRODUCT(int _merge_count;)
   NOT_PRODUCT(int _last_gen_in_use;)
 
+  // how many generations are queued
   inline int generations_in_use() const {
     return (_tail >= _head ? (_tail - _head + 1) : (MAX_GENERATIONS - (_head - _tail) + 1));
   }
--- a/src/share/vm/services/memTracker.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memTracker.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -29,6 +29,7 @@
 #include "runtime/mutexLocker.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/threadCritical.hpp"
+#include "runtime/vm_operations.hpp"
 #include "services/memPtr.hpp"
 #include "services/memReporter.hpp"
 #include "services/memTracker.hpp"
@@ -65,6 +66,8 @@
 MemTracker::ShutdownReason      MemTracker::_reason = NMT_shutdown_none;
 int                             MemTracker::_thread_count = 255;
 volatile jint                   MemTracker::_pooled_recorder_count = 0;
+volatile unsigned long          MemTracker::_processing_generation = 0;
+volatile bool                   MemTracker::_worker_thread_idle = false;
 debug_only(intx                 MemTracker::_main_thread_tid = 0;)
 NOT_PRODUCT(volatile jint       MemTracker::_pending_recorder_count = 0;)
 
@@ -279,7 +282,7 @@
      }
      cur_head->set_next(NULL);
      Atomic::dec(&_pooled_recorder_count);
-     debug_only(cur_head->set_generation();)
+     cur_head->set_generation();
      return cur_head;
   }
 }
@@ -570,6 +573,51 @@
   return false;
 }
 
+// Whitebox API for blocking until the current generation of NMT data has been merged
+bool MemTracker::wbtest_wait_for_data_merge() {
+  // NMT can't be shutdown while we're holding _query_lock
+  MutexLockerEx lock(_query_lock, true);
+  assert(_worker_thread != NULL, "Invalid query");
+  // the generation at query time, so NMT will spin till this generation is processed
+  unsigned long generation_at_query_time = SequenceGenerator::current_generation();
+  unsigned long current_processing_generation = _processing_generation;
+  // if generation counter overflown
+  bool generation_overflown = (generation_at_query_time < current_processing_generation);
+  long generations_to_wrap = MAX_UNSIGNED_LONG - current_processing_generation;
+  // spin
+  while (!shutdown_in_progress()) {
+    if (!generation_overflown) {
+      if (current_processing_generation > generation_at_query_time) {
+        return true;
+      }
+    } else {
+      assert(generations_to_wrap >= 0, "Sanity check");
+      long current_generations_to_wrap = MAX_UNSIGNED_LONG - current_processing_generation;
+      assert(current_generations_to_wrap >= 0, "Sanity check");
+      // to overflow an unsigned long should take long time, so to_wrap check should be sufficient
+      if (current_generations_to_wrap > generations_to_wrap &&
+          current_processing_generation > generation_at_query_time) {
+        return true;
+      }
+    }
+
+    // if worker thread is idle, but generation is not advancing, that means
+    // there is not safepoint to let NMT advance generation, force one.
+    if (_worker_thread_idle) {
+      VM_ForceSafepoint vfs;
+      VMThread::execute(&vfs);
+    }
+    MemSnapshot* snapshot = get_snapshot();
+    if (snapshot == NULL) {
+      return false;
+    }
+    snapshot->wait(1000);
+    current_processing_generation = _processing_generation;
+  }
+  // We end up here if NMT is shutting down before our data has been merged
+  return false;
+}
+
 // compare memory usage between current snapshot and baseline
 bool MemTracker::compare_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) {
   MutexLockerEx lock(_query_lock, true);
--- a/src/share/vm/services/memTracker.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/services/memTracker.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -41,6 +41,10 @@
 
 extern bool NMT_track_callsite;
 
+#ifndef MAX_UNSIGNED_LONG
+#define MAX_UNSIGNED_LONG    (unsigned long)(-1)
+#endif
+
 #ifdef ASSERT
   #define DEBUG_CALLER_PC  (NMT_track_callsite ? os::get_caller_pc(2) : 0)
 #else
@@ -310,6 +314,11 @@
   static bool compare_memory_usage(BaselineOutputer& out, size_t unit,
            bool summary_only = true);
 
+  // the version for whitebox testing support, it ensures that all memory
+  // activities before this method call, are reflected in the snapshot
+  // database.
+  static bool wbtest_wait_for_data_merge();
+
   // sync is called within global safepoint to synchronize nmt data
   static void sync();
 
@@ -362,6 +371,15 @@
   static void create_record_in_recorder(address addr, MEMFLAGS type,
                    size_t size, address pc, JavaThread* thread);
 
+  static void set_current_processing_generation(unsigned long generation) {
+    _worker_thread_idle = false;
+    _processing_generation = generation;
+  }
+
+  static void report_worker_idle() {
+    _worker_thread_idle = true;
+  }
+
  private:
   // global memory snapshot
   static MemSnapshot*     _snapshot;
@@ -413,6 +431,11 @@
   static volatile enum NMTStates   _state;
   // the reason for shutting down nmt
   static enum ShutdownReason       _reason;
+  // the generation that NMT is processing
+  static volatile unsigned long    _processing_generation;
+  // although NMT is still procesing current generation, but
+  // there is not more recorder to process, set idle state
+  static volatile bool             _worker_thread_idle;
 };
 
 #endif // SHARE_VM_SERVICES_MEM_TRACKER_HPP
--- a/src/share/vm/utilities/accessFlags.cpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/utilities/accessFlags.cpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -59,8 +59,6 @@
   } while(f != old_flags);
 }
 
-#ifndef PRODUCT
-
 void AccessFlags::print_on(outputStream* st) const {
   if (is_public      ()) st->print("public "      );
   if (is_private     ()) st->print("private "     );
@@ -79,8 +77,6 @@
   if (is_obsolete    ()) st->print("{obsolete} "  );
 }
 
-#endif
-
 void accessFlags_init() {
   assert(sizeof(AccessFlags) == sizeof(jint), "just checking size of flags");
 }
--- a/src/share/vm/utilities/accessFlags.hpp	Wed Feb 06 08:26:05 2013 -0500
+++ b/src/share/vm/utilities/accessFlags.hpp	Wed Feb 06 08:41:14 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, 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
@@ -229,7 +229,7 @@
   inline friend AccessFlags accessFlags_from(jint flags);
 
   // Printing/debugging
-  void print_on(outputStream* st) const PRODUCT_RETURN;
+  void print_on(outputStream* st) const;
 };
 
 inline AccessFlags accessFlags_from(jint flags) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/OutputAnalyzerTest.java	Wed Feb 06 08:41:14 2013 -0500
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Test the OutputAnalyzer utility class
+ * @library /testlibrary
+ */
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+
+public class OutputAnalyzerTest {
+
+  public static void main(String args[]) throws Exception {
+
+    String stdout = "aaaaaa";
+    String stderr = "bbbbbb";
+
+    OutputAnalyzer output = new OutputAnalyzer(stdout, stderr);
+
+    if (!stdout.equals(output.getStdout())) {
+      throw new Exception("getStdout() returned '" + output.getStdout() + "', expected '" + stdout + "'");
+    }
+
+    if (!stderr.equals(output.getStderr())) {
+      throw new Exception("getStderr() returned '" + output.getStderr() + "', expected '" + stderr + "'");
+    }
+
+    try {
+      output.shouldContain(stdout);
+      output.stdoutShouldContain(stdout);
+      output.shouldContain(stderr);
+      output.stderrShouldContain(stderr);
+    } catch (RuntimeException e) {
+      throw new Exception("shouldContain() failed", e);
+    }
+
+    try {
+      output.shouldContain("cccc");
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stdoutShouldContain(stderr);
+      throw new Exception("stdoutShouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stderrShouldContain(stdout);
+      throw new Exception("stdoutShouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.shouldNotContain("cccc");
+      output.stdoutShouldNotContain("cccc");
+      output.stderrShouldNotContain("cccc");
+    } catch (RuntimeException e) {
+      throw new Exception("shouldNotContain() failed", e);
+    }
+
+    try {
+      output.shouldNotContain(stdout);
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stdoutShouldNotContain(stdout);
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+
+    try {
+      output.stderrShouldNotContain(stderr);
+      throw new Exception("shouldContain() failed to throw exception");
+    } catch (RuntimeException e) {
+      // expected
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/JDKToolFinder.java	Wed Feb 06 08:41:14 2013 -0500
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.File;
+
+public final class JDKToolFinder {
+
+  private JDKToolFinder() {
+  }
+
+  /**
+   * Returns the full path to an executable in jdk/bin based on System property
+   * test.jdk (set by jtreg test suite)
+   *
+   * @return Full path to an executable in jdk/bin
+   */
+  public static String getJDKTool(String tool) {
+    String binPath = System.getProperty("test.jdk");
+    if (binPath == null) {
+      throw new RuntimeException("System property 'test.jdk' not set. This property is normally set by jtreg. "
+          + "When running test separately, set this property using '-Dtest.jdk=/path/to/jdk'.");
+    }
+
+    binPath += File.separatorChar + "bin" + File.separatorChar + tool;
+
+    return binPath;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java	Wed Feb 06 08:41:14 2013 -0500
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.IOException;
+
+public final class OutputAnalyzer {
+
+  private final String stdout;
+  private final String stderr;
+  private final int exitValue;
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output and exit
+   * value from a Process
+   *
+   * @param process Process to analyze
+   * @throws IOException If an I/O error occurs.
+   */
+  public OutputAnalyzer(Process process) throws IOException {
+    OutputBuffer output = ProcessTools.getOutput(process);
+    exitValue = process.exitValue();
+    this.stdout = output.getStdout();
+    this.stderr = output.getStderr();
+  }
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output
+   *
+   * @param buf String buffer to analyze
+   */
+  public OutputAnalyzer(String buf) {
+    this(buf, buf);
+  }
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output
+   *
+   * @param stdout stdout buffer to analyze
+   * @param stderr stderr buffer to analyze
+   */
+  public OutputAnalyzer(String stdout, String stderr) {
+    this.stdout = stdout;
+    this.stderr = stderr;
+    exitValue = -1;
+  }
+
+  /**
+   * Verify that the stdout and stderr contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public void shouldContain(String expectedString) {
+    if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) {
+      throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr: [" + stdout + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public void stdoutShouldContain(String expectedString) {
+    if (!stdout.contains(expectedString)) {
+      throw new RuntimeException("'" + expectedString + "' missing from stdout: [" + stdout + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public void stderrShouldContain(String expectedString) {
+    if (!stderr.contains(expectedString)) {
+      throw new RuntimeException("'" + expectedString + "' missing from stderr: [" + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stdout and stderr contents of output buffer does not contain the string
+   *
+   * @param expectedString String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public void shouldNotContain(String notExpectedString) {
+    if (stdout.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stdout: [" + stdout + "]\n");
+    }
+    if (stderr.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stderr: [" + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer does not contain the string
+   *
+   * @param expectedString String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public void stdoutShouldNotContain(String notExpectedString) {
+    if (stdout.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stdout: [" + stdout + "]\n");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer does not contain the string
+   *
+   * @param expectedString String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public void stderrShouldNotContain(String notExpectedString) {
+    if (stderr.contains(notExpectedString)) {
+      throw new RuntimeException("'" + notExpectedString + "' found in stderr: [" + stderr + "]\n");
+    }
+  }
+
+  /**
+   * Verifiy the exit value of the process
+   *
+   * @param expectedExitValue Expected exit value from process
+   * @throws RuntimeException If the exit value from the process did not match the expected value
+   */
+  public void shouldHaveExitValue(int expectedExitValue) {
+    if (getExitValue() != expectedExitValue) {
+      throw new RuntimeException("Exit value " + getExitValue() + " , expected to get " + expectedExitValue);
+    }
+  }
+
+  /**
+   * Get the contents of the output buffer (stdout and stderr)
+   *
+   * @return Content of the output buffer
+   */
+  public String getOutput() {
+    return stdout + stderr;
+  }
+
+  /**
+   * Get the contents of the stdout buffer
+   *
+   * @return Content of the stdout buffer
+   */
+  public String getStdout() {
+    return stdout;
+  }
+
+  /**
+   * Get the contents of the stderr buffer
+   *
+   * @return Content of the stderr buffer
+   */
+  public String getStderr() {
+    return stderr;
+  }
+
+  /**
+   * Get the process exit value
+   *
+   * @return Process exit value
+   */
+  public int getExitValue() {
+    return exitValue;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/OutputBuffer.java	Wed Feb 06 08:41:14 2013 -0500
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+public class OutputBuffer {
+  private final String stdout;
+  private final String stderr;
+
+  /**
+   * Create an OutputBuffer, a class for storing and managing stdout and stderr
+   * results separately
+   *
+   * @param stdout stdout result
+   * @param stderr stderr result
+   */
+  public OutputBuffer(String stdout, String stderr) {
+    this.stdout = stdout;
+    this.stderr = stderr;
+  }
+
+  /**
+   * Returns the stdout result
+   *
+   * @return stdout result
+   */
+  public String getStdout() {
+    return stdout;
+  }
+
+  /**
+   * Returns the stderr result
+   *
+   * @return stderr result
+   */
+  public String getStderr() {
+    return stderr;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java	Wed Feb 06 08:41:14 2013 -0500
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import sun.management.VMManagement;
+
+public final class ProcessTools {
+
+  private ProcessTools() {
+  }
+
+  /**
+   * Pumps stdout and stderr from running the process into a String.
+   *
+   * @param processHandler ProcessHandler to run.
+   * @return Output from process.
+   * @throws IOException If an I/O error occurs.
+   */
+  public static OutputBuffer getOutput(ProcessBuilder processBuilder) throws IOException {
+    return getOutput(processBuilder.start());
+  }
+
+  /**
+   * Pumps stdout and stderr the running process into a String.
+   *
+   * @param process Process to pump.
+   * @return Output from process.
+   * @throws IOException If an I/O error occurs.
+   */
+  public static OutputBuffer getOutput(Process process) throws IOException {
+    ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream();
+    ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
+    StreamPumper outPumper = new StreamPumper(process.getInputStream(), stdoutBuffer);
+    StreamPumper errPumper = new StreamPumper(process.getErrorStream(), stderrBuffer);
+    Thread outPumperThread = new Thread(outPumper);
+    Thread errPumperThread = new Thread(errPumper);
+
+    outPumperThread.setDaemon(true);
+    errPumperThread.setDaemon(true);
+
+    outPumperThread.start();
+    errPumperThread.start();
+
+    try {
+      process.waitFor();
+      outPumperThread.join();
+      errPumperThread.join();
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      return null;
+    }
+
+    return new OutputBuffer(stdoutBuffer.toString(), stderrBuffer.toString());
+  }
+
+  /**
+   * Get the process id of the current running Java process
+   *
+   * @return Process id
+   */
+  public static int getProcessId() throws Exception {
+
+    // Get the current process id using a reflection hack
+    RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+    Field jvm = runtime.getClass().getDeclaredField("jvm");
+
+    jvm.setAccessible(true);
+    VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
+
+    Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
+
+    pid_method.setAccessible(true);
+
+    int pid = (Integer) pid_method.invoke(mgmt);
+
+    return pid;
+  }
+
+  /**
+   * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris)
+   *
+   * @return String[] with platform specific arguments, empty if there are none
+   */
+  public static String[] getPlatformSpecificVMArgs() {
+    String osName = System.getProperty("os.name");
+    String dataModel = System.getProperty("sun.arch.data.model");
+
+    if (osName.equals("SunOS") && dataModel.equals("64")) {
+      return new String[] { "-d64" };
+    }
+
+    return new String[] {};
+  }
+
+  /**
+   * Create ProcessBuilder using the java launcher from the jdk to be tested and
+   * with any platform specific arguments prepended
+   */
+  public static ProcessBuilder createJavaProcessBuilder(String... command) throws Exception {
+    String javapath = JDKToolFinder.getJDKTool("java");
+
+    ArrayList<String> args = new ArrayList<>();
+    args.add(javapath);
+    Collections.addAll(args, getPlatformSpecificVMArgs());
+    Collections.addAll(args, command);
+
+    return new ProcessBuilder(args.toArray(new String[args.size()]));
+
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/StreamPumper.java	Wed Feb 06 08:41:14 2013 -0500
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public final class StreamPumper implements Runnable {
+
+  private static final int BUF_SIZE = 256;
+
+  private final OutputStream out;
+  private final InputStream in;
+
+  /**
+   * Create a StreamPumper that reads from in and writes to out.
+   *
+   * @param in The stream to read from.
+   * @param out The stream to write to.
+   */
+  public StreamPumper(InputStream in, OutputStream out) {
+    this.in = in;
+    this.out = out;
+  }
+
+  /**
+   * Implements Thread.run(). Continuously read from <code>in</code> and write
+   * to <code>out</code> until <code>in</code> has reached end of stream. Abort
+   * on interruption. Abort on IOExceptions.
+   */
+  @Override
+  public void run() {
+    int length;
+    InputStream localIn = in;
+    OutputStream localOut = out;
+    byte[] buffer = new byte[BUF_SIZE];
+
+    try {
+      while (!Thread.interrupted() && (length = localIn.read(buffer)) > 0) {
+        localOut.write(buffer, 0, length);
+      }
+    } catch (IOException e) {
+      // Just abort if something like this happens.
+      e.printStackTrace();
+    } finally {
+      try {
+        localOut.flush();
+        in.close();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+}