changeset 4031:b14da2e6f2dc

7174978: NPG: Fix bactrace builder for class redefinition Summary: Remove Method* from backtrace but save version so redefine classes doesn't give inaccurate line numbers. Removed old Merlin API with duplicate code. Reviewed-by: dholmes, sspitsyn
author coleenp
date Thu, 17 Jan 2013 13:40:31 -0500
parents 203f64878aab
children b5f6465019f6
files make/bsd/makefiles/mapfile-vers-debug make/bsd/makefiles/mapfile-vers-product make/linux/makefiles/mapfile-vers-debug make/linux/makefiles/mapfile-vers-product make/solaris/makefiles/mapfile-vers src/share/vm/classfile/javaClasses.cpp src/share/vm/classfile/javaClasses.hpp src/share/vm/oops/constantPool.cpp src/share/vm/oops/constantPool.hpp src/share/vm/prims/jvm.cpp src/share/vm/prims/jvm.h src/share/vm/prims/jvmtiRedefineClasses.cpp
diffstat 12 files changed, 301 insertions(+), 345 deletions(-) [+]
line wrap: on
line diff
--- a/make/bsd/makefiles/mapfile-vers-debug	Thu Jan 17 10:25:16 2013 -0500
+++ b/make/bsd/makefiles/mapfile-vers-debug	Thu Jan 17 13:40:31 2013 -0500
@@ -3,7 +3,7 @@
 #
 
 #
-# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2002, 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
@@ -205,7 +205,6 @@
                 JVM_NewMultiArray;
                 JVM_OnExit;
                 JVM_Open;
-                JVM_PrintStackTrace;
                 JVM_RaiseSignal;
                 JVM_RawMonitorCreate;
                 JVM_RawMonitorDestroy;
--- a/make/bsd/makefiles/mapfile-vers-product	Thu Jan 17 10:25:16 2013 -0500
+++ b/make/bsd/makefiles/mapfile-vers-product	Thu Jan 17 13:40:31 2013 -0500
@@ -3,7 +3,7 @@
 #
 
 #
-# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2002, 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
@@ -205,7 +205,6 @@
                 JVM_NewMultiArray;
                 JVM_OnExit;
                 JVM_Open;
-                JVM_PrintStackTrace;
                 JVM_RaiseSignal;
                 JVM_RawMonitorCreate;
                 JVM_RawMonitorDestroy;
--- a/make/linux/makefiles/mapfile-vers-debug	Thu Jan 17 10:25:16 2013 -0500
+++ b/make/linux/makefiles/mapfile-vers-debug	Thu Jan 17 13:40:31 2013 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2002, 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
@@ -201,7 +201,6 @@
                 JVM_NewMultiArray;
                 JVM_OnExit;
                 JVM_Open;
-                JVM_PrintStackTrace;
                 JVM_RaiseSignal;
                 JVM_RawMonitorCreate;
                 JVM_RawMonitorDestroy;
--- a/make/linux/makefiles/mapfile-vers-product	Thu Jan 17 10:25:16 2013 -0500
+++ b/make/linux/makefiles/mapfile-vers-product	Thu Jan 17 13:40:31 2013 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2002, 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
@@ -201,7 +201,6 @@
                 JVM_NewMultiArray;
                 JVM_OnExit;
                 JVM_Open;
-                JVM_PrintStackTrace;
                 JVM_RaiseSignal;
                 JVM_RawMonitorCreate;
                 JVM_RawMonitorDestroy;
--- a/make/solaris/makefiles/mapfile-vers	Thu Jan 17 10:25:16 2013 -0500
+++ b/make/solaris/makefiles/mapfile-vers	Thu Jan 17 13:40:31 2013 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2000, 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
@@ -201,7 +201,6 @@
                 JVM_NewMultiArray;
                 JVM_OnExit;
                 JVM_Open;
-                JVM_PrintStackTrace;
                 JVM_RaiseSignal;
                 JVM_RawMonitorCreate;
                 JVM_RawMonitorDestroy;
--- a/src/share/vm/classfile/javaClasses.cpp	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/classfile/javaClasses.cpp	Thu Jan 17 13:40:31 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
@@ -1158,179 +1158,43 @@
   }
 }
 
-// Print stack trace element to resource allocated buffer
-char* java_lang_Throwable::print_stack_element_to_buffer(Method* method, int bci) {
-  // Get strings and string lengths
-  InstanceKlass* klass = method->method_holder();
-  const char* klass_name  = klass->external_name();
-  int buf_len = (int)strlen(klass_name);
-  char* source_file_name;
-  if (klass->source_file_name() == NULL) {
-    source_file_name = NULL;
+// After this many redefines, the stack trace is unreliable.
+const int MAX_VERSION = USHRT_MAX;
+
+// Helper backtrace functions to store bci|version together.
+static inline int merge_bci_and_version(int bci, int version) {
+  // only store u2 for version, checking for overflow.
+  if (version > USHRT_MAX || version < 0) version = MAX_VERSION;
+  assert((jushort)bci == bci, "bci should be short");
+  return build_int_from_shorts(version, bci);
+}
+
+static inline int bci_at(unsigned int merged) {
+  return extract_high_short_from_int(merged);
+}
+static inline int version_at(unsigned int merged) {
+  return extract_low_short_from_int(merged);
+}
+
+static inline bool version_matches(Method* method, int version) {
+  return (method->constants()->version() == version && version < MAX_VERSION);
+}
+
+static inline int get_line_number(Method* method, int bci) {
+  int line_number = 0;
+  if (method->is_native()) {
+    // Negative value different from -1 below, enabling Java code in
+    // class java.lang.StackTraceElement to distinguish "native" from
+    // "no LineNumberTable".  JDK tests for -2.
+    line_number = -2;
   } else {
-    source_file_name = klass->source_file_name()->as_C_string();
-    buf_len += (int)strlen(source_file_name);
-  }
-  char* method_name = method->name()->as_C_string();
-  buf_len += (int)strlen(method_name);
-
-  // Allocate temporary buffer with extra space for formatting and line number
-  char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64);
-
-  // Print stack trace line in buffer
-  sprintf(buf, "\tat %s.%s", klass_name, method_name);
-  if (method->is_native()) {
-    strcat(buf, "(Native Method)");
-  } else {
-    int line_number = method->line_number_from_bci(bci);
-    if (source_file_name != NULL && (line_number != -1)) {
-      // Sourcename and linenumber
-      sprintf(buf + (int)strlen(buf), "(%s:%d)", source_file_name, line_number);
-    } else if (source_file_name != NULL) {
-      // Just sourcename
-      sprintf(buf + (int)strlen(buf), "(%s)", source_file_name);
-    } else {
-      // Neither soucename and linenumber
-      sprintf(buf + (int)strlen(buf), "(Unknown Source)");
-    }
-    nmethod* nm = method->code();
-    if (WizardMode && nm != NULL) {
-      sprintf(buf + (int)strlen(buf), "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm);
+    // Returns -1 if no LineNumberTable, and otherwise actual line number
+    line_number = method->line_number_from_bci(bci);
+    if (line_number == -1 && ShowHiddenFrames) {
+      line_number = bci + 1000000;
     }
   }
-
-  return buf;
-}
-
-
-void java_lang_Throwable::print_stack_element(Handle stream, Method* method, int bci) {
-  ResourceMark rm;
-  char* buf = print_stack_element_to_buffer(method, bci);
-  print_to_stream(stream, buf);
-}
-
-void java_lang_Throwable::print_stack_element(outputStream *st, Method* method, int bci) {
-  ResourceMark rm;
-  char* buf = print_stack_element_to_buffer(method, bci);
-  st->print_cr("%s", buf);
-}
-
-void java_lang_Throwable::print_to_stream(Handle stream, const char* str) {
-  if (stream.is_null()) {
-    tty->print_cr("%s", str);
-  } else {
-    EXCEPTION_MARK;
-    JavaValue result(T_VOID);
-    Handle arg (THREAD, oopFactory::new_charArray(str, THREAD));
-    if (!HAS_PENDING_EXCEPTION) {
-      JavaCalls::call_virtual(&result,
-                              stream,
-                              KlassHandle(THREAD, stream->klass()),
-                              vmSymbols::println_name(),
-                              vmSymbols::char_array_void_signature(),
-                              arg,
-                              THREAD);
-    }
-    // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM.
-    if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION;
-  }
-
-}
-
-
-const char* java_lang_Throwable::no_stack_trace_message() {
-  return "\t<<no stack trace available>>";
-}
-
-
-// Currently used only for exceptions occurring during startup
-void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) {
-  Thread *THREAD = Thread::current();
-  Handle h_throwable(THREAD, throwable);
-  while (h_throwable.not_null()) {
-    objArrayHandle result (THREAD, objArrayOop(backtrace(h_throwable())));
-    if (result.is_null()) {
-      st->print_cr(no_stack_trace_message());
-      return;
-    }
-
-    while (result.not_null()) {
-      typeArrayHandle methods (THREAD,
-                               typeArrayOop(result->obj_at(trace_methods_offset)));
-      typeArrayHandle bcis (THREAD,
-                            typeArrayOop(result->obj_at(trace_bcis_offset)));
-
-      if (methods.is_null() || bcis.is_null()) {
-        st->print_cr(no_stack_trace_message());
-        return;
-      }
-
-      int length = methods()->length();
-      for (int index = 0; index < length; index++) {
-        Method* method = ((Method*)methods()->metadata_at(index));
-        if (method == NULL) goto handle_cause;
-        int bci = bcis->ushort_at(index);
-        print_stack_element(st, method, bci);
-      }
-      result = objArrayHandle(THREAD, objArrayOop(result->obj_at(trace_next_offset)));
-    }
-  handle_cause:
-    {
-      EXCEPTION_MARK;
-      JavaValue result(T_OBJECT);
-      JavaCalls::call_virtual(&result,
-                              h_throwable,
-                              KlassHandle(THREAD, h_throwable->klass()),
-                              vmSymbols::getCause_name(),
-                              vmSymbols::void_throwable_signature(),
-                              THREAD);
-      // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM.
-      if (HAS_PENDING_EXCEPTION) {
-        CLEAR_PENDING_EXCEPTION;
-        h_throwable = Handle();
-      } else {
-        h_throwable = Handle(THREAD, (oop) result.get_jobject());
-        if (h_throwable.not_null()) {
-          st->print("Caused by: ");
-          print(h_throwable, st);
-          st->cr();
-        }
-      }
-    }
-  }
-}
-
-
-void java_lang_Throwable::print_stack_trace(oop throwable, oop print_stream) {
-  // Note: this is no longer used in Merlin, but we support it for compatibility.
-  Thread *thread = Thread::current();
-  Handle stream(thread, print_stream);
-  objArrayHandle result (thread, objArrayOop(backtrace(throwable)));
-  if (result.is_null()) {
-    print_to_stream(stream, no_stack_trace_message());
-    return;
-  }
-
-  while (result.not_null()) {
-    typeArrayHandle methods(thread,
-                            typeArrayOop(result->obj_at(trace_methods_offset)));
-    typeArrayHandle bcis (thread,
-                          typeArrayOop(result->obj_at(trace_bcis_offset)));
-
-    if (methods.is_null() || bcis.is_null()) {
-      print_to_stream(stream, no_stack_trace_message());
-      return;
-    }
-
-    int length = methods()->length();
-    for (int index = 0; index < length; index++) {
-      Method* method = ((Method*)methods()->metadata_at(index));
-      if (method == NULL) return;
-      int bci = bcis->ushort_at(index);
-      print_stack_element(stream, method, bci);
-    }
-    result = objArrayHandle(thread, objArrayOop(result->obj_at(trace_next_offset)));
-  }
+  return line_number;
 }
 
 // This class provides a simple wrapper over the internal structure of
@@ -1350,13 +1214,30 @@
 
   enum {
     trace_methods_offset = java_lang_Throwable::trace_methods_offset,
-    trace_bcis_offset    = java_lang_Throwable::trace_bcis_offset,
+    trace_bcis_offset = java_lang_Throwable::trace_bcis_offset,
     trace_mirrors_offset = java_lang_Throwable::trace_mirrors_offset,
     trace_next_offset    = java_lang_Throwable::trace_next_offset,
     trace_size           = java_lang_Throwable::trace_size,
     trace_chunk_size     = java_lang_Throwable::trace_chunk_size
   };
 
+  // get info out of chunks
+  static typeArrayOop get_methods(objArrayHandle chunk) {
+    typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset));
+    assert(methods != NULL, "method array should be initialized in backtrace");
+    return methods;
+  }
+  static typeArrayOop get_bcis(objArrayHandle chunk) {
+    typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset));
+    assert(bcis != NULL, "bci array should be initialized in backtrace");
+    return bcis;
+  }
+  static objArrayOop get_mirrors(objArrayHandle chunk) {
+    objArrayOop mirrors = objArrayOop(chunk->obj_at(trace_mirrors_offset));
+    assert(mirrors != NULL, "mirror array should be initialized in backtrace");
+    return mirrors;
+  }
+
   // constructor for new backtrace
   BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL), _mirrors(NULL) {
     expand(CHECK);
@@ -1364,6 +1245,19 @@
     _index = 0;
   }
 
+  BacktraceBuilder(objArrayHandle backtrace) {
+    _methods = get_methods(backtrace);
+    _bcis = get_bcis(backtrace);
+    _mirrors = get_mirrors(backtrace);
+    assert(_methods->length() == _bcis->length() &&
+           _methods->length() == _mirrors->length(),
+           "method and source information arrays should match");
+
+    // head is the preallocated backtrace
+    _backtrace = _head = backtrace();
+    _index = 0;
+  }
+
   void expand(TRAPS) {
     objArrayHandle old_head(THREAD, _head);
     Pause_No_Safepoint_Verifier pnsv(&_nsv);
@@ -1371,10 +1265,10 @@
     objArrayOop head = oopFactory::new_objectArray(trace_size, CHECK);
     objArrayHandle new_head(THREAD, head);
 
-    typeArrayOop methods = oopFactory::new_metaDataArray(trace_chunk_size, CHECK);
+    typeArrayOop methods = oopFactory::new_shortArray(trace_chunk_size, CHECK);
     typeArrayHandle new_methods(THREAD, methods);
 
-    typeArrayOop bcis = oopFactory::new_shortArray(trace_chunk_size, CHECK);
+    typeArrayOop bcis = oopFactory::new_intArray(trace_chunk_size, CHECK);
     typeArrayHandle new_bcis(THREAD, bcis);
 
     objArrayOop mirrors = oopFactory::new_objectArray(trace_chunk_size, CHECK);
@@ -1389,7 +1283,7 @@
 
     _head    = new_head();
     _methods = new_methods();
-    _bcis    = new_bcis();
+    _bcis = new_bcis();
     _mirrors = new_mirrors();
     _index = 0;
   }
@@ -1403,7 +1297,6 @@
     // shorts.  The later line number lookup would just smear the -1
     // to a 0 even if it could be recorded.
     if (bci == SynchronizationEntryBCI) bci = 0;
-    assert(bci == (jushort)bci, "doesn't fit");
 
     if (_index >= trace_chunk_size) {
       methodHandle mhandle(THREAD, method);
@@ -1411,26 +1304,148 @@
       method = mhandle();
     }
 
-    _methods->metadata_at_put(_index, method);
-    _bcis->ushort_at_put(_index, bci);
-    // we need to save the mirrors in the backtrace to keep the methods from
-    // being unloaded if their class loader is unloaded while we still have
-    // this stack trace.
+    _methods->short_at_put(_index, method->method_idnum());
+    _bcis->int_at_put(_index, merge_bci_and_version(bci, method->constants()->version()));
+
+    // We need to save the mirrors in the backtrace to keep the class
+    // from being unloaded while we still have this stack trace.
+    assert(method->method_holder()->java_mirror() != NULL, "never push null for mirror");
     _mirrors->obj_at_put(_index, method->method_holder()->java_mirror());
     _index++;
   }
 
-  Method* current_method() {
-    assert(_index >= 0 && _index < trace_chunk_size, "out of range");
-    return ((Method*)_methods->metadata_at(_index));
+};
+
+// Print stack trace element to resource allocated buffer
+char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror,
+                                  int method_id, int version, int bci) {
+
+  // Get strings and string lengths
+  InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror()));
+  const char* klass_name  = holder->external_name();
+  int buf_len = (int)strlen(klass_name);
+
+  // pushing to the stack trace added one.
+  Method* method = holder->method_with_idnum(method_id);
+  char* method_name = method->name()->as_C_string();
+  buf_len += (int)strlen(method_name);
+
+  char* source_file_name = NULL;
+  if (version_matches(method, version)) {
+    Symbol* source = holder->source_file_name();
+    if (source != NULL) {
+      source_file_name = source->as_C_string();
+      buf_len += (int)strlen(source_file_name);
+    }
   }
 
-  jushort current_bci() {
-    assert(_index >= 0 && _index < trace_chunk_size, "out of range");
-    return _bcis->ushort_at(_index);
+  // Allocate temporary buffer with extra space for formatting and line number
+  char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64);
+
+  // Print stack trace line in buffer
+  sprintf(buf, "\tat %s.%s", klass_name, method_name);
+
+  if (!version_matches(method, version)) {
+    strcat(buf, "(Redefined)");
+  } else {
+    int line_number = get_line_number(method, bci);
+    if (line_number == -2) {
+      strcat(buf, "(Native Method)");
+    } else {
+      if (source_file_name != NULL && (line_number != -1)) {
+        // Sourcename and linenumber
+        sprintf(buf + (int)strlen(buf), "(%s:%d)", source_file_name, line_number);
+      } else if (source_file_name != NULL) {
+        // Just sourcename
+        sprintf(buf + (int)strlen(buf), "(%s)", source_file_name);
+      } else {
+        // Neither sourcename nor linenumber
+        sprintf(buf + (int)strlen(buf), "(Unknown Source)");
+      }
+      nmethod* nm = method->code();
+      if (WizardMode && nm != NULL) {
+        sprintf(buf + (int)strlen(buf), "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm);
+      }
+    }
   }
-};
-
+
+  return buf;
+}
+
+void java_lang_Throwable::print_stack_element(outputStream *st, Handle mirror,
+                                              int method_id, int version, int bci) {
+  ResourceMark rm;
+  char* buf = print_stack_element_to_buffer(mirror, method_id, version, bci);
+  st->print_cr("%s", buf);
+}
+
+void java_lang_Throwable::print_stack_element(outputStream *st, methodHandle method, int bci) {
+  Handle mirror = method->method_holder()->java_mirror();
+  int method_id = method->method_idnum();
+  int version = method->constants()->version();
+  print_stack_element(st, mirror, method_id, version, bci);
+}
+
+const char* java_lang_Throwable::no_stack_trace_message() {
+  return "\t<<no stack trace available>>";
+}
+
+
+// Currently used only for exceptions occurring during startup
+void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) {
+  Thread *THREAD = Thread::current();
+  Handle h_throwable(THREAD, throwable);
+  while (h_throwable.not_null()) {
+    objArrayHandle result (THREAD, objArrayOop(backtrace(h_throwable())));
+    if (result.is_null()) {
+      st->print_cr(no_stack_trace_message());
+      return;
+    }
+
+    while (result.not_null()) {
+
+      // Get method id, bci, version and mirror from chunk
+      typeArrayHandle methods (THREAD, BacktraceBuilder::get_methods(result));
+      typeArrayHandle bcis (THREAD, BacktraceBuilder::get_bcis(result));
+      objArrayHandle mirrors (THREAD, BacktraceBuilder::get_mirrors(result));
+
+      int length = methods()->length();
+      for (int index = 0; index < length; index++) {
+        Handle mirror(THREAD, mirrors->obj_at(index));
+        // NULL mirror means end of stack trace
+        if (mirror.is_null()) goto handle_cause;
+        int method = methods->short_at(index);
+        int version = version_at(bcis->int_at(index));
+        int bci = bci_at(bcis->int_at(index));
+        print_stack_element(st, mirror, method, version, bci);
+      }
+      result = objArrayHandle(THREAD, objArrayOop(result->obj_at(trace_next_offset)));
+    }
+  handle_cause:
+    {
+      EXCEPTION_MARK;
+      JavaValue cause(T_OBJECT);
+      JavaCalls::call_virtual(&cause,
+                              h_throwable,
+                              KlassHandle(THREAD, h_throwable->klass()),
+                              vmSymbols::getCause_name(),
+                              vmSymbols::void_throwable_signature(),
+                              THREAD);
+      // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM.
+      if (HAS_PENDING_EXCEPTION) {
+        CLEAR_PENDING_EXCEPTION;
+        h_throwable = Handle();
+      } else {
+        h_throwable = Handle(THREAD, (oop) cause.get_jobject());
+        if (h_throwable.not_null()) {
+          st->print("Caused by: ");
+          print(h_throwable, st);
+          st->cr();
+        }
+      }
+    }
+  }
+}
 
 void java_lang_Throwable::fill_in_stack_trace(Handle throwable, methodHandle method, TRAPS) {
   if (!StackTraceInThrowable) return;
@@ -1591,21 +1606,8 @@
 
   // No-op if stack trace is disabled
   if (!StackTraceInThrowable) return;
-
-  objArrayOop h_oop = oopFactory::new_objectArray(trace_size, CHECK);
-  objArrayHandle backtrace  (THREAD, h_oop);
-  typeArrayOop m_oop = oopFactory::new_metaDataArray(trace_chunk_size, CHECK);
-  typeArrayHandle methods (THREAD, m_oop);
-  typeArrayOop b = oopFactory::new_shortArray(trace_chunk_size, CHECK);
-  typeArrayHandle bcis(THREAD, b);
-  objArrayOop mirror_oop = oopFactory::new_objectArray(trace_chunk_size, CHECK);
-  objArrayHandle mirrors (THREAD, mirror_oop);
-
-  // backtrace has space for one chunk (next is NULL)
-  backtrace->obj_at_put(trace_methods_offset, methods());
-  backtrace->obj_at_put(trace_bcis_offset, bcis());
-  backtrace->obj_at_put(trace_mirrors_offset, mirrors());
-  set_backtrace(throwable(), backtrace());
+  BacktraceBuilder bt(CHECK);   // creates a backtrace
+  set_backtrace(throwable(), bt.backtrace());
 }
 
 
@@ -1617,48 +1619,26 @@
 
   assert(throwable->is_a(SystemDictionary::Throwable_klass()), "sanity check");
 
-  objArrayOop backtrace = (objArrayOop)java_lang_Throwable::backtrace(throwable());
-  assert(backtrace != NULL, "backtrace not preallocated");
-
-  oop m = backtrace->obj_at(trace_methods_offset);
-  typeArrayOop methods = typeArrayOop(m);
-  assert(methods != NULL && methods->length() > 0, "method array not preallocated");
-
-  oop b = backtrace->obj_at(trace_bcis_offset);
-  typeArrayOop bcis = typeArrayOop(b);
-  assert(bcis != NULL, "bci array not preallocated");
-
-  oop mr = backtrace->obj_at(trace_mirrors_offset);
-  objArrayOop mirrors = objArrayOop(mr);
-  assert(mirrors != NULL, "bci array not preallocated");
-
-  assert(methods->length() == bcis->length() &&
-         methods->length() == mirrors->length(),
-         "method and bci arrays should match");
-
-  JavaThread* thread = JavaThread::current();
-  ResourceMark rm(thread);
-  vframeStream st(thread);
+  JavaThread* THREAD = JavaThread::current();
+
+  objArrayHandle backtrace (THREAD, (objArrayOop)java_lang_Throwable::backtrace(throwable()));
+  assert(backtrace.not_null(), "backtrace should have been preallocated");
+
+  ResourceMark rm(THREAD);
+  vframeStream st(THREAD);
+
+  BacktraceBuilder bt(backtrace);
 
   // Unlike fill_in_stack_trace we do not skip fillInStackTrace or throwable init
   // methods as preallocated errors aren't created by "java" code.
 
   // fill in as much stack trace as possible
+  typeArrayOop methods = BacktraceBuilder::get_methods(backtrace);
   int max_chunks = MIN2(methods->length(), (int)MaxJavaStackTraceDepth);
   int chunk_count = 0;
 
   for (;!st.at_end(); st.next()) {
-    // Add entry and smear the -1 bci to 0 since the array only holds
-    // unsigned shorts.  The later line number lookup would just smear
-    // the -1 to a 0 even if it could be recorded.
-    int bci = st.bci();
-    if (bci == SynchronizationEntryBCI) bci = 0;
-    assert(bci == (jushort)bci, "doesn't fit");
-    bcis->ushort_at_put(chunk_count, bci);
-    methods->metadata_at_put(chunk_count, st.method());
-    mirrors->obj_at_put(chunk_count,
-                   st.method()->method_holder()->java_mirror());
-
+    bt.push(st.method(), st.bci(), CHECK);
     chunk_count++;
 
     // Bail-out for deep stacks
@@ -1672,7 +1652,6 @@
       java_lang_Throwable::set_stacktrace(throwable(), java_lang_Throwable::unassigned_stacktrace());
       assert(java_lang_Throwable::unassigned_stacktrace() != NULL, "not initialized");
   }
-
 }
 
 
@@ -1691,12 +1670,12 @@
       chunk = next;
     }
     assert(chunk != NULL && chunk->obj_at(trace_next_offset) == NULL, "sanity check");
-    // Count element in remaining partial chunk
-    typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset));
-    typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset));
-    assert(methods != NULL && bcis != NULL, "sanity check");
-    for (int i = 0; i < methods->length(); i++) {
-      if (methods->metadata_at(i) == NULL) break;
+    // Count element in remaining partial chunk.  NULL value for mirror
+    // marks the end of the stack trace elements that are saved.
+    objArrayOop mirrors = BacktraceBuilder::get_mirrors(chunk);
+    assert(mirrors != NULL, "sanity check");
+    for (int i = 0; i < mirrors->length(); i++) {
+      if (mirrors->obj_at(i) == NULL) break;
       depth++;
     }
   }
@@ -1722,25 +1701,28 @@
   if (chunk == NULL) {
     THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL);
   }
-  // Get method,bci from chunk
-  typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset));
-  typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset));
-  assert(methods != NULL && bcis != NULL, "sanity check");
-  methodHandle method(THREAD, ((Method*)methods->metadata_at(chunk_index)));
-  int bci = bcis->ushort_at(chunk_index);
+  // Get method id, bci, version and mirror from chunk
+  typeArrayOop methods = BacktraceBuilder::get_methods(chunk);
+  typeArrayOop bcis = BacktraceBuilder::get_bcis(chunk);
+  objArrayOop mirrors = BacktraceBuilder::get_mirrors(chunk);
+
+  assert(methods != NULL && bcis != NULL && mirrors != NULL, "sanity check");
+
+  int method = methods->short_at(chunk_index);
+  int version = version_at(bcis->int_at(chunk_index));
+  int bci = bci_at(bcis->int_at(chunk_index));
+  Handle mirror(THREAD, mirrors->obj_at(chunk_index));
+
   // Chunk can be partial full
-  if (method.is_null()) {
+  if (mirror.is_null()) {
     THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL);
   }
 
-  oop element = java_lang_StackTraceElement::create(method, bci, CHECK_0);
+  oop element = java_lang_StackTraceElement::create(mirror, method, version, bci, CHECK_0);
   return element;
 }
 
-oop java_lang_StackTraceElement::create(methodHandle method, int bci, TRAPS) {
-  // SystemDictionary::stackTraceElement_klass() will be null for pre-1.4 JDKs
-  assert(JDK_Version::is_gte_jdk14x_version(), "should only be called in >= 1.4");
-
+oop java_lang_StackTraceElement::create(Handle mirror, int method_id, int version, int bci, TRAPS) {
   // Allocate java.lang.StackTraceElement instance
   Klass* k = SystemDictionary::StackTraceElement_klass();
   assert(k != NULL, "must be loaded in 1.4+");
@@ -1752,37 +1734,39 @@
   Handle element = ik->allocate_instance_handle(CHECK_0);
   // Fill in class name
   ResourceMark rm(THREAD);
-  const char* str = method->method_holder()->external_name();
+  InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror()));
+  const char* str = holder->external_name();
   oop classname = StringTable::intern((char*) str, CHECK_0);
   java_lang_StackTraceElement::set_declaringClass(element(), classname);
+
   // Fill in method name
+  Method* method = holder->method_with_idnum(method_id);
   oop methodname = StringTable::intern(method->name(), CHECK_0);
   java_lang_StackTraceElement::set_methodName(element(), methodname);
-  // Fill in source file name
-  Symbol* source = method->method_holder()->source_file_name();
-  if (ShowHiddenFrames && source == NULL)
-    source = vmSymbols::unknown_class_name();
-  oop filename = StringTable::intern(source, CHECK_0);
-  java_lang_StackTraceElement::set_fileName(element(), filename);
-  // File in source line number
-  int line_number;
-  if (method->is_native()) {
-    // Negative value different from -1 below, enabling Java code in
-    // class java.lang.StackTraceElement to distinguish "native" from
-    // "no LineNumberTable".
-    line_number = -2;
+
+  if (!version_matches(method, version)) {
+    // The method was redefined, accurate line number information isn't available
+    java_lang_StackTraceElement::set_fileName(element(), NULL);
+    java_lang_StackTraceElement::set_lineNumber(element(), -1);
   } else {
-    // Returns -1 if no LineNumberTable, and otherwise actual line number
-    line_number = method->line_number_from_bci(bci);
-    if (line_number == -1 && ShowHiddenFrames) {
-      line_number = bci + 1000000;
-    }
+    // Fill in source file name and line number.
+    Symbol* source = holder->source_file_name();
+    if (ShowHiddenFrames && source == NULL)
+      source = vmSymbols::unknown_class_name();
+    oop filename = StringTable::intern(source, CHECK_0);
+    java_lang_StackTraceElement::set_fileName(element(), filename);
+
+    int line_number = get_line_number(method, bci);
+    java_lang_StackTraceElement::set_lineNumber(element(), line_number);
   }
-  java_lang_StackTraceElement::set_lineNumber(element(), line_number);
-
   return element();
 }
 
+oop java_lang_StackTraceElement::create(methodHandle method, int bci, TRAPS) {
+  Handle mirror (THREAD, method->method_holder()->java_mirror());
+  int method_id = method->method_idnum();
+  return create(mirror, method_id, method->constants()->version(), bci, THREAD);
+}
 
 void java_lang_reflect_AccessibleObject::compute_offsets() {
   Klass* k = SystemDictionary::reflect_AccessibleObject_klass();
--- a/src/share/vm/classfile/javaClasses.hpp	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/classfile/javaClasses.hpp	Thu Jan 17 13:40:31 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
@@ -469,8 +469,7 @@
   static int static_unassigned_stacktrace_offset;
 
   // Printing
-  static char* print_stack_element_to_buffer(Method* method, int bci);
-  static void print_to_stream(Handle stream, const char* str);
+  static char* print_stack_element_to_buffer(Handle mirror, int method, int version, int bci);
   // StackTrace (programmatic access, new since 1.4)
   static void clear_stacktrace(oop throwable);
   // No stack trace available
@@ -490,12 +489,9 @@
   static oop message(oop throwable);
   static oop message(Handle throwable);
   static void set_message(oop throwable, oop value);
-  // Print stack trace stored in exception by call-back to Java
-  // Note: this is no longer used in Merlin, but we still suppport
-  // it for compatibility.
-  static void print_stack_trace(oop throwable, oop print_stream);
-  static void print_stack_element(Handle stream, Method* method, int bci);
-  static void print_stack_element(outputStream *st, Method* method, int bci);
+  static void print_stack_element(outputStream *st, Handle mirror, int method,
+                                  int version, int bci);
+  static void print_stack_element(outputStream *st, methodHandle method, int bci);
   static void print_stack_usage(Handle stream);
 
   // Allocate space for backtrace (created but stack trace not filled in)
@@ -1263,7 +1259,8 @@
   static void set_lineNumber(oop element, int value);
 
   // Create an instance of StackTraceElement
-  static oop create(methodHandle m, int bci, TRAPS);
+  static oop create(Handle mirror, int method, int version, int bci, TRAPS);
+  static oop create(methodHandle method, int bci, TRAPS);
 
   // Debugging
   friend class JavaClasses;
--- a/src/share/vm/oops/constantPool.cpp	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/oops/constantPool.cpp	Thu Jan 17 13:40:31 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
@@ -66,7 +66,7 @@
   set_pool_holder(NULL);
   set_flags(0);
   // only set to non-zero if constant pool is merged by RedefineClasses
-  set_orig_length(0);
+  set_version(0);
   set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock"));
   // all fields are initialized; needed for GC
   set_on_stack(false);
--- a/src/share/vm/oops/constantPool.hpp	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/oops/constantPool.hpp	Thu Jan 17 13:40:31 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
@@ -103,8 +103,8 @@
   union {
     // set for CDS to restore resolved references
     int                _resolved_reference_length;
-  // only set to non-zero if constant pool is merged by RedefineClasses
-  int                  _orig_length;
+    // keeps version number for redefined classes (used in backtrace)
+    int                _version;
   } _saved;
 
   Monitor*             _lock;
@@ -784,8 +784,11 @@
   static void copy_cp_to_impl(constantPoolHandle from_cp, int start_i, int end_i, constantPoolHandle to_cp, int to_i, TRAPS);
   static void copy_entry_to(constantPoolHandle from_cp, int from_i, constantPoolHandle to_cp, int to_i, TRAPS);
   int  find_matching_entry(int pattern_i, constantPoolHandle search_cp, TRAPS);
-  int  orig_length() const                { return _saved._orig_length; }
-  void set_orig_length(int orig_length)   { _saved._orig_length = orig_length; }
+  int  version() const                    { return _saved._version; }
+  void set_version(int version)           { _saved._version = version; }
+  void increment_and_save_version(int version) {
+    _saved._version = version >= 0 ? version++ : version;  // keep overflow
+  }
 
   void set_resolved_reference_length(int length) { _saved._resolved_reference_length = length; }
   int  resolved_reference_length() const  { return _saved._resolved_reference_length; }
--- a/src/share/vm/prims/jvm.cpp	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/prims/jvm.cpp	Thu Jan 17 13:40:31 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
@@ -484,15 +484,6 @@
 JVM_END
 
 
-JVM_ENTRY(void, JVM_PrintStackTrace(JNIEnv *env, jobject receiver, jobject printable))
-  JVMWrapper("JVM_PrintStackTrace");
-  // Note: This is no longer used in Merlin, but we still support it for compatibility.
-  oop exception = JNIHandles::resolve_non_null(receiver);
-  oop stream    = JNIHandles::resolve_non_null(printable);
-  java_lang_Throwable::print_stack_trace(exception, stream);
-JVM_END
-
-
 JVM_ENTRY(jint, JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable))
   JVMWrapper("JVM_GetStackTraceDepth");
   oop exception = JNIHandles::resolve(throwable);
--- a/src/share/vm/prims/jvm.h	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/prims/jvm.h	Thu Jan 17 13:40:31 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
@@ -212,9 +212,6 @@
 JNIEXPORT void JNICALL
 JVM_FillInStackTrace(JNIEnv *env, jobject throwable);
 
-JNIEXPORT void JNICALL
-JVM_PrintStackTrace(JNIEnv *env, jobject throwable, jobject printable);
-
 JNIEXPORT jint JNICALL
 JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable);
 
--- a/src/share/vm/prims/jvmtiRedefineClasses.cpp	Thu Jan 17 10:25:16 2013 -0500
+++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp	Thu Jan 17 13:40:31 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, 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
@@ -1334,20 +1334,8 @@
     return JVMTI_ERROR_INTERNAL;
   }
 
-  int orig_length = old_cp->orig_length();
-  if (orig_length == 0) {
-    // This old_cp is an actual original constant pool. We save
-    // the original length in the merged constant pool so that
-    // merge_constant_pools() can be more efficient. If a constant
-    // pool has a non-zero orig_length() value, then that constant
-    // pool was created by a merge operation in RedefineClasses.
-    merge_cp->set_orig_length(old_cp->length());
-  } else {
-    // This old_cp is a merged constant pool from a previous
-    // RedefineClasses() calls so just copy the orig_length()
-    // value.
-    merge_cp->set_orig_length(old_cp->orig_length());
-  }
+  // Update the version number of the constant pool
+  merge_cp->increment_and_save_version(old_cp->version());
 
   ResourceMark rm(THREAD);
   _index_map_count = 0;
@@ -2417,18 +2405,19 @@
        int scratch_cp_length, TRAPS) {
   assert(scratch_cp->length() >= scratch_cp_length, "sanity check");
 
-    // scratch_cp is a merged constant pool and has enough space for a
-    // worst case merge situation. We want to associate the minimum
-    // sized constant pool with the klass to save space.
-    constantPoolHandle smaller_cp(THREAD,
-    ConstantPool::allocate(loader_data, scratch_cp_length,
-                                   THREAD));
-    // preserve orig_length() value in the smaller copy
-    int orig_length = scratch_cp->orig_length();
-    assert(orig_length != 0, "sanity check");
-    smaller_cp->set_orig_length(orig_length);
-    scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD);
-    scratch_cp = smaller_cp;
+  // scratch_cp is a merged constant pool and has enough space for a
+  // worst case merge situation. We want to associate the minimum
+  // sized constant pool with the klass to save space.
+  constantPoolHandle smaller_cp(THREAD,
+          ConstantPool::allocate(loader_data, scratch_cp_length, THREAD));
+
+  // preserve version() value in the smaller copy
+  int version = scratch_cp->version();
+  assert(version != 0, "sanity check");
+  smaller_cp->set_version(version);
+
+  scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD);
+  scratch_cp = smaller_cp;
 
   // attach new constant pool to klass
   scratch_cp->set_pool_holder(scratch_class());