changeset 8038:f2f59d888427 jdk8u112-b03

Merge
author asaha
date Thu, 14 Jul 2016 12:13:21 -0700
parents 371fd9bb8202 (current diff) 15928d255046 (diff)
children c171546c49b5
files
diffstat 12 files changed, 516 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/classfile/classFileParser.cpp	Tue Jul 05 15:18:38 2016 -0700
+++ b/src/share/vm/classfile/classFileParser.cpp	Thu Jul 14 12:13:21 2016 -0700
@@ -537,6 +537,9 @@
           int name_index = cp->name_ref_index_at(index);
           Symbol*  name = cp->symbol_at(name_index);
           Symbol*  sig = cp->symbol_at(sig_index);
+          guarantee_property(sig->utf8_length() != 0,
+            "Illegal zero length constant pool entry at %d in class %s",
+            sig_index, CHECK_(nullHandle));
           if (sig->byte_at(0) == JVM_SIGNATURE_FUNC) {
             verify_legal_method_signature(name, sig, CHECK_(nullHandle));
           } else {
@@ -560,8 +563,9 @@
           verify_legal_field_name(name, CHECK_(nullHandle));
           if (_need_verify && _major_version >= JAVA_7_VERSION) {
             // Signature is verified above, when iterating NameAndType_info.
-            // Need only to be sure it's the right type.
-            if (signature->byte_at(0) == JVM_SIGNATURE_FUNC) {
+            // Need only to be sure it's non-zero length and the right type.
+            if (signature->utf8_length() == 0 ||
+                signature->byte_at(0) == JVM_SIGNATURE_FUNC) {
               throwIllegalSignature(
                   "Field", name, signature, CHECK_(nullHandle));
             }
@@ -572,8 +576,9 @@
           verify_legal_method_name(name, CHECK_(nullHandle));
           if (_need_verify && _major_version >= JAVA_7_VERSION) {
             // Signature is verified above, when iterating NameAndType_info.
-            // Need only to be sure it's the right type.
-            if (signature->byte_at(0) != JVM_SIGNATURE_FUNC) {
+            // Need only to be sure it's non-zero length and the right type.
+            if (signature->utf8_length() == 0 ||
+                signature->byte_at(0) != JVM_SIGNATURE_FUNC) {
               throwIllegalSignature(
                   "Method", name, signature, CHECK_(nullHandle));
             }
@@ -584,8 +589,7 @@
             // 4509014: If a class method name begins with '<', it must be "<init>".
             assert(name != NULL, "method name in constant pool is null");
             unsigned int name_len = name->utf8_length();
-            assert(name_len > 0, "bad method name");  // already verified as legal name
-            if (name->byte_at(0) == '<') {
+            if (name_len != 0 && name->byte_at(0) == '<') {
               if (name != vmSymbols::object_initializer_name()) {
                 classfile_parse_error(
                   "Bad method name at constant pool index %u in class file %s",
--- a/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp	Tue Jul 05 15:18:38 2016 -0700
+++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp	Thu Jul 14 12:13:21 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, 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
@@ -28,22 +28,23 @@
 #include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
 #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
 #include "gc_implementation/g1/g1StringDedupTable.hpp"
+#include "gc_implementation/shared/concurrentGCThread.hpp"
 #include "memory/gcLocker.hpp"
 #include "memory/padded.inline.hpp"
 #include "oops/typeArrayOop.hpp"
 #include "runtime/mutexLocker.hpp"
 
 //
-// Freelist in the deduplication table entry cache. Links table
+// List of deduplication table entries. Links table
 // entries together using their _next fields.
 //
-class G1StringDedupEntryFreeList : public CHeapObj<mtGC> {
+class G1StringDedupEntryList : public CHeapObj<mtGC> {
 private:
   G1StringDedupEntry* _list;
   size_t              _length;
 
 public:
-  G1StringDedupEntryFreeList() :
+  G1StringDedupEntryList() :
     _list(NULL),
     _length(0) {
   }
@@ -63,6 +64,12 @@
     return entry;
   }
 
+  G1StringDedupEntry* remove_all() {
+    G1StringDedupEntry* list = _list;
+    _list = NULL;
+    return list;
+  }
+
   size_t length() {
     return _length;
   }
@@ -84,43 +91,53 @@
 //
 class G1StringDedupEntryCache : public CHeapObj<mtGC> {
 private:
-  // One freelist per GC worker to allow lock less freeing of
-  // entries while doing a parallel scan of the table. Using
-  // PaddedEnd to avoid false sharing.
-  PaddedEnd<G1StringDedupEntryFreeList>* _lists;
-  size_t                                 _nlists;
+  // One cache/overflow list per GC worker to allow lock less freeing of
+  // entries while doing a parallel scan of the table. Using PaddedEnd to
+  // avoid false sharing.
+  size_t                             _nlists;
+  size_t                             _max_list_length;
+  PaddedEnd<G1StringDedupEntryList>* _cached;
+  PaddedEnd<G1StringDedupEntryList>* _overflowed;
 
 public:
-  G1StringDedupEntryCache();
+  G1StringDedupEntryCache(size_t max_size);
   ~G1StringDedupEntryCache();
 
-  // Get a table entry from the cache freelist, or allocate a new
-  // entry if the cache is empty.
+  // Set max number of table entries to cache.
+  void set_max_size(size_t max_size);
+
+  // Get a table entry from the cache, or allocate a new entry if the cache is empty.
   G1StringDedupEntry* alloc();
 
-  // Insert a table entry into the cache freelist.
+  // Insert a table entry into the cache.
   void free(G1StringDedupEntry* entry, uint worker_id);
 
   // Returns current number of entries in the cache.
   size_t size();
 
-  // If the cache has grown above the given max size, trim it down
-  // and deallocate the memory occupied by trimmed of entries.
-  void trim(size_t max_size);
+  // Deletes overflowed entries.
+  void delete_overflowed();
 };
 
-G1StringDedupEntryCache::G1StringDedupEntryCache() {
-  _nlists = MAX2(ParallelGCThreads, (size_t)1);
-  _lists = PaddedArray<G1StringDedupEntryFreeList, mtGC>::create_unfreeable((uint)_nlists);
+G1StringDedupEntryCache::G1StringDedupEntryCache(size_t max_size) :
+  _nlists(MAX2(ParallelGCThreads, (size_t)1)),
+  _max_list_length(0),
+  _cached(PaddedArray<G1StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)),
+  _overflowed(PaddedArray<G1StringDedupEntryList, mtGC>::create_unfreeable((uint)_nlists)) {
+  set_max_size(max_size);
 }
 
 G1StringDedupEntryCache::~G1StringDedupEntryCache() {
   ShouldNotReachHere();
 }
 
+void G1StringDedupEntryCache::set_max_size(size_t size) {
+  _max_list_length = size / _nlists;
+}
+
 G1StringDedupEntry* G1StringDedupEntryCache::alloc() {
   for (size_t i = 0; i < _nlists; i++) {
-    G1StringDedupEntry* entry = _lists[i].remove();
+    G1StringDedupEntry* entry = _cached[i].remove();
     if (entry != NULL) {
       return entry;
     }
@@ -131,31 +148,55 @@
 void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) {
   assert(entry->obj() != NULL, "Double free");
   assert(worker_id < _nlists, "Invalid worker id");
+
   entry->set_obj(NULL);
   entry->set_hash(0);
-  _lists[worker_id].add(entry);
+
+  if (_cached[worker_id].length() < _max_list_length) {
+    // Cache is not full
+    _cached[worker_id].add(entry);
+  } else {
+    // Cache is full, add to overflow list for later deletion
+    _overflowed[worker_id].add(entry);
+  }
 }
 
 size_t G1StringDedupEntryCache::size() {
   size_t size = 0;
   for (size_t i = 0; i < _nlists; i++) {
-    size += _lists[i].length();
+    size += _cached[i].length();
   }
   return size;
 }
 
-void G1StringDedupEntryCache::trim(size_t max_size) {
-  size_t cache_size = 0;
+void G1StringDedupEntryCache::delete_overflowed() {
+  double start = os::elapsedTime();
+  uintx count = 0;
+
   for (size_t i = 0; i < _nlists; i++) {
-    G1StringDedupEntryFreeList* list = &_lists[i];
-    cache_size += list->length();
-    while (cache_size > max_size) {
-      G1StringDedupEntry* entry = list->remove();
-      assert(entry != NULL, "Should not be null");
-      cache_size--;
+    G1StringDedupEntry* entry;
+
+    {
+      // The overflow list can be modified during safepoints, therefore
+      // we temporarily join the suspendible thread set while removing
+      // all entries from the list.
+      SuspendibleThreadSetJoiner sts_join;
+      entry = _overflowed[i].remove_all();
+    }
+
+    // Delete all entries
+    while (entry != NULL) {
+      G1StringDedupEntry* next = entry->next();
       delete entry;
+      entry = next;
+      count++;
     }
   }
+
+  double end = os::elapsedTime();
+  if (PrintStringDeduplicationStatistics) {
+    gclog_or_tty->print_cr("[GC concurrent-string-deduplication, deleted " UINTX_FORMAT " entries, " G1_STRDEDUP_TIME_FORMAT "]", count, end - start);
+  }
 }
 
 G1StringDedupTable*      G1StringDedupTable::_table = NULL;
@@ -192,7 +233,7 @@
 
 void G1StringDedupTable::create() {
   assert(_table == NULL, "One string deduplication table allowed");
-  _entry_cache = new G1StringDedupEntryCache();
+  _entry_cache = new G1StringDedupEntryCache((size_t)(_min_size * _max_cache_factor));
   _table = new G1StringDedupTable(_min_size);
 }
 
@@ -375,6 +416,9 @@
   // Update statistics
   _resize_count++;
 
+  // Update max cache size
+  _entry_cache->set_max_size((size_t)(size * _max_cache_factor));
+
   // Allocate the new table. The new table will be populated by workers
   // calling unlink_or_oops_do() and finally installed by finish_resize().
   return new G1StringDedupTable(size, _table->_hash_seed);
@@ -427,7 +471,7 @@
     removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id);
   }
 
-  // Delayed update avoid contention on the table lock
+  // Delayed update to avoid contention on the table lock
   if (removed > 0) {
     MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
     _table->_entries -= removed;
@@ -545,10 +589,8 @@
   }
 }
 
-void G1StringDedupTable::trim_entry_cache() {
-  MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag);
-  size_t max_cache_size = (size_t)(_table->_size * _max_cache_factor);
-  _entry_cache->trim(max_cache_size);
+void G1StringDedupTable::clean_entry_cache() {
+  _entry_cache->delete_overflowed();
 }
 
 void G1StringDedupTable::print_statistics(outputStream* st) {
--- a/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp	Tue Jul 05 15:18:38 2016 -0700
+++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp	Thu Jul 14 12:13:21 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, 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
@@ -218,8 +218,8 @@
   // and deletes the previously active table.
   static void finish_rehash(G1StringDedupTable* rehashed_table);
 
-  // If the table entry cache has grown too large, trim it down according to policy
-  static void trim_entry_cache();
+  // If the table entry cache has grown too large, delete overflowed entries.
+  static void clean_entry_cache();
 
   static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id);
 
--- a/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp	Tue Jul 05 15:18:38 2016 -0700
+++ b/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp	Thu Jul 14 12:13:21 2016 -0700
@@ -100,14 +100,14 @@
         }
       }
 
-      G1StringDedupTable::trim_entry_cache();
-
       stat.mark_done();
 
       // Print statistics
       total_stat.add(stat);
       print(gclog_or_tty, stat, total_stat);
     }
+
+    G1StringDedupTable::clean_entry_cache();
   }
 
   terminate();
--- a/src/share/vm/opto/stringopts.cpp	Tue Jul 05 15:18:38 2016 -0700
+++ b/src/share/vm/opto/stringopts.cpp	Thu Jul 14 12:13:21 2016 -0700
@@ -1641,16 +1641,11 @@
     }
     kit.store_String_value(kit.control(), result, char_array);
 
-    // Do not let stores that initialize this object be reordered with
-    // a subsequent store that would make this object accessible by
-    // other threads.
-    // Record what AllocateNode this StoreStore protects so that
-    // escape analysis can go from the MemBarStoreStoreNode to the
-    // AllocateNode and eliminate the MemBarStoreStoreNode if possible
-    // based on the escape status of the AllocateNode.
-    AllocateNode* alloc = AllocateNode::Ideal_allocation(result, _gvn);
-    assert(alloc != NULL, "should be newly allocated");
-    kit.insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out(AllocateNode::RawAddress));
+    // The value field is final. Emit a barrier here to ensure that the effect
+    // of the initialization is committed to memory before any code publishes
+    // a reference to the newly constructed object (see Parse::do_exits()).
+    assert(AllocateNode::Ideal_allocation(result, _gvn) != NULL, "should be newly allocated");
+    kit.insert_mem_bar(Op_MemBarRelease, result);
   } else {
     result = C->top();
   }
--- a/test/compiler/jsr292/VMAnonymousClasses.java	Tue Jul 05 15:18:38 2016 -0700
+++ b/test/compiler/jsr292/VMAnonymousClasses.java	Thu Jul 14 12:13:21 2016 -0700
@@ -24,7 +24,7 @@
 /**
  * @test
  * @bug 8058828
- * @run main/bootclasspath -Xbatch VMAnonymousClasses
+ * @run main/bootclasspath/othervm -Xbatch VMAnonymousClasses
  */
 
 import jdk.internal.org.objectweb.asm.ClassWriter;
--- a/test/compiler/stringopts/TestStringObjectInitialization.java	Tue Jul 05 15:18:38 2016 -0700
+++ b/test/compiler/stringopts/TestStringObjectInitialization.java	Thu Jul 14 12:13:21 2016 -0700
@@ -27,6 +27,7 @@
 /*
  * @test
  * @bug 8159244
+ * @requires vm.gc == "Parallel" | vm.gc == "null"
  * @summary Verifies that no partially initialized String object escapes from
  *          C2's String concat optimization in a highly concurrent setting.
  *          This test triggers the bug in about 1 out of 10 runs.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/classFileParserBug/BadNameAndType.java	Thu Jul 14 12:13:21 2016 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, 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
+ * @bug 8042660
+ * @summary Constant pool NameAndType entries must point to non-zero length Utf8 strings
+ * @compile emptySigUtf8.jcod
+ * @compile emptyNameUtf8.jcod
+ * @run main/othervm -Xverify:all BadNameAndType
+ */
+
+// Test that a constant pool NameAndType descriptor_index and/or name_index
+// that points to a zero length Utf8 string causes a ClassFormatError.
+public class BadNameAndType {
+    public static void main(String args[]) throws Throwable {
+
+        System.out.println("Regression test for bug 8042660");
+
+        // Test descriptor_index pointing to zero-length string.
+        try {
+            Class newClass = Class.forName("emptySigUtf8");
+            throw new RuntimeException("Expected ClassFormatError exception not thrown");
+        } catch (java.lang.ClassFormatError e) {
+            System.out.println("Test BadNameAndType passed test case emptySigUtf8");
+        }
+
+        // Test name_index pointing to zero-length string.
+        try {
+            Class newClass = Class.forName("emptyNameUtf8");
+            throw new RuntimeException("Expected ClassFormatError exception not thrown");
+        } catch (java.lang.ClassFormatError e) {
+            System.out.println("Test BadNameAndType passed test case emptyNameUtf8");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/classFileParserBug/emptyNameUtf8.jcod	Thu Jul 14 12:13:21 2016 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+// This class has an illegal NameAndType at constant pool #4.  It's illegal because
+// the Utf8 that it points to at #27 is a zero length string which is not a valid
+// name.  Loading this class should cause a ClassFormatError exception.
+class emptyNameUtf8 {
+  0xCAFEBABE;
+  0; // minor version
+  52; // version
+  [29] { // Constant Pool
+    ; // first element is empty
+    Method #6 #15; // #1     at 0x0A
+    Field #16 #17; // #2     at 0x0F
+    String #18; // #3     at 0x14
+    NameAndType #27 #28; // #4     at 0x9F
+    class #21; // #5     at 0x1C
+    class #22; // #6     at 0x1F
+    Utf8 "<init>"; // #7     at 0x22
+    Utf8 "()V"; // #8     at 0x2B
+    Utf8 "Code"; // #9     at 0x2E
+    Utf8 "LineNumberTable"; // #10     at 0x35
+    Utf8 "main"; // #11     at 0x47
+    Utf8 "([Ljava/lang/String;)V"; // #12     at 0x4E
+    Utf8 "SourceFile"; // #13     at 0x67
+    Utf8 "emptyNameUtf8.java"; // #14     at 0x74
+    NameAndType #7 #8; // #15     at 0x81
+    class #23; // #16     at 0x86
+    NameAndType #24 #25; // #17     at 0x89
+    Utf8 "Hello World"; // #18     at 0x8E
+    class #26; // #19     at 0x9C
+    Method #19 #4; // #20     at 0x17
+    Utf8 "emptyNameUtf8"; // #21     at 0xA4
+    Utf8 "java/lang/Object"; // #22     at 0xAC
+    Utf8 "java/lang/System"; // #23     at 0xBF
+    Utf8 "out"; // #24     at 0xD2
+    Utf8 "Ljava/io/PrintStream;"; // #25     at 0xD8
+    Utf8 "java/io/PrintStream"; // #26     at 0xF0
+    Utf8 ""; // #27     at 0x0106
+    Utf8 "()V"; // #28     at 0x0110
+  } // Constant Pool
+
+  0x0021; // access
+  #5;// this_cpx
+  #6;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [0] { // fields
+  } // fields
+
+  [2] { // methods
+    { // Member at 0x0134
+      0x0001; // access
+      #7; // name_cpx
+      #8; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 29) { // Code at 0x013C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 6) { // LineNumberTable at 0x0153
+              [1] { // LineNumberTable
+                0  2; //  at 0x015F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x015F
+      0x0009; // access
+      #11; // name_cpx
+      #12; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 37) { // Code at 0x0167
+          2; // max_stack
+          1; // max_locals
+          Bytes[9]{
+            0xB200021203B60004;
+            0xB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 10) { // LineNumberTable at 0x0182
+              [2] { // LineNumberTable
+                0  4; //  at 0x018E
+                8  5; //  at 0x0192
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#13, 2) { // SourceFile at 0x0194
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class emptyNameUtf8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/classFileParserBug/emptySigUtf8.jcod	Thu Jul 14 12:13:21 2016 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ */
+
+// This class has an illegal NameAndType at constant pool #4.  It's illegal because
+// the type that it points to at #28 is a zero length Utf8 string which is not a
+// valid signature.  Loading this class should cause a ClassFormatError exception.
+class emptySigUtf8 {
+  0xCAFEBABE;
+  0; // minor version
+  52; // version
+  [29] { // Constant Pool
+    ; // first element is empty
+    Method #6 #15; // #1     at 0x0A
+    Field #16 #17; // #2     at 0x0F
+    String #18; // #3     at 0x14
+    NameAndType #27 #28; // #4     at 0x9F
+    class #21; // #5     at 0x1C
+    class #22; // #6     at 0x1F
+    Utf8 "<init>"; // #7     at 0x22
+    Utf8 "()V"; // #8     at 0x2B
+    Utf8 "Code"; // #9     at 0x2E
+    Utf8 "LineNumberTable"; // #10     at 0x35
+    Utf8 "main"; // #11     at 0x47
+    Utf8 "([Ljava/lang/String;)V"; // #12     at 0x4E
+    Utf8 "SourceFile"; // #13     at 0x67
+    Utf8 "emptySigUtf8.java"; // #14     at 0x74
+    NameAndType #7 #8; // #15     at 0x81
+    class #23; // #16     at 0x86
+    NameAndType #24 #25; // #17     at 0x89
+    Utf8 "Hello World"; // #18     at 0x8E
+    class #26; // #19     at 0x9C
+    Method #19 #4; // #20     at 0x17
+    Utf8 "emptySigUtf8"; // #21     at 0xA4
+    Utf8 "java/lang/Object"; // #22     at 0xAC
+    Utf8 "java/lang/System"; // #23     at 0xBF
+    Utf8 "out"; // #24     at 0xD2
+    Utf8 "Ljava/io/PrintStream;"; // #25     at 0xD8
+    Utf8 "java/io/PrintStream"; // #26     at 0xF0
+    Utf8 "hi"; // #27     at 0x0106
+    Utf8 ""; // #28     at 0x0110
+  } // Constant Pool
+
+  0x0021; // access
+  #5;// this_cpx
+  #6;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [0] { // fields
+  } // fields
+
+  [2] { // methods
+    { // Member at 0x0134
+      0x0001; // access
+      #7; // name_cpx
+      #8; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 29) { // Code at 0x013C
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 6) { // LineNumberTable at 0x0153
+              [1] { // LineNumberTable
+                0  2; //  at 0x015F
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+    ;
+    { // Member at 0x015F
+      0x0009; // access
+      #11; // name_cpx
+      #12; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 37) { // Code at 0x0167
+          2; // max_stack
+          1; // max_locals
+          Bytes[9]{
+            0xB200021203B60004;
+            0xB1;
+          };
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 10) { // LineNumberTable at 0x0182
+              [2] { // LineNumberTable
+                0  4; //  at 0x018E
+                8  5; //  at 0x0192
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [1] { // Attributes
+    Attr(#13, 2) { // SourceFile at 0x0194
+      #14;
+    } // end SourceFile
+  } // Attributes
+} // end class emptySigUtf8
--- a/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java	Tue Jul 05 15:18:38 2016 -0700
+++ b/test/testlibrary/com/oracle/java/testlibrary/OutputAnalyzer.java	Thu Jul 14 12:13:21 2016 -0700
@@ -24,6 +24,8 @@
 package com.oracle.java.testlibrary;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -69,6 +71,58 @@
   }
 
   /**
+   * Verify that the stdout contents of output buffer is empty
+   *
+   * @throws RuntimeException
+   *             If stdout was not empty
+   */
+  public void stdoutShouldBeEmpty() {
+    if (!getStdout().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stdout was not empty");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer is empty
+   *
+   * @throws RuntimeException
+   *             If stderr was not empty
+   */
+  public void stderrShouldBeEmpty() {
+    if (!getStderr().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stderr was not empty");
+    }
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer is not empty
+   *
+   * @throws RuntimeException
+   *             If stdout was empty
+   */
+  public void stdoutShouldNotBeEmpty() {
+    if (getStdout().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stdout was empty");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer is not empty
+   *
+   * @throws RuntimeException
+   *             If stderr was empty
+   */
+  public void stderrShouldNotBeEmpty() {
+    if (getStderr().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stderr was empty");
+    }
+  }
+
+    /**
    * Verify that the stdout and stderr contents of output buffer contains the string
    *
    * @param expectedString String that buffer should contain
@@ -365,4 +419,18 @@
   public int getExitValue() {
     return exitValue;
   }
+
+  /**
+   * Get the contents of the output buffer (stdout and stderr) as list of strings.
+   * Output will be split by newlines.
+   *
+   * @return Contents of the output buffer as list of strings
+   */
+  public List<String> asLines() {
+    return asLines(getOutput());
+  }
+
+  private List<String> asLines(String buffer) {
+    return Arrays.asList(buffer.split("(\\r\\n|\\n|\\r)"));
+  }
 }
--- a/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java	Tue Jul 05 15:18:38 2016 -0700
+++ b/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java	Thu Jul 14 12:13:21 2016 -0700
@@ -187,23 +187,36 @@
     return executeProcess(pb);
   }
 
-  /**
-   * Executes a process, waits for it to finish and returns the process output.
-   * @param pb The ProcessBuilder to execute.
-   * @return The output from the process.
-   */
-  public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Throwable {
-    OutputAnalyzer output = null;
-    try {
-      output = new OutputAnalyzer(pb.start());
-      return output;
-    } catch (Throwable t) {
-      System.out.println("executeProcess() failed: " + t);
-      throw t;
-    } finally {
-      System.out.println(getProcessLog(pb, output));
+    /**
+     * Executes a process, waits for it to finish and returns the process output.
+     * The process will have exited before this method returns.
+     * @param pb The ProcessBuilder to execute.
+     * @return The {@linkplain OutputAnalyzer} instance wrapping the process.
+     */
+    public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Exception {
+        OutputAnalyzer output = null;
+        Process p = null;
+        boolean failed = false;
+        try {
+            p = pb.start();
+            output = new OutputAnalyzer(p);
+            p.waitFor();
+
+            return output;
+        } catch (Throwable t) {
+            if (p != null) {
+                p.destroyForcibly().waitFor();
+            }
+
+            failed = true;
+            System.out.println("executeProcess() failed: " + t);
+            throw t;
+        } finally {
+            if (failed) {
+                System.err.println(getProcessLog(pb, output));
+            }
+        }
     }
-  }
 
   /**
    * Executes a process, waits for it to finish and returns the process output.