aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorminqi <none@none>2012-11-12 14:03:53 -0800
committerminqi <none@none>2012-11-12 14:03:53 -0800
commita0a063373b4bea636f530a6f1d8535108015c0f0 (patch)
tree771f25e40ff64de77f93c22b658957881cab87ea /src
parentf2147cb21125cde235827d0a9f9c340738cd807f (diff)
6830717: replay of compilations would help with debugging
Summary: When java process crashed in compiler thread, repeat the compilation process will help finding root cause. This is done with using SA dump application class data and replay data from core dump, then use debug version of jvm to recompile the problematic java method. Reviewed-by: kvn, twisti, sspitsyn Contributed-by: yumin.qi@oracle.com
Diffstat (limited to 'src')
-rw-r--r--src/share/vm/ci/ciClassList.hpp1
-rw-r--r--src/share/vm/ci/ciEnv.cpp46
-rw-r--r--src/share/vm/ci/ciEnv.hpp9
-rw-r--r--src/share/vm/ci/ciInstanceKlass.cpp111
-rw-r--r--src/share/vm/ci/ciInstanceKlass.hpp3
-rw-r--r--src/share/vm/ci/ciMetadata.hpp1
-rw-r--r--src/share/vm/ci/ciMethod.cpp47
-rw-r--r--src/share/vm/ci/ciMethod.hpp5
-rw-r--r--src/share/vm/ci/ciMethodData.cpp79
-rw-r--r--src/share/vm/ci/ciMethodData.hpp2
-rw-r--r--src/share/vm/ci/ciObject.hpp1
-rw-r--r--src/share/vm/ci/ciObjectFactory.hpp1
-rw-r--r--src/share/vm/ci/ciReplay.cpp942
-rw-r--r--src/share/vm/ci/ciReplay.hpp55
-rw-r--r--src/share/vm/ci/ciSymbol.cpp5
-rw-r--r--src/share/vm/ci/ciSymbol.hpp3
-rw-r--r--src/share/vm/ci/ciUtilities.hpp5
-rw-r--r--src/share/vm/classfile/javaClasses.cpp16
-rw-r--r--src/share/vm/classfile/javaClasses.hpp2
-rw-r--r--src/share/vm/code/dependencies.cpp1
-rw-r--r--src/share/vm/interpreter/invocationCounter.hpp5
-rw-r--r--src/share/vm/oops/instanceKlass.cpp7
-rw-r--r--src/share/vm/oops/instanceKlass.hpp1
-rw-r--r--src/share/vm/oops/symbol.cpp20
-rw-r--r--src/share/vm/oops/symbol.hpp2
-rw-r--r--src/share/vm/opto/bytecodeInfo.cpp13
-rw-r--r--src/share/vm/prims/jni.cpp2
-rw-r--r--src/share/vm/prims/jvmtiExport.hpp2
-rw-r--r--src/share/vm/runtime/compilationPolicy.cpp13
-rw-r--r--src/share/vm/runtime/globals.hpp20
-rw-r--r--src/share/vm/runtime/vmStructs.cpp1
-rw-r--r--src/share/vm/utilities/array.hpp6
-rw-r--r--src/share/vm/utilities/utf8.cpp149
-rw-r--r--src/share/vm/utilities/utf8.hpp32
-rw-r--r--src/share/vm/utilities/vmError.cpp9
35 files changed, 1576 insertions, 41 deletions
diff --git a/src/share/vm/ci/ciClassList.hpp b/src/share/vm/ci/ciClassList.hpp
index 7272ea875..71e5dd18c 100644
--- a/src/share/vm/ci/ciClassList.hpp
+++ b/src/share/vm/ci/ciClassList.hpp
@@ -106,6 +106,7 @@ friend class ciSymbol; \
friend class ciArray; \
friend class ciObjArray; \
friend class ciMetadata; \
+friend class ciReplay; \
friend class ciTypeArray; \
friend class ciType; \
friend class ciReturnAddress; \
diff --git a/src/share/vm/ci/ciEnv.cpp b/src/share/vm/ci/ciEnv.cpp
index 0465b9cf9..878313d11 100644
--- a/src/share/vm/ci/ciEnv.cpp
+++ b/src/share/vm/ci/ciEnv.cpp
@@ -30,6 +30,7 @@
#include "ci/ciInstanceKlass.hpp"
#include "ci/ciMethod.hpp"
#include "ci/ciNullObject.hpp"
+#include "ci/ciReplay.hpp"
#include "ci/ciUtilities.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
@@ -772,6 +773,11 @@ ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool,
: !m->method_holder()->is_loaded())) {
m = NULL;
}
+#ifdef ASSERT
+ if (m != NULL && ReplayCompiles && !ciReplay::is_loaded(m)) {
+ m = NULL;
+ }
+#endif
if (m != NULL) {
// We found the method.
return get_method(m);
@@ -1144,3 +1150,43 @@ void ciEnv::record_out_of_memory_failure() {
// If memory is low, we stop compiling methods.
record_method_not_compilable("out of memory");
}
+
+fileStream* ciEnv::_replay_data_stream = NULL;
+
+void ciEnv::dump_replay_data() {
+ VM_ENTRY_MARK;
+ MutexLocker ml(Compile_lock);
+ if (_replay_data_stream == NULL) {
+ _replay_data_stream = new (ResourceObj::C_HEAP, mtCompiler) fileStream(ReplayDataFile);
+ if (_replay_data_stream == NULL) {
+ fatal(err_msg("Can't open %s for replay data", ReplayDataFile));
+ }
+ }
+ dump_replay_data(_replay_data_stream);
+}
+
+
+void ciEnv::dump_replay_data(outputStream* out) {
+ ASSERT_IN_VM;
+
+#if INCLUDE_JVMTI
+ out->print_cr("JvmtiExport can_access_local_variables %d", _jvmti_can_access_local_variables);
+ out->print_cr("JvmtiExport can_hotswap_or_post_breakpoint %d", _jvmti_can_hotswap_or_post_breakpoint);
+ out->print_cr("JvmtiExport can_post_on_exceptions %d", _jvmti_can_post_on_exceptions);
+#endif // INCLUDE_JVMTI
+
+ GrowableArray<ciMetadata*>* objects = _factory->get_ci_metadata();
+ out->print_cr("# %d ciObject found", objects->length());
+ for (int i = 0; i < objects->length(); i++) {
+ objects->at(i)->dump_replay_data(out);
+ }
+ Method* method = task()->method();
+ int entry_bci = task()->osr_bci();
+ // Klass holder = method->method_holder();
+ out->print_cr("compile %s %s %s %d",
+ method->klass_name()->as_quoted_ascii(),
+ method->name()->as_quoted_ascii(),
+ method->signature()->as_quoted_ascii(),
+ entry_bci);
+ out->flush();
+}
diff --git a/src/share/vm/ci/ciEnv.hpp b/src/share/vm/ci/ciEnv.hpp
index f252dc7c0..042bc32fa 100644
--- a/src/share/vm/ci/ciEnv.hpp
+++ b/src/share/vm/ci/ciEnv.hpp
@@ -46,6 +46,8 @@ class ciEnv : StackObj {
friend class CompileBroker;
friend class Dependencies; // for get_object, during logging
+ static fileStream* _replay_data_stream;
+
private:
Arena* _arena; // Alias for _ciEnv_arena except in init_shared_objects()
Arena _ciEnv_arena;
@@ -448,6 +450,13 @@ public:
// RedefineClasses support
void metadata_do(void f(Metadata*)) { _factory->metadata_do(f); }
+
+ // Dump the compilation replay data for this ciEnv to
+ // ReplayDataFile, creating the file if needed.
+ void dump_replay_data();
+
+ // Dump the compilation replay data for the ciEnv to the stream.
+ void dump_replay_data(outputStream* out);
};
#endif // SHARE_VM_CI_CIENV_HPP
diff --git a/src/share/vm/ci/ciInstanceKlass.cpp b/src/share/vm/ci/ciInstanceKlass.cpp
index 7a3eb328f..de5e973bf 100644
--- a/src/share/vm/ci/ciInstanceKlass.cpp
+++ b/src/share/vm/ci/ciInstanceKlass.cpp
@@ -561,3 +561,114 @@ ciInstanceKlass* ciInstanceKlass::implementor() {
}
return impl;
}
+
+// Utility class for printing of the contents of the static fields for
+// use by compilation replay. It only prints out the information that
+// could be consumed by the compiler, so for primitive types it prints
+// out the actual value. For Strings it's the actual string value.
+// For array types it it's first level array size since that's the
+// only value which statically unchangeable. For all other reference
+// types it simply prints out the dynamic type.
+
+class StaticFinalFieldPrinter : public FieldClosure {
+ outputStream* _out;
+ const char* _holder;
+ public:
+ StaticFinalFieldPrinter(outputStream* out, const char* holder) :
+ _out(out),
+ _holder(holder) {
+ }
+ void do_field(fieldDescriptor* fd) {
+ if (fd->is_final() && !fd->has_initial_value()) {
+ oop mirror = fd->field_holder()->java_mirror();
+ _out->print("staticfield %s %s %s ", _holder, fd->name()->as_quoted_ascii(), fd->signature()->as_quoted_ascii());
+ switch (fd->field_type()) {
+ case T_BYTE: _out->print_cr("%d", mirror->byte_field(fd->offset())); break;
+ case T_BOOLEAN: _out->print_cr("%d", mirror->bool_field(fd->offset())); break;
+ case T_SHORT: _out->print_cr("%d", mirror->short_field(fd->offset())); break;
+ case T_CHAR: _out->print_cr("%d", mirror->char_field(fd->offset())); break;
+ case T_INT: _out->print_cr("%d", mirror->int_field(fd->offset())); break;
+ case T_LONG: _out->print_cr(INT64_FORMAT, mirror->long_field(fd->offset())); break;
+ case T_FLOAT: {
+ float f = mirror->float_field(fd->offset());
+ _out->print_cr("%d", *(int*)&f);
+ break;
+ }
+ case T_DOUBLE: {
+ double d = mirror->double_field(fd->offset());
+ _out->print_cr(INT64_FORMAT, *(jlong*)&d);
+ break;
+ }
+ case T_ARRAY: {
+ oop value = mirror->obj_field_acquire(fd->offset());
+ if (value == NULL) {
+ _out->print_cr("null");
+ } else {
+ typeArrayOop ta = (typeArrayOop)value;
+ _out->print("%d", ta->length());
+ if (value->is_objArray()) {
+ objArrayOop oa = (objArrayOop)value;
+ const char* klass_name = value->klass()->name()->as_quoted_ascii();
+ _out->print(" %s", klass_name);
+ }
+ _out->cr();
+ }
+ break;
+ }
+ case T_OBJECT: {
+ oop value = mirror->obj_field_acquire(fd->offset());
+ if (value == NULL) {
+ _out->print_cr("null");
+ } else if (value->is_instance()) {
+ if (value->is_a(SystemDictionary::String_klass())) {
+ _out->print("\"");
+ _out->print_raw(java_lang_String::as_quoted_ascii(value));
+ _out->print_cr("\"");
+ } else {
+ const char* klass_name = value->klass()->name()->as_quoted_ascii();
+ _out->print_cr(klass_name);
+ }
+ } else {
+ ShouldNotReachHere();
+ }
+ break;
+ }
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ }
+};
+
+
+void ciInstanceKlass::dump_replay_data(outputStream* out) {
+ ASSERT_IN_VM;
+ InstanceKlass* ik = get_instanceKlass();
+ ConstantPool* cp = ik->constants();
+
+ // Try to record related loaded classes
+ Klass* sub = ik->subklass();
+ while (sub != NULL) {
+ if (sub->oop_is_instance()) {
+ out->print_cr("instanceKlass %s", sub->name()->as_quoted_ascii());
+ }
+ sub = sub->next_sibling();
+ }
+
+ // Dump out the state of the constant pool tags. During replay the
+ // tags will be validated for things which shouldn't change and
+ // classes will be resolved if the tags indicate that they were
+ // resolved at compile time.
+ out->print("ciInstanceKlass %s %d %d %d", ik->name()->as_quoted_ascii(),
+ is_linked(), is_initialized(), cp->length());
+ for (int index = 1; index < cp->length(); index++) {
+ out->print(" %d", cp->tags()->at(index));
+ }
+ out->cr();
+ if (is_initialized()) {
+ // Dump out the static final fields in case the compilation relies
+ // on their value for correct replay.
+ StaticFinalFieldPrinter sffp(out, ik->name()->as_quoted_ascii());
+ ik->do_local_static_fields(&sffp);
+ }
+}
diff --git a/src/share/vm/ci/ciInstanceKlass.hpp b/src/share/vm/ci/ciInstanceKlass.hpp
index 205ee493d..f7aeba2df 100644
--- a/src/share/vm/ci/ciInstanceKlass.hpp
+++ b/src/share/vm/ci/ciInstanceKlass.hpp
@@ -230,6 +230,9 @@ public:
// What kind of ciObject is this?
bool is_instance_klass() const { return true; }
bool is_java_klass() const { return true; }
+
+ // Dump the current state of this klass for compilation replay.
+ virtual void dump_replay_data(outputStream* out);
};
#endif // SHARE_VM_CI_CIINSTANCEKLASS_HPP
diff --git a/src/share/vm/ci/ciMetadata.hpp b/src/share/vm/ci/ciMetadata.hpp
index cbd0bc028..e0baea966 100644
--- a/src/share/vm/ci/ciMetadata.hpp
+++ b/src/share/vm/ci/ciMetadata.hpp
@@ -61,6 +61,7 @@ class ciMetadata: public ciBaseObject {
virtual bool is_array_klass() const { return false; }
virtual bool is_obj_array_klass() const { return false; }
virtual bool is_type_array_klass() const { return false; }
+ virtual void dump_replay_data(outputStream* st) { /* do nothing */ }
ciMethod* as_method() {
assert(is_method(), "bad cast");
diff --git a/src/share/vm/ci/ciMethod.cpp b/src/share/vm/ci/ciMethod.cpp
index 7071f54d0..951183d60 100644
--- a/src/share/vm/ci/ciMethod.cpp
+++ b/src/share/vm/ci/ciMethod.cpp
@@ -31,6 +31,7 @@
#include "ci/ciMethodData.hpp"
#include "ci/ciStreams.hpp"
#include "ci/ciSymbol.hpp"
+#include "ci/ciReplay.hpp"
#include "ci/ciUtilities.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/abstractCompiler.hpp"
@@ -139,6 +140,12 @@ ciMethod::ciMethod(methodHandle h_m) : ciMetadata(h_m()) {
}
if (_interpreter_invocation_count == 0)
_interpreter_invocation_count = 1;
+ _instructions_size = -1;
+#ifdef ASSERT
+ if (ReplayCompiles) {
+ ciReplay::initialize(this);
+ }
+#endif
}
@@ -161,7 +168,8 @@ ciMethod::ciMethod(ciInstanceKlass* holder,
#if defined(COMPILER2) || defined(SHARK)
,
_flow( NULL),
- _bcea( NULL)
+ _bcea( NULL),
+ _instructions_size(-1)
#endif // COMPILER2 || SHARK
{
// Usually holder and accessor are the same type but in some cases
@@ -1000,8 +1008,7 @@ bool ciMethod::can_be_osr_compiled(int entry_bci) {
// ------------------------------------------------------------------
// ciMethod::has_compiled_code
bool ciMethod::has_compiled_code() {
- VM_ENTRY_MARK;
- return get_Method()->code() != NULL;
+ return instructions_size() > 0;
}
int ciMethod::comp_level() {
@@ -1039,14 +1046,18 @@ int ciMethod::code_size_for_inlining() {
// junk like exception handler, stubs, and constant table, which are
// not highly relevant to an inlined method. So we use the more
// specific accessor nmethod::insts_size.
-int ciMethod::instructions_size(int comp_level) {
- GUARDED_VM_ENTRY(
- nmethod* code = get_Method()->code();
- if (code != NULL && (comp_level == CompLevel_any || comp_level == code->comp_level())) {
- return code->insts_end() - code->verified_entry_point();
- }
- return 0;
- )
+int ciMethod::instructions_size() {
+ if (_instructions_size == -1) {
+ GUARDED_VM_ENTRY(
+ nmethod* code = get_Method()->code();
+ if (code != NULL && (code->comp_level() == CompLevel_full_optimization)) {
+ _instructions_size = code->insts_end() - code->verified_entry_point();
+ } else {
+ _instructions_size = 0;
+ }
+ );
+ }
+ return _instructions_size;
}
// ------------------------------------------------------------------
@@ -1166,6 +1177,20 @@ ciMethodBlocks *ciMethod::get_method_blocks() {
#undef FETCH_FLAG_FROM_VM
+void ciMethod::dump_replay_data(outputStream* st) {
+ ASSERT_IN_VM;
+ Method* method = get_Method();
+ Klass* holder = method->method_holder();
+ st->print_cr("ciMethod %s %s %s %d %d %d %d %d",
+ holder->name()->as_quoted_ascii(),
+ method->name()->as_quoted_ascii(),
+ method->signature()->as_quoted_ascii(),
+ method->invocation_counter()->raw_counter(),
+ method->backedge_counter()->raw_counter(),
+ interpreter_invocation_count(),
+ interpreter_throwout_count(),
+ _instructions_size);
+}
// ------------------------------------------------------------------
// ciMethod::print_codes
diff --git a/src/share/vm/ci/ciMethod.hpp b/src/share/vm/ci/ciMethod.hpp
index 15f652133..ecbaacc45 100644
--- a/src/share/vm/ci/ciMethod.hpp
+++ b/src/share/vm/ci/ciMethod.hpp
@@ -51,6 +51,7 @@ class ciMethod : public ciMetadata {
friend class ciExceptionHandlerStream;
friend class ciBytecodeStream;
friend class ciMethodHandle;
+ friend class ciReplay;
private:
// General method information.
@@ -69,6 +70,7 @@ class ciMethod : public ciMetadata {
int _handler_count;
int _interpreter_invocation_count;
int _interpreter_throwout_count;
+ int _instructions_size;
bool _uses_monitors;
bool _balanced_monitors;
@@ -252,7 +254,6 @@ class ciMethod : public ciMetadata {
bool can_be_osr_compiled(int entry_bci);
void set_not_compilable();
bool has_compiled_code();
- int instructions_size(int comp_level = CompLevel_any);
void log_nmethod_identity(xmlStream* log);
bool is_not_reached(int bci);
bool was_executed_more_than(int times);
@@ -260,6 +261,7 @@ class ciMethod : public ciMetadata {
bool is_klass_loaded(int refinfo_index, bool must_be_resolved) const;
bool check_call(int refinfo_index, bool is_static) const;
bool ensure_method_data(); // make sure it exists in the VM also
+ int instructions_size();
int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC
// JSR 292 support
@@ -291,6 +293,7 @@ class ciMethod : public ciMetadata {
bool is_accessor () const;
bool is_initializer () const;
bool can_be_statically_bound() const { return _can_be_statically_bound; }
+ void dump_replay_data(outputStream* st);
// Print the bytecodes of this method.
void print_codes_on(outputStream* st);
diff --git a/src/share/vm/ci/ciMethodData.cpp b/src/share/vm/ci/ciMethodData.cpp
index aef1c03c7..ee5490be8 100644
--- a/src/share/vm/ci/ciMethodData.cpp
+++ b/src/share/vm/ci/ciMethodData.cpp
@@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "ci/ciMetadata.hpp"
#include "ci/ciMethodData.hpp"
+#include "ci/ciReplay.hpp"
#include "ci/ciUtilities.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
@@ -115,6 +116,11 @@ void ciMethodData::load_data() {
_arg_local = mdo->arg_local();
_arg_stack = mdo->arg_stack();
_arg_returned = mdo->arg_returned();
+#ifndef PRODUCT
+ if (ReplayCompiles) {
+ ciReplay::initialize(this);
+ }
+#endif
}
void ciReceiverTypeData::translate_receiver_data_from(ProfileData* data) {
@@ -366,6 +372,79 @@ void ciMethodData::print_impl(outputStream* st) {
ciMetadata::print_impl(st);
}
+void ciMethodData::dump_replay_data(outputStream* out) {
+ ASSERT_IN_VM;
+ MethodData* mdo = get_MethodData();
+ Method* method = mdo->method();
+ Klass* holder = method->method_holder();
+ out->print("ciMethodData %s %s %s %d %d",
+ holder->name()->as_quoted_ascii(),
+ method->name()->as_quoted_ascii(),
+ method->signature()->as_quoted_ascii(),
+ _state,
+ current_mileage());
+
+ // dump the contents of the MDO header as raw data
+ unsigned char* orig = (unsigned char*)&_orig;
+ int length = sizeof(_orig);
+ out->print(" orig %d", length);
+ for (int i = 0; i < length; i++) {
+ out->print(" %d", orig[i]);
+ }
+
+ // dump the MDO data as raw data
+ int elements = data_size() / sizeof(intptr_t);
+ out->print(" data %d", elements);
+ for (int i = 0; i < elements; i++) {
+ // We could use INTPTR_FORMAT here but that's a zero justified
+ // which makes comparing it with the SA version of this output
+ // harder.
+#ifdef _LP64
+ out->print(" 0x%" FORMAT64_MODIFIER "x", data()[i]);
+#else
+ out->print(" 0x%x", data()[i]);
+#endif
+ }
+
+ // The MDO contained oop references as ciObjects, so scan for those
+ // and emit pairs of offset and klass name so that they can be
+ // reconstructed at runtime. The first round counts the number of
+ // oop references and the second actually emits them.
+ int count = 0;
+ for (int round = 0; round < 2; round++) {
+ if (round == 1) out->print(" oops %d", count);
+ ProfileData* pdata = first_data();
+ for ( ; is_valid(pdata); pdata = next_data(pdata)) {
+ if (pdata->is_ReceiverTypeData()) {
+ ciReceiverTypeData* vdata = (ciReceiverTypeData*)pdata;
+ for (uint i = 0; i < vdata->row_limit(); i++) {
+ ciKlass* k = vdata->receiver(i);
+ if (k != NULL) {
+ if (round == 0) {
+ count++;
+ } else {
+ out->print(" %d %s", dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t), k->name()->as_quoted_ascii());
+ }
+ }
+ }
+ } else if (pdata->is_VirtualCallData()) {
+ ciVirtualCallData* vdata = (ciVirtualCallData*)pdata;
+ for (uint i = 0; i < vdata->row_limit(); i++) {
+ ciKlass* k = vdata->receiver(i);
+ if (k != NULL) {
+ if (round == 0) {
+ count++;
+ } else {
+ out->print(" %d %s", dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t), k->name()->as_quoted_ascii());
+ }
+ }
+ }
+ }
+ }
+ }
+ out->cr();
+}
+
#ifndef PRODUCT
void ciMethodData::print() {
print_data_on(tty);
diff --git a/src/share/vm/ci/ciMethodData.hpp b/src/share/vm/ci/ciMethodData.hpp
index bca5d1cd1..ea650cbd7 100644
--- a/src/share/vm/ci/ciMethodData.hpp
+++ b/src/share/vm/ci/ciMethodData.hpp
@@ -144,6 +144,7 @@ public:
class ciMethodData : public ciMetadata {
CI_PACKAGE_ACCESS
+ friend class ciReplay;
private:
// Size in bytes
@@ -320,6 +321,7 @@ public:
void print();
void print_data_on(outputStream* st);
#endif
+ void dump_replay_data(outputStream* out);
};
#endif // SHARE_VM_CI_CIMETHODDATA_HPP
diff --git a/src/share/vm/ci/ciObject.hpp b/src/share/vm/ci/ciObject.hpp
index 6246e173e..c33b79ed5 100644
--- a/src/share/vm/ci/ciObject.hpp
+++ b/src/share/vm/ci/ciObject.hpp
@@ -131,6 +131,7 @@ public:
// Is this a type or value which has no associated class?
// It is true of primitive types and null objects.
virtual bool is_classless() const { return false; }
+ virtual void dump_replay_data(outputStream* st) { /* do nothing */ }
// Note: some ciObjects refer to oops which have yet to be created.
// We refer to these as "unloaded". Specifically, there are
diff --git a/src/share/vm/ci/ciObjectFactory.hpp b/src/share/vm/ci/ciObjectFactory.hpp
index 10870b904..29de514b2 100644
--- a/src/share/vm/ci/ciObjectFactory.hpp
+++ b/src/share/vm/ci/ciObjectFactory.hpp
@@ -137,6 +137,7 @@ public:
ciReturnAddress* get_return_address(int bci);
+ GrowableArray<ciMetadata*>* get_ci_metadata() const { return _ci_metadata; }
// RedefineClasses support
void metadata_do(void f(Metadata*));
diff --git a/src/share/vm/ci/ciReplay.cpp b/src/share/vm/ci/ciReplay.cpp
new file mode 100644
index 000000000..726d84b1b
--- /dev/null
+++ b/src/share/vm/ci/ciReplay.cpp
@@ -0,0 +1,942 @@
+/* Copyright (c) 2012, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "ci/ciMethodData.hpp"
+#include "ci/ciReplay.hpp"
+#include "ci/ciUtilities.hpp"
+#include "compiler/compileBroker.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/oopFactory.hpp"
+#include "memory/resourceArea.hpp"
+#include "utilities/copy.hpp"
+
+#ifdef ASSERT
+
+// ciReplay
+
+typedef struct _ciMethodDataRecord {
+ const char* klass;
+ const char* method;
+ const char* signature;
+ int state;
+ int current_mileage;
+ intptr_t* data;
+ int data_length;
+ char* orig_data;
+ int orig_data_length;
+ int oops_length;
+ jobject* oops_handles;
+ int* oops_offsets;
+} ciMethodDataRecord;
+
+typedef struct _ciMethodRecord {
+ const char* klass;
+ const char* method;
+ const char* signature;
+ int instructions_size;
+ int interpreter_invocation_count;
+ int interpreter_throwout_count;
+ int invocation_counter;
+ int backedge_counter;
+} ciMethodRecord;
+
+class CompileReplay;
+static CompileReplay* replay_state;
+
+class CompileReplay : public StackObj {
+ private:
+ FILE* stream;
+ Thread* thread;
+ Handle protection_domain;
+ Handle loader;
+
+ GrowableArray<ciMethodRecord*> ci_method_records;
+ GrowableArray<ciMethodDataRecord*> ci_method_data_records;
+
+ const char* _error_message;
+
+ char* bufptr;
+ char* buffer;
+ int buffer_length;
+ int buffer_end;
+ int line_no;
+
+ public:
+ CompileReplay(const char* filename, TRAPS) {
+ thread = THREAD;
+ loader = Handle(thread, SystemDictionary::java_system_loader());
+ stream = fopen(filename, "rt");
+ if (stream == NULL) {
+ fprintf(stderr, "Can't open replay file %s\n", filename);
+ }
+ buffer_length = 32;
+ buffer = NEW_RESOURCE_ARRAY(char, buffer_length);
+ _error_message = NULL;
+
+ test();
+ }
+
+ ~CompileReplay() {
+ if (stream != NULL) fclose(stream);
+ }
+
+ void test() {
+ strcpy(buffer, "1 2 foo 4 bar 0x9 \"this is it\"");
+ bufptr = buffer;
+ assert(parse_int("test") == 1, "what");
+ assert(parse_int("test") == 2, "what");
+ assert(strcmp(parse_string(), "foo") == 0, "what");
+ assert(parse_int("test") == 4, "what");
+ assert(strcmp(parse_string(), "bar") == 0, "what");
+ assert(parse_intptr_t("test") == 9, "what");
+ assert(strcmp(parse_quoted_string(), "this is it") == 0, "what");
+ }
+
+ bool had_error() {
+ return _error_message != NULL || thread->has_pending_exception();
+ }
+
+ bool can_replay() {
+ return !(stream == NULL || had_error());
+ }
+
+ void report_error(const char* msg) {
+ _error_message = msg;
+ // Restore the buffer contents for error reporting
+ for (int i = 0; i < buffer_end; i++) {
+ if (buffer[i] == '\0') buffer[i] = ' ';
+ }
+ }
+
+ int parse_int(const char* label) {
+ if (had_error()) {
+ return 0;
+ }
+
+ int v = 0;
+ int read;
+ if (sscanf(bufptr, "%i%n", &v, &read) != 1) {
+ report_error(label);
+ } else {
+ bufptr += read;
+ }
+ return v;
+ }
+
+ intptr_t parse_intptr_t(const char* label) {
+ if (had_error()) {
+ return 0;
+ }
+
+ intptr_t v = 0;
+ int read;
+ if (sscanf(bufptr, INTPTR_FORMAT "%n", &v, &read) != 1) {
+ report_error(label);
+ } else {
+ bufptr += read;
+ }
+ return v;
+ }
+
+ void skip_ws() {
+ // Skip any leading whitespace
+ while (*bufptr == ' ' || *bufptr == '\t') {
+ bufptr++;
+ }
+ }
+
+
+ char* scan_and_terminate(char delim) {
+ char* str = bufptr;
+ while (*bufptr != delim && *bufptr != '\0') {
+ bufptr++;
+ }
+ if (*bufptr != '\0') {
+ *bufptr++ = '\0';
+ }
+ if (bufptr == str) {
+ // nothing here
+ return NULL;
+ }
+ return str;
+ }
+
+ char* parse_string() {
+ if (had_error()) return NULL;
+
+ skip_ws();
+ return scan_and_terminate(' ');
+ }
+
+ char* parse_quoted_string() {
+ if (had_error()) return NULL;
+
+ skip_ws();
+
+ if (*bufptr == '"') {
+ bufptr++;
+ return scan_and_terminate('"');
+ } else {
+ return scan_and_terminate(' ');
+ }
+ }
+
+ const char* parse_escaped_string() {
+ char* result = parse_quoted_string();
+ if (result != NULL) {
+ unescape_string(result);
+ }
+ return result;
+ }
+
+ // Look for the tag 'tag' followed by an
+ bool parse_tag_and_count(const char* tag, int& length) {
+ const char* t = parse_string();
+ if (t == NULL) {
+ return false;
+ }
+
+ if (strcmp(tag, t) != 0) {
+ report_error(tag);
+ return false;
+ }
+ length = parse_int("parse_tag_and_count");
+ return !had_error();
+ }
+
+ // Parse a sequence of raw data encoded as bytes and return the
+ // resulting data.
+ char* parse_data(const char* tag, int& length) {
+ if (!parse_tag_and_count(tag, length)) {
+ return NULL;
+ }
+
+ char * result = NEW_RESOURCE_ARRAY(char, length);
+ for (int i = 0; i < length; i++) {
+ int val = parse_int("data");
+ result[i] = val;
+ }
+ return result;
+ }
+
+ // Parse a standard chunk of data emitted as:
+ // 'tag' <length> # # ...
+ // Where each # is an intptr_t item
+ intptr_t* parse_intptr_data(const char* tag, int& length) {
+ if (!parse_tag_and_count(tag, length)) {
+ return NULL;
+ }
+
+ intptr_t* result = NEW_RESOURCE_ARRAY(intptr_t, length);
+ for (int i = 0; i < length; i++) {
+ skip_ws();
+ intptr_t val = parse_intptr_t("data");
+ result[i] = val;
+ }
+ return result;
+ }
+
+ // Parse a possibly quoted version of a symbol into a symbolOop
+ Symbol* parse_symbol(TRAPS) {
+ const char* str = parse_escaped_string();
+ if (str != NULL) {
+ Symbol* sym = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL);
+ return sym;
+ }
+ return NULL;
+ }
+
+ // Parse a valid klass name and look it up
+ Klass* parse_klass(TRAPS) {
+ const char* str = parse_escaped_string();
+ Symbol* klass_name = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL);
+ if (klass_name != NULL) {
+ Klass* k = SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ oop throwable = PENDING_EXCEPTION;
+ java_lang_Throwable::print(throwable, tty);
+ tty->cr();
+ report_error(str);
+ return NULL;
+ }
+ return k;
+ }
+ return NULL;
+ }
+
+ // Lookup a klass
+ Klass* resolve_klass(const char* klass, TRAPS) {
+ Symbol* klass_name = SymbolTable::lookup(klass, (int)strlen(klass), CHECK_NULL);
+ return SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, CHECK_NULL);
+ }
+
+ // Parse the standard tuple of <klass> <name> <signature>
+ Method* parse_method(TRAPS) {
+ InstanceKlass* k = (InstanceKlass*)parse_klass(CHECK_NULL);
+ Symbol* method_name = parse_symbol(CHECK_NULL);
+ Symbol* method_signature = parse_symbol(CHECK_NULL);
+ Method* m = k->find_method(method_name, method_signature);
+ if (m == NULL) {
+ report_error("can't find method");
+ }
+ return m;
+ }
+
+ // Process each line of the replay file executing each command until
+ // the file ends.
+ void process(TRAPS) {
+ line_no = 1;
+ int pos = 0;
+ int c = getc(stream);
+ while(c != EOF) {
+ if (pos + 1 >= buffer_length) {
+ int newl = buffer_length * 2;
+ char* newb = NEW_RESOURCE_ARRAY(char, newl);
+ memcpy(newb, buffer, pos);
+ buffer = newb;
+ buffer_length = newl;
+ }
+ if (c == '\n') {
+ // null terminate it, reset the pointer and process the line
+ buffer[pos] = '\0';
+ buffer_end = pos++;
+ bufptr = buffer;
+ process_command(CHECK);
+ if (had_error()) {
+ tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message);
+ tty->print_cr("%s", buffer);
+ assert(false, "error");
+ return;
+ }
+ pos = 0;
+ buffer_end = 0;
+ line_no++;
+ } else if (c == '\r') {
+ // skip LF
+ } else {
+ buffer[pos++] = c;
+ }
+ c = getc(stream);
+ }
+ }
+
+ void process_command(TRAPS) {
+ char* cmd = parse_string();
+ if (cmd == NULL) {
+ return;
+ }
+ if (strcmp("#", cmd) == 0) {
+ // ignore
+ } else if (strcmp("compile", cmd) == 0) {
+ process_compile(CHECK);
+ } else if (strcmp("ciMethod", cmd) == 0) {
+ process_ciMethod(CHECK);
+ } else if (strcmp("ciMethodData", cmd) == 0) {
+ process_ciMethodData(CHECK);
+ } else if (strcmp("staticfield", cmd) == 0) {
+ process_staticfield(CHECK);
+ } else if (strcmp("ciInstanceKlass", cmd) == 0) {
+ process_ciInstanceKlass(CHECK);
+ } else if (strcmp("instanceKlass", cmd) == 0) {
+ process_instanceKlass(CHECK);
+#if INCLUDE_JVMTI
+ } else if (strcmp("JvmtiExport", cmd) == 0) {
+ process_JvmtiExport(CHECK);
+#endif // INCLUDE_JVMTI
+ } else {
+ report_error("unknown command");
+ }
+ }
+
+ // compile <klass> <name> <signature> <entry_bci>
+ void process_compile(TRAPS) {
+ // methodHandle method;
+ Method* method = parse_method(CHECK);
+ int entry_bci = parse_int("entry_bci");
+ Klass* k = method->method_holder();
+ ((InstanceKlass*)k)->initialize(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ oop throwable = PENDING_EXCEPTION;
+ java_lang_Throwable::print(throwable, tty);
+ tty->cr();
+ if (ReplayIgnoreInitErrors) {
+ CLEAR_PENDING_EXCEPTION;
+ ((InstanceKlass*)k)->set_init_state(InstanceKlass::fully_initialized);
+ } else {
+ return;
+ }
+ }
+ // Make sure the existence of a prior compile doesn't stop this one
+ nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, CompLevel_full_optimization, true) : method->code();
+ if (nm != NULL) {
+ nm->make_not_entrant();
+ }
+ replay_state = this;
+ CompileBroker::compile_method(method, entry_bci, CompLevel_full_optimization,
+ methodHandle(), 0, "replay", THREAD);
+ replay_state = NULL;
+ reset();
+ }
+
+ // ciMethod <klass> <name> <signature> <invocation_counter> <backedge_counter> <interpreter_invocation_count> <interpreter_throwout_count> <instructions_size>
+ //
+ //
+ void process_ciMethod(TRAPS) {
+ Method* method = parse_method(CHECK);
+ ciMethodRecord* rec = new_ciMethod(method);
+ rec->invocation_counter = parse_int("invocation_counter");
+ rec->backedge_counter = parse_int("backedge_counter");
+ rec->interpreter_invocation_count = parse_int("interpreter_invocation_count");
+ rec->interpreter_throwout_count = parse_int("interpreter_throwout_count");
+ rec->instructions_size = parse_int("instructions_size");
+ }
+
+ // ciMethodData <klass> <name> <signature> <state> <current mileage> orig <length> # # ... data <length> # # ... oops <length>
+ void process_ciMethodData(TRAPS) {
+ Method* method = parse_method(CHECK);
+ /* jsut copied from Method, to build interpret data*/
+ if (InstanceRefKlass::owns_pending_list_lock((JavaThread*)THREAD)) {
+ return;
+ }
+ // methodOopDesc::build_interpreter_method_data(method, CHECK);
+ {
+ // Grab a lock here to prevent multiple
+ // MethodData*s from being created.
+ MutexLocker ml(MethodData_lock, THREAD);
+ if (method->method_data() == NULL) {
+ ClassLoaderData* loader_data = method->method_holder()->class_loader_data();
+ MethodData* method_data = MethodData::allocate(loader_data, method, CHECK);
+ method->set_method_data(method_data);
+ }
+ }
+
+ // collect and record all the needed information for later
+ ciMethodDataRecord* rec = new_ciMethodData(method);
+ rec->state = parse_int("state");
+ rec->current_mileage = parse_int("current_mileage");
+
+ rec->orig_data = parse_data("orig", rec->orig_data_length);
+ if (rec->orig_data == NULL) {
+ return;
+ }
+ rec->data = parse_intptr_data("data", rec->data_length);
+ if (rec->data == NULL) {
+ return;
+ }
+ if (!parse_tag_and_count("oops", rec->oops_length)) {
+ return;
+ }
+ rec->oops_handles = NEW_RESOURCE_ARRAY(jobject, rec->oops_length);
+ rec->oops_offsets = NEW_RESOURCE_ARRAY(int, rec->oops_length);
+ for (int i = 0; i < rec->oops_length; i++) {
+ int offset = parse_int("offset");
+ if (had_error()) {
+ return;
+ }
+ Klass* k = parse_klass(CHECK);
+ rec->oops_offsets[i] = offset;
+ rec->oops_handles[i] = (jobject)(new KlassHandle(THREAD, k));
+ }
+ }
+
+ // instanceKlass <name>
+ //
+ // Loads and initializes the klass 'name'. This can be used to
+ // create particular class loading environments
+ void process_instanceKlass(TRAPS) {
+ // just load the referenced class
+ Klass* k = parse_klass(CHECK);
+ }
+
+ // ciInstanceKlass <name> <is_linked> <is_initialized> <length> tag # # # ...
+ //
+ // Load the klass 'name' and link or initialize it. Verify that the
+ // constant pool is the same length as 'length' and make sure the
+ // constant pool tags are in the same state.
+ void process_ciInstanceKlass(TRAPS) {
+ InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK);
+ int is_linked = parse_int("is_linked");
+ int is_initialized = parse_int("is_initialized");
+ int length = parse_int("length");
+ if (is_initialized) {
+ k->initialize(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ oop throwable = PENDING_EXCEPTION;
+ java_lang_Throwable::print(throwable, tty);
+ tty->cr();
+ if (ReplayIgnoreInitErrors) {
+ CLEAR_PENDING_EXCEPTION;
+ k->set_init_state(InstanceKlass::fully_initialized);
+ } else {
+ return;
+ }
+ }
+ } else if (is_linked) {
+ k->link_class(CHECK);
+ }
+ ConstantPool* cp = k->constants();
+ if (length != cp->length()) {
+ report_error("constant pool length mismatch: wrong class files?");
+ return;
+ }
+
+ int parsed_two_word = 0;
+ for (int i = 1; i < length; i++) {
+ int tag = parse_int("tag");
+ if (had_error()) {
+ return;
+ }
+ switch (cp->tag_at(i).value()) {
+ case JVM_CONSTANT_UnresolvedClass: {
+ if (tag == JVM_CONSTANT_Class) {
+ tty->print_cr("Resolving klass %s at %d", cp->unresolved_klass_at(i)->as_utf8(), i);
+ Klass* k = cp->klass_at(i, CHECK);
+ }
+ break;
+ }
+ case JVM_CONSTANT_Long:
+ case JVM_CONSTANT_Double:
+ parsed_two_word = i + 1;
+
+ case JVM_CONSTANT_ClassIndex:
+ case JVM_CONSTANT_StringIndex:
+ case JVM_CONSTANT_String:
+ case JVM_CONSTANT_UnresolvedClassInError:
+ case JVM_CONSTANT_Fieldref:
+ case JVM_CONSTANT_Methodref:
+ case JVM_CONSTANT_InterfaceMethodref:
+ case JVM_CONSTANT_NameAndType:
+ case JVM_CONSTANT_Utf8:
+ case JVM_CONSTANT_Integer:
+ case JVM_CONSTANT_Float:
+ if (tag != cp->tag_at(i).value()) {
+ report_error("tag mismatch: wrong class files?");
+ return;
+ }
+ break;
+
+ case JVM_CONSTANT_Class:
+ if (tag == JVM_CONSTANT_Class) {
+ } else if (tag == JVM_CONSTANT_UnresolvedClass) {
+ tty->print_cr("Warning: entry was unresolved in the replay data");
+ } else {
+ report_error("Unexpected tag");
+ return;
+ }
+ break;
+
+ case 0:
+ if (parsed_two_word == i) continue;
+
+ default:
+ ShouldNotReachHere();
+ break;
+ }
+
+ }
+ }
+
+ // Initialize a class and fill in the value for a static field.
+ // This is useful when the compile was dependent on the value of
+ // static fields but it's impossible to properly rerun the static
+ // initiailizer.
+ void process_staticfield(TRAPS) {
+ InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK);
+
+ if (ReplaySuppressInitializers == 0 ||
+ ReplaySuppressInitializers == 2 && k->class_loader() == NULL) {
+ return;
+ }
+
+ assert(k->is_initialized(), "must be");
+
+ const char* field_name = parse_escaped_string();;
+ const char* field_signature = parse_string();
+ fieldDescriptor fd;
+ Symbol* name = SymbolTable::lookup(field_name, (int)strlen(field_name), CHECK);
+ Symbol* sig = SymbolTable::lookup(field_signature, (int)strlen(field_signature), CHECK);
+ if (!k->find_local_field(name, sig, &fd) ||
+ !fd.is_static() ||
+ fd.has_initial_value()) {
+ report_error(field_name);
+ return;
+ }
+
+ oop java_mirror = k->java_mirror();
+ if (field_signature[0] == '[') {
+ int length = parse_int("array length");
+ oop value = NULL;
+
+ if (field_signature[1] == '[') {
+ // multi dimensional array
+ ArrayKlass* kelem = (ArrayKlass *)parse_klass(CHECK);
+ int rank = 0;
+ while (field_signature[rank] == '[') {
+ rank++;
+ }
+ int* dims = NEW_RESOURCE_ARRAY(int, rank);
+ dims[0] = length;
+ for (int i = 1; i < rank; i++) {
+ dims[i] = 1; // These aren't relevant to the compiler
+ }
+ value = kelem->multi_allocate(rank, dims, CHECK);
+ } else {
+ if (strcmp(field_signature, "[B") == 0) {
+ value = oopFactory::new_byteArray(length, CHECK);
+ } else if (strcmp(field_signature, "[Z") == 0) {
+ value = oopFactory::new_boolArray(length, CHECK);
+ } else if (strcmp(field_signature, "[C") == 0) {
+ value = oopFactory::new_charArray(length, CHECK);
+ } else if (strcmp(field_signature, "[S") == 0) {
+ value = oopFactory::new_shortArray(length, CHECK);
+ } else if (strcmp(field_signature, "[F") == 0) {
+ value = oopFactory::new_singleArray(length, CHECK);
+ } else if (strcmp(field_signature, "[D") == 0) {
+ value = oopFactory::new_doubleArray(length, CHECK);
+ } else if (strcmp(field_signature, "[I") == 0) {
+ value = oopFactory::new_intArray(length, CHECK);
+ } else if (strcmp(field_signature, "[J") == 0) {
+ value = oopFactory::new_longArray(length, CHECK);
+ } else if (field_signature[0] == '[' && field_signature[1] == 'L') {
+ KlassHandle kelem = resolve_klass(field_signature + 1, CHECK);
+ value = oopFactory::new_objArray(kelem(), length, CHECK);
+ } else {
+ report_error("unhandled array staticfield");
+ }
+ }
+ java_mirror->obj_field_put(fd.offset(), value);
+ } else {
+ const char* string_value = parse_escaped_string();
+ if (strcmp(field_signature, "I") == 0) {
+ int value = atoi(string_value);
+ java_mirror->int_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "B") == 0) {
+ int value = atoi(string_value);
+ java_mirror->byte_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "C") == 0) {
+ int value = atoi(string_value);
+ java_mirror->char_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "S") == 0) {
+ int value = atoi(string_value);
+ java_mirror->short_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "Z") == 0) {
+ int value = atol(string_value);
+ java_mirror->bool_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "J") == 0) {
+ jlong value;
+ if (sscanf(string_value, INT64_FORMAT, &value) != 1) {
+ fprintf(stderr, "Error parsing long: %s\n", string_value);
+ return;
+ }
+ java_mirror->long_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "F") == 0) {
+ float value = atof(string_value);
+ java_mirror->float_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "D") == 0) {
+ double value = atof(string_value);
+ java_mirror->double_field_put(fd.offset(), value);
+ } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) {
+ Handle value = java_lang_String::create_from_str(string_value, CHECK);
+ java_mirror->obj_field_put(fd.offset(), value());
+ } else if (field_signature[0] == 'L') {
+ Symbol* klass_name = SymbolTable::lookup(field_signature, (int)strlen(field_signature), CHECK);
+ KlassHandle kelem = resolve_klass(field_signature, CHECK);
+ oop value = ((InstanceKlass*)kelem())->allocate_instance(CHECK);
+ java_mirror->obj_field_put(fd.offset(), value);
+ } else {
+ report_error("unhandled staticfield");
+ }
+ }
+ }
+
+#if INCLUDE_JVMTI
+ void process_JvmtiExport(TRAPS) {
+ const char* field = parse_string();
+ bool value = parse_int("JvmtiExport flag") != 0;
+ if (strcmp(field, "can_access_local_variables") == 0) {
+ JvmtiExport::set_can_access_local_variables(value);
+ } else if (strcmp(field, "can_hotswap_or_post_breakpoint") == 0) {
+ JvmtiExport::set_can_hotswap_or_post_breakpoint(value);
+ } else if (strcmp(field, "can_post_on_exceptions") == 0) {
+ JvmtiExport::set_can_post_on_exceptions(value);
+ } else {
+ report_error("Unrecognized JvmtiExport directive");
+ }
+ }
+#endif // INCLUDE_JVMTI
+
+ // Create and initialize a record for a ciMethod
+ ciMethodRecord* new_ciMethod(Method* method) {
+ ciMethodRecord* rec = NEW_RESOURCE_OBJ(ciMethodRecord);
+ rec->klass = method->method_holder()->name()->as_utf8();
+ rec->method = method->name()->as_utf8();
+ rec->signature = method->signature()->as_utf8();
+ ci_method_records.append(rec);
+ return rec;
+ }
+
+ // Lookup data for a ciMethod
+ ciMethodRecord* find_ciMethodRecord(Method* method) {
+ const char* klass_name = method->method_holder()->name()->as_utf8();
+ const char* method_name = method->name()->as_utf8();
+ const char* signature = method->signature()->as_utf8();
+ for (int i = 0; i < ci_method_records.length(); i++) {
+ ciMethodRecord* rec = ci_method_records.at(i);
+ if (strcmp(rec->klass, klass_name) == 0 &&
+ strcmp(rec->method, method_name) == 0 &&
+ strcmp(rec->signature, signature) == 0) {
+ return rec;
+ }
+ }
+ return NULL;
+ }
+
+ // Create and initialize a record for a ciMethodData
+ ciMethodDataRecord* new_ciMethodData(Method* method) {
+ ciMethodDataRecord* rec = NEW_RESOURCE_OBJ(ciMethodDataRecord);
+ rec->klass = method->method_holder()->name()->as_utf8();
+ rec->method = method->name()->as_utf8();
+ rec->signature = method->signature()->as_utf8();
+ ci_method_data_records.append(rec);
+ return rec;
+ }
+
+ // Lookup data for a ciMethodData
+ ciMethodDataRecord* find_ciMethodDataRecord(Method* method) {
+ const char* klass_name = method->method_holder()->name()->as_utf8();
+ const char* method_name = method->name()->as_utf8();
+ const char* signature = method->signature()->as_utf8();
+ for (int i = 0; i < ci_method_data_records.length(); i++) {
+ ciMethodDataRecord* rec = ci_method_data_records.at(i);
+ if (strcmp(rec->klass, klass_name) == 0 &&
+ strcmp(rec->method, method_name) == 0 &&
+ strcmp(rec->signature, signature) == 0) {
+ return rec;
+ }
+ }
+ return NULL;
+ }
+
+ const char* error_message() {
+ return _error_message;
+ }
+
+ void reset() {
+ _error_message = NULL;
+ ci_method_records.clear();
+ ci_method_data_records.clear();
+ }
+
+ // Take an ascii string contain \u#### escapes and convert it to utf8
+ // in place.
+ static void unescape_string(char* value) {
+ char* from = value;
+ char* to = value;
+ while (*from != '\0') {
+ if (*from != '\\') {
+ *from++ = *to++;
+ } else {
+ switch (from[1]) {
+ case 'u': {
+ from += 2;
+ jchar value=0;
+ for (int i=0; i<4; i++) {
+ char c = *from++;
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ value = (value << 4) + c - '0';
+ break;
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ value = (value << 4) + 10 + c - 'a';
+ break;
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ value = (value << 4) + 10 + c - 'A';
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ UNICODE::convert_to_utf8(&value, 1, to);
+ to++;
+ break;
+ }
+ case 't': *to++ = '\t'; from += 2; break;
+ case 'n': *to++ = '\n'; from += 2; break;
+ case 'r': *to++ = '\r'; from += 2; break;
+ case 'f': *to++ = '\f'; from += 2; break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ }
+ *from = *to;
+ }
+};
+
+void ciReplay::replay(TRAPS) {
+ int exit_code = replay_impl(THREAD);
+
+ Threads::destroy_vm();
+
+ vm_exit(exit_code);
+}
+
+int ciReplay::replay_impl(TRAPS) {
+ HandleMark hm;
+ ResourceMark rm;
+ // Make sure we don't run with background compilation
+ BackgroundCompilation = false;
+
+ if (ReplaySuppressInitializers > 2) {
+ // ReplaySuppressInitializers > 2 means that we want to allow
+ // normal VM bootstrap but once we get into the replay itself
+ // don't allow any intializers to be run.
+ ReplaySuppressInitializers = 1;
+ }
+
+ // Load and parse the replay data
+ CompileReplay rp(ReplayDataFile, THREAD);
+ int exit_code = 0;
+ if (rp.can_replay()) {
+ rp.process(THREAD);
+ } else {
+ exit_code = 1;
+ return exit_code;
+ }
+
+ if (HAS_PENDING_EXCEPTION) {
+ oop throwable = PENDING_EXCEPTION;
+ CLEAR_PENDING_EXCEPTION;
+ java_lang_Throwable::print(throwable, tty);
+ tty->cr();
+ java_lang_Throwable::print_stack_trace(throwable, tty);
+ tty->cr();
+ exit_code = 2;
+ }
+
+ if (rp.had_error()) {
+ tty->print_cr("Failed on %s", rp.error_message());
+ exit_code = 1;
+ }
+ return exit_code;
+}
+
+
+void ciReplay::initialize(ciMethodData* m) {
+ if (replay_state == NULL) {
+ return;
+ }
+
+ ASSERT_IN_VM;
+ ResourceMark rm;
+
+ Method* method = m->get_MethodData()->method();
+ ciMethodDataRecord* rec = replay_state->find_ciMethodDataRecord(method);
+ if (rec == NULL) {
+ // This indicates some mismatch with the original environment and
+ // the replay environment though it's not always enough to
+ // interfere with reproducing a bug
+ tty->print_cr("Warning: requesting ciMethodData record for method with no data: ");
+ method->print_name(tty);
+ tty->cr();
+ } else {
+ m->_state = rec->state;
+ m->_current_mileage = rec->current_mileage;
+ if (rec->data_length != 0) {
+ assert(m->_data_size == rec->data_length * (int)sizeof(rec->data[0]), "must agree");
+
+ // Write the correct ciObjects back into the profile data
+ ciEnv* env = ciEnv::current();
+ for (int i = 0; i < rec->oops_length; i++) {
+ KlassHandle *h = (KlassHandle *)rec->oops_handles[i];
+ *(ciMetadata**)(rec->data + rec->oops_offsets[i]) =
+ env->get_metadata((*h)());
+ }
+ // Copy the updated profile data into place as intptr_ts
+#ifdef _LP64
+ Copy::conjoint_jlongs_atomic((jlong *)rec->data, (jlong *)m->_data, rec->data_length);
+#else
+ Copy::conjoint_jints_atomic((jint *)rec->data, (jint *)m->_data, rec->data_length);
+#endif
+ }
+
+ // copy in the original header
+ Copy::conjoint_jbytes(rec->orig_data, (char*)&m->_orig, rec->orig_data_length);
+ }
+}
+
+
+bool ciReplay::should_not_inline(ciMethod* method) {
+ if (replay_state == NULL) {
+ return false;
+ }
+
+ VM_ENTRY_MARK;
+ // ciMethod without a record shouldn't be inlined.
+ return replay_state->find_ciMethodRecord(method->get_Method()) == NULL;
+}
+
+
+void ciReplay::initialize(ciMethod* m) {
+ if (replay_state == NULL) {
+ return;
+ }
+
+ ASSERT_IN_VM;
+ ResourceMark rm;
+
+ Method* method = m->get_Method();
+ ciMethodRecord* rec = replay_state->find_ciMethodRecord(method);
+ if (rec == NULL) {
+ // This indicates some mismatch with the original environment and
+ // the replay environment though it's not always enough to
+ // interfere with reproducing a bug
+ tty->print_cr("Warning: requesting ciMethod record for method with no data: ");
+ method->print_name(tty);
+ tty->cr();
+ } else {
+ // m->_instructions_size = rec->instructions_size;
+ m->_instructions_size = -1;
+ m->_interpreter_invocation_count = rec->interpreter_invocation_count;
+ m->_interpreter_throwout_count = rec->interpreter_throwout_count;
+ method->invocation_counter()->_counter = rec->invocation_counter;
+ method->backedge_counter()->_counter = rec->backedge_counter;
+ }
+}
+
+bool ciReplay::is_loaded(Method* method) {
+ if (replay_state == NULL) {
+ return true;
+ }
+
+ ASSERT_IN_VM;
+ ResourceMark rm;
+
+ ciMethodRecord* rec = replay_state->find_ciMethodRecord(method);
+ return rec != NULL;
+}
+#endif
diff --git a/src/share/vm/ci/ciReplay.hpp b/src/share/vm/ci/ciReplay.hpp
new file mode 100644
index 000000000..5966d3ece
--- /dev/null
+++ b/src/share/vm/ci/ciReplay.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ */
+
+#ifndef SHARE_VM_CI_CIREPLAY_HPP
+#define SHARE_VM_CI_CIREPLAY_HPP
+
+#include "ci/ciMethod.hpp"
+
+// ciReplay
+
+class ciReplay {
+ CI_PACKAGE_ACCESS
+
+#ifdef ASSERT
+ private:
+ static int replay_impl(TRAPS);
+
+ public:
+ static void replay(TRAPS);
+
+ // These are used by the CI to fill in the cached data from the
+ // replay file when replaying compiles.
+ static void initialize(ciMethodData* method);
+ static void initialize(ciMethod* method);
+
+ static bool is_loaded(Method* method);
+ static bool is_loaded(Klass* klass);
+
+ static bool should_not_inline(ciMethod* method);
+
+#endif
+};
+
+#endif // SHARE_VM_CI_CIREPLAY_HPP
diff --git a/src/share/vm/ci/ciSymbol.cpp b/src/share/vm/ci/ciSymbol.cpp
index 26b3e4d56..1a89adf5d 100644
--- a/src/share/vm/ci/ciSymbol.cpp
+++ b/src/share/vm/ci/ciSymbol.cpp
@@ -63,6 +63,11 @@ const char* ciSymbol::as_utf8() {
return s->as_utf8();
}
+// The text of the symbol as a null-terminated C string.
+const char* ciSymbol::as_quoted_ascii() {
+ GUARDED_VM_QUICK_ENTRY(return get_symbol()->as_quoted_ascii();)
+}
+
// ------------------------------------------------------------------
// ciSymbol::base
const jbyte* ciSymbol::base() {
diff --git a/src/share/vm/ci/ciSymbol.hpp b/src/share/vm/ci/ciSymbol.hpp
index 642be4625..d54b54009 100644
--- a/src/share/vm/ci/ciSymbol.hpp
+++ b/src/share/vm/ci/ciSymbol.hpp
@@ -73,6 +73,9 @@ public:
const char* as_utf8();
int utf8_length();
+ // The text of the symbol as ascii with all non-printable characters quoted as \u####
+ const char* as_quoted_ascii();
+
// Return the i-th utf8 byte, where i < utf8_length
int byte_at(int i);
diff --git a/src/share/vm/ci/ciUtilities.hpp b/src/share/vm/ci/ciUtilities.hpp
index 9788f77a7..1a075bf6e 100644
--- a/src/share/vm/ci/ciUtilities.hpp
+++ b/src/share/vm/ci/ciUtilities.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, 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
@@ -80,6 +80,9 @@
#define GUARDED_VM_ENTRY(action) \
{if (IS_IN_VM) { action } else { VM_ENTRY_MARK; { action }}}
+#define GUARDED_VM_QUICK_ENTRY(action) \
+ {if (IS_IN_VM) { action } else { VM_QUICK_ENTRY_MARK; { action }}}
+
// Redefine this later.
#define KILL_COMPILE_ON_FATAL_(result) \
THREAD); \
diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp
index c414079e9..5662fabad 100644
--- a/src/share/vm/classfile/javaClasses.cpp
+++ b/src/share/vm/classfile/javaClasses.cpp
@@ -348,6 +348,22 @@ unsigned int java_lang_String::to_hash(oop java_string) {
return java_lang_String::to_hash(value->char_at_addr(offset), length);
}
+char* java_lang_String::as_quoted_ascii(oop java_string) {
+ typeArrayOop value = java_lang_String::value(java_string);
+ int offset = java_lang_String::offset(java_string);
+ int length = java_lang_String::length(java_string);
+
+ jchar* base = (length == 0) ? NULL : value->char_at_addr(offset);
+ if (base == NULL) return NULL;
+
+ int result_length = UNICODE::quoted_ascii_length(base, length) + 1;
+ char* result = NEW_RESOURCE_ARRAY(char, result_length);
+ UNICODE::as_quoted_ascii(base, length, result, result_length);
+ assert(result_length >= length + 1, "must not be shorter");
+ assert(result_length == (int)strlen(result) + 1, "must match");
+ return result;
+}
+
unsigned int java_lang_String::hash_string(oop java_string) {
int length = java_lang_String::length(java_string);
// Zero length string doesn't hash necessarily hash to zero.
diff --git a/src/share/vm/classfile/javaClasses.hpp b/src/share/vm/classfile/javaClasses.hpp
index 902099f19..04e0214d2 100644
--- a/src/share/vm/classfile/javaClasses.hpp
+++ b/src/share/vm/classfile/javaClasses.hpp
@@ -154,6 +154,8 @@ class java_lang_String : AllStatic {
static char* as_utf8_string(oop java_string, int start, int len);
static char* as_platform_dependent_str(Handle java_string, TRAPS);
static jchar* as_unicode_string(oop java_string, int& length);
+ // produce an ascii string with all other values quoted using \u####
+ static char* as_quoted_ascii(oop java_string);
// Compute the hash value for a java.lang.String object which would
// contain the characters passed in.
diff --git a/src/share/vm/code/dependencies.cpp b/src/share/vm/code/dependencies.cpp
index 93db50afe..d81fb3f3f 100644
--- a/src/share/vm/code/dependencies.cpp
+++ b/src/share/vm/code/dependencies.cpp
@@ -569,6 +569,7 @@ void Dependencies::print_dependency(DepType dept, int nargs, DepArgument args[],
void Dependencies::DepStream::log_dependency(Klass* witness) {
if (_deps == NULL && xtty == NULL) return; // fast cutout for runtime
+ ResourceMark rm;
int nargs = argument_count();
DepArgument args[max_arg_count];
for (int j = 0; j < nargs; j++) {
diff --git a/src/share/vm/interpreter/invocationCounter.hpp b/src/share/vm/interpreter/invocationCounter.hpp
index 38725907e..db896d8ad 100644
--- a/src/share/vm/interpreter/invocationCounter.hpp
+++ b/src/share/vm/interpreter/invocationCounter.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2012, 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
@@ -40,6 +40,7 @@
class InvocationCounter VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
+ friend class ciReplay;
private: // bit no: |31 3| 2 | 1 0 |
unsigned int _counter; // format: [count|carry|state]
@@ -85,6 +86,8 @@ class InvocationCounter VALUE_OBJ_CLASS_SPEC {
void set_carry(); // set the sticky carry bit
void set_carry_flag() { _counter |= carry_mask; }
+ int raw_counter() { return _counter; }
+
// Accessors
State state() const { return (State)(_counter & state_mask); }
bool carry() const { return (_counter & carry_mask) != 0; }
diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp
index c5b20d8ff..2f1ef7006 100644
--- a/src/share/vm/oops/instanceKlass.cpp
+++ b/src/share/vm/oops/instanceKlass.cpp
@@ -1052,6 +1052,13 @@ Method* InstanceKlass::class_initializer() {
}
void InstanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) {
+ if (ReplayCompiles &&
+ (ReplaySuppressInitializers == 1 ||
+ ReplaySuppressInitializers >= 2 && this_oop->class_loader() != NULL)) {
+ // Hide the existence of the initializer for the purpose of replaying the compile
+ return;
+ }
+
methodHandle h_method(THREAD, this_oop->class_initializer());
assert(!this_oop->is_initialized(), "we cannot initialize twice");
if (TraceClassInitialization) {
diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp
index f427f338c..ad59ab063 100644
--- a/src/share/vm/oops/instanceKlass.hpp
+++ b/src/share/vm/oops/instanceKlass.hpp
@@ -133,6 +133,7 @@ class OopMapBlock VALUE_OBJ_CLASS_SPEC {
class InstanceKlass: public Klass {
friend class VMStructs;
friend class ClassFileParser;
+ friend class CompileReplay;
protected:
// Constructor
diff --git a/src/share/vm/oops/symbol.cpp b/src/share/vm/oops/symbol.cpp
index 6a8191cbe..f2253dbcc 100644
--- a/src/share/vm/oops/symbol.cpp
+++ b/src/share/vm/oops/symbol.cpp
@@ -153,17 +153,15 @@ char* Symbol::as_C_string_flexible_buffer(Thread* t,
void Symbol::print_symbol_on(outputStream* st) const {
st = st ? st : tty;
- int length = UTF8::unicode_length((const char*)bytes(), utf8_length());
- const char *ptr = (const char *)bytes();
- jchar value;
- for (int index = 0; index < length; index++) {
- ptr = UTF8::next(ptr, &value);
- if (value >= 32 && value < 127 || value == '\'' || value == '\\') {
- st->put(value);
- } else {
- st->print("\\u%04x", value);
- }
- }
+ st->print("%s", as_quoted_ascii());
+}
+
+char* Symbol::as_quoted_ascii() const {
+ const char *ptr = (const char *)&_body[0];
+ int quoted_length = UTF8::quoted_ascii_length(ptr, utf8_length());
+ char* result = NEW_RESOURCE_ARRAY(char, quoted_length + 1);
+ UTF8::as_quoted_ascii(ptr, result, quoted_length + 1);
+ return result;
}
jchar* Symbol::as_unicode(int& length) const {
diff --git a/src/share/vm/oops/symbol.hpp b/src/share/vm/oops/symbol.hpp
index 90ec5c176..55867d2b0 100644
--- a/src/share/vm/oops/symbol.hpp
+++ b/src/share/vm/oops/symbol.hpp
@@ -189,6 +189,8 @@ class Symbol : public MetaspaceObj {
// Use buf if needed buffer length is <= size.
char* as_C_string_flexible_buffer(Thread* t, char* buf, int size) const;
+ // Returns an escaped form of a Java string.
+ char* as_quoted_ascii() const;
// Returns a null terminated utf8 string in a resource array
char* as_utf8() const { return as_C_string(); }
diff --git a/src/share/vm/opto/bytecodeInfo.cpp b/src/share/vm/opto/bytecodeInfo.cpp
index 1c08a6ebf..80117960d 100644
--- a/src/share/vm/opto/bytecodeInfo.cpp
+++ b/src/share/vm/opto/bytecodeInfo.cpp
@@ -23,6 +23,7 @@
*/
#include "precompiled.hpp"
+#include "ci/ciReplay.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "compiler/compileBroker.hpp"
@@ -150,7 +151,7 @@ const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_
} else {
// Not hot. Check for medium-sized pre-existing nmethod at cold sites.
if (callee_method->has_compiled_code() &&
- callee_method->instructions_size(CompLevel_full_optimization) > inline_small_code_size)
+ callee_method->instructions_size() > inline_small_code_size)
return "already compiled into a medium method";
}
if (size > max_inline_size) {
@@ -192,7 +193,7 @@ const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* cal
}
if (callee_method->has_compiled_code() &&
- callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) {
+ callee_method->instructions_size() > InlineSmallCode) {
wci_result->set_profit(wci_result->profit() * 0.1);
// %%% adjust wci_result->size()?
}
@@ -216,7 +217,7 @@ const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* cal
// Now perform checks which are heuristic
if (callee_method->has_compiled_code() &&
- callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) {
+ callee_method->instructions_size() > InlineSmallCode) {
return "already compiled into a big method";
}
@@ -235,6 +236,12 @@ const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* cal
return "disallowed by CompilerOracle";
}
+#ifndef PRODUCT
+ if (ciReplay::should_not_inline(callee_method)) {
+ return "disallowed by ciReplay";
+ }
+#endif
+
if (UseStringCache) {
// Do not inline StringCache::profile() method used only at the beginning.
if (callee_method->name() == ciSymbol::profile_name() &&
diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp
index aeb62798b..1678053c6 100644
--- a/src/share/vm/prims/jni.cpp
+++ b/src/share/vm/prims/jni.cpp
@@ -24,6 +24,7 @@
*/
#include "precompiled.hpp"
+#include "ci/ciReplay.hpp"
#include "classfile/altHashing.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/javaClasses.hpp"
@@ -5151,6 +5152,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v
// Check if we should compile all classes on bootclasspath
NOT_PRODUCT(if (CompileTheWorld) ClassLoader::compile_the_world();)
+ NOT_PRODUCT(if (ReplayCompiles) ciReplay::replay(thread);)
// Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native);
} else {
diff --git a/src/share/vm/prims/jvmtiExport.hpp b/src/share/vm/prims/jvmtiExport.hpp
index 7dcab6831..1100d5269 100644
--- a/src/share/vm/prims/jvmtiExport.hpp
+++ b/src/share/vm/prims/jvmtiExport.hpp
@@ -64,6 +64,8 @@ class AttachOperation;
//
class JvmtiExport : public AllStatic {
friend class VMStructs;
+ friend class CompileReplay;
+
private:
#if INCLUDE_JVMTI
diff --git a/src/share/vm/runtime/compilationPolicy.cpp b/src/share/vm/runtime/compilationPolicy.cpp
index 73b8f8393..713163a10 100644
--- a/src/share/vm/runtime/compilationPolicy.cpp
+++ b/src/share/vm/runtime/compilationPolicy.cpp
@@ -97,6 +97,9 @@ void CompilationPolicy::completed_vm_startup() {
// This is intended to force compiles for methods (usually for
// debugging) that would otherwise be interpreted for some reason.
bool CompilationPolicy::must_be_compiled(methodHandle m, int comp_level) {
+ // Don't allow Xcomp to cause compiles in replay mode
+ if (ReplayCompiles) return false;
+
if (m->has_compiled_code()) return false; // already compiled
if (!can_be_compiled(m, comp_level)) return false;
@@ -322,6 +325,16 @@ nmethod* NonTieredCompPolicy::event(methodHandle method, methodHandle inlinee, i
return NULL;
}
}
+ if (CompileTheWorld || ReplayCompiles) {
+ // Don't trigger other compiles in testing mode
+ if (bci == InvocationEntryBci) {
+ reset_counter_for_invocation_event(method);
+ } else {
+ reset_counter_for_back_branch_event(method);
+ }
+ return NULL;
+ }
+
if (bci == InvocationEntryBci) {
// when code cache is full, compilation gets switched off, UseCompiler
// is set to false
diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp
index 62ad97f89..785965ca3 100644
--- a/src/share/vm/runtime/globals.hpp
+++ b/src/share/vm/runtime/globals.hpp
@@ -3189,6 +3189,26 @@ class CommandLineFlags {
product(ccstrlist, CompileCommand, "", \
"Prepend to .hotspot_compiler; e.g. log,java/lang/String.<init>") \
\
+ develop(bool, ReplayCompiles, false, \
+ "Enable replay of compilations from ReplayDataFile") \
+ \
+ develop(ccstr, ReplayDataFile, "replay.txt", \
+ "file containing compilation replay information") \
+ \
+ develop(intx, ReplaySuppressInitializers, 2, \
+ "Controls handling of class initialization during replay" \
+ "0 - don't do anything special" \
+ "1 - treat all class initializers as empty" \
+ "2 - treat class initializers for application classes as empty" \
+ "3 - allow all class initializers to run during bootstrap but" \
+ " pretend they are empty after starting replay") \
+ \
+ develop(bool, ReplayIgnoreInitErrors, false, \
+ "Ignore exceptions thrown during initialization for replay") \
+ \
+ develop(bool, DumpReplayDataOnError, true, \
+ "record replay data for crashing compiler threads") \
+ \
product(bool, CICompilerCountPerCPU, false, \
"1 compiler thread for log(N CPUs)") \
\
diff --git a/src/share/vm/runtime/vmStructs.cpp b/src/share/vm/runtime/vmStructs.cpp
index eb6d3cd78..484566125 100644
--- a/src/share/vm/runtime/vmStructs.cpp
+++ b/src/share/vm/runtime/vmStructs.cpp
@@ -993,6 +993,7 @@ typedef BinaryTreeDictionary<Metablock, FreeList> MetablockTreeDictionary;
\
nonstatic_field(ciMethod, _interpreter_invocation_count, int) \
nonstatic_field(ciMethod, _interpreter_throwout_count, int) \
+ nonstatic_field(ciMethod, _instructions_size, int) \
\
nonstatic_field(ciMethodData, _data_size, int) \
nonstatic_field(ciMethodData, _state, u_char) \
diff --git a/src/share/vm/utilities/array.hpp b/src/share/vm/utilities/array.hpp
index 9690529c5..5578ed9b6 100644
--- a/src/share/vm/utilities/array.hpp
+++ b/src/share/vm/utilities/array.hpp
@@ -353,9 +353,9 @@ protected:
// sort the array.
bool contains(const T& x) const { return index_of(x) >= 0; }
- T at(int i) const { return _data[i]; }
- void at_put(const int i, const T& x) { _data[i] = x; }
- T* adr_at(const int i) { return &_data[i]; }
+ T at(int i) const { assert(i >= 0 && i< _length, err_msg_res("oob: 0 <= %d < %d", i, _length)); return _data[i]; }
+ void at_put(const int i, const T& x) { assert(i >= 0 && i< _length, err_msg_res("oob: 0 <= %d < %d", i, _length)); _data[i] = x; }
+ T* adr_at(const int i) { assert(i >= 0 && i< _length, err_msg_res("oob: 0 <= %d < %d", i, _length)); return &_data[i]; }
int find(const T& x) { return index_of(x); }
T at_acquire(const int which) { return OrderAccess::load_acquire(adr_at(which)); }
diff --git a/src/share/vm/utilities/utf8.cpp b/src/share/vm/utilities/utf8.cpp
index be7d18815..da470b18c 100644
--- a/src/share/vm/utilities/utf8.cpp
+++ b/src/share/vm/utilities/utf8.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2012, 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
@@ -147,7 +147,7 @@ static u_char* utf8_write(u_char* base, jchar ch) {
void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unicode_length) {
unsigned char ch;
- const char *ptr = (const char *)utf8_str;
+ const char *ptr = utf8_str;
int index = 0;
/* ASCII case loop optimization */
@@ -162,6 +162,119 @@ void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unic
}
}
+// returns the quoted ascii length of a 0-terminated utf8 string
+int UTF8::quoted_ascii_length(const char* utf8_str, int utf8_length) {
+ const char *ptr = utf8_str;
+ const char* end = ptr + utf8_length;
+ int result = 0;
+ while (ptr < end) {
+ jchar c;
+ ptr = UTF8::next(ptr, &c);
+ if (c >= 32 && c < 127) {
+ result++;
+ } else {
+ result += 6;
+ }
+ }
+ return result;
+}
+
+// converts a utf8 string to quoted ascii
+void UTF8::as_quoted_ascii(const char* utf8_str, char* buf, int buflen) {
+ const char *ptr = utf8_str;
+ char* p = buf;
+ char* end = buf + buflen;
+ while (*ptr != '\0') {
+ jchar c;
+ ptr = UTF8::next(ptr, &c);
+ if (c >= 32 && c < 127) {
+ if (p + 1 >= end) break; // string is truncated
+ *p++ = (char)c;
+ } else {
+ if (p + 6 >= end) break; // string is truncated
+ sprintf(p, "\\u%04x", c);
+ p += 6;
+ }
+ }
+ *p = '\0';
+}
+
+
+const char* UTF8::from_quoted_ascii(const char* quoted_ascii_str) {
+ const char *ptr = quoted_ascii_str;
+ char* result = NULL;
+ while (*ptr != '\0') {
+ char c = *ptr;
+ if (c < 32 || c >= 127) break;
+ }
+ if (*ptr == '\0') {
+ // nothing to do so return original string
+ return quoted_ascii_str;
+ }
+ // everything up to this point was ok.
+ int length = ptr - quoted_ascii_str;
+ char* buffer = NULL;
+ for (int round = 0; round < 2; round++) {
+ while (*ptr != '\0') {
+ if (*ptr != '\\') {
+ if (buffer != NULL) {
+ buffer[length] = *ptr;
+ }
+ length++;
+ } else {
+ switch (ptr[1]) {
+ case 'u': {
+ ptr += 2;
+ jchar value=0;
+ for (int i=0; i<4; i++) {
+ char c = *ptr++;
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ value = (value << 4) + c - '0';
+ break;
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ value = (value << 4) + 10 + c - 'a';
+ break;
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ value = (value << 4) + 10 + c - 'A';
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ if (buffer == NULL) {
+ char utf8_buffer[4];
+ char* next = (char*)utf8_write((u_char*)utf8_buffer, value);
+ length += next - utf8_buffer;
+ } else {
+ char* next = (char*)utf8_write((u_char*)&buffer[length], value);
+ length += next - &buffer[length];
+ }
+ break;
+ }
+ case 't': if (buffer != NULL) buffer[length] = '\t'; ptr += 2; length++; break;
+ case 'n': if (buffer != NULL) buffer[length] = '\n'; ptr += 2; length++; break;
+ case 'r': if (buffer != NULL) buffer[length] = '\r'; ptr += 2; length++; break;
+ case 'f': if (buffer != NULL) buffer[length] = '\f'; ptr += 2; length++; break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ }
+ if (round == 0) {
+ buffer = NEW_RESOURCE_ARRAY(char, length + 1);
+ ptr = quoted_ascii_str;
+ } else {
+ buffer[length] = '\0';
+ }
+ }
+ return buffer;
+}
+
+
// Returns NULL if 'c' it not found. This only works as long
// as 'c' is an ASCII character
const jbyte* UTF8::strrchr(const jbyte* base, int length, jbyte c) {
@@ -242,3 +355,35 @@ void UNICODE::convert_to_utf8(const jchar* base, int length, char* utf8_buffer)
}
*utf8_buffer = '\0';
}
+
+// returns the quoted ascii length of a unicode string
+int UNICODE::quoted_ascii_length(jchar* base, int length) {
+ int result = 0;
+ for (int i = 0; i < length; i++) {
+ jchar c = base[i];
+ if (c >= 32 && c < 127) {
+ result++;
+ } else {
+ result += 6;
+ }
+ }
+ return result;
+}
+
+// converts a utf8 string to quoted ascii
+void UNICODE::as_quoted_ascii(const jchar* base, int length, char* buf, int buflen) {
+ char* p = buf;
+ char* end = buf + buflen;
+ for (int index = 0; index < length; index++) {
+ jchar c = base[index];
+ if (c >= 32 && c < 127) {
+ if (p + 1 >= end) break; // string is truncated
+ *p++ = (char)c;
+ } else {
+ if (p + 6 >= end) break; // string is truncated
+ sprintf(p, "\\u%04x", c);
+ p += 6;
+ }
+ }
+ *p = '\0';
+}
diff --git a/src/share/vm/utilities/utf8.hpp b/src/share/vm/utilities/utf8.hpp
index c56d550ec..69710fcce 100644
--- a/src/share/vm/utilities/utf8.hpp
+++ b/src/share/vm/utilities/utf8.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2012, 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
@@ -32,22 +32,32 @@
class UTF8 : AllStatic {
public:
- // returns the unicode length of a 0-terminated uft8 string
- static int unicode_length(const char* uft8_str);
+ // returns the unicode length of a 0-terminated utf8 string
+ static int unicode_length(const char* utf8_str);
- // returns the unicode length of a non-0-terminated uft8 string
- static int unicode_length(const char* uft8_str, int len);
+ // returns the unicode length of a non-0-terminated utf8 string
+ static int unicode_length(const char* utf8_str, int len);
- // converts a uft8 string to a unicode string
+ // converts a utf8 string to a unicode string
static void convert_to_unicode(const char* utf8_str, jchar* unicode_buffer, int unicode_length);
+ // returns the quoted ascii length of a utf8 string
+ static int quoted_ascii_length(const char* utf8_str, int utf8_length);
+
+ // converts a utf8 string to quoted ascii
+ static void as_quoted_ascii(const char* utf8_str, char* buf, int buflen);
+
+ // converts a quoted ascii string to utf8 string. returns the original
+ // string unchanged if nothing needs to be done.
+ static const char* from_quoted_ascii(const char* quoted_ascii_string);
+
// decodes the current utf8 character, stores the result in value,
- // and returns the end of the current uft8 chararacter.
+ // and returns the end of the current utf8 chararacter.
static char* next(const char* str, jchar* value);
// decodes the current utf8 character, gets the supplementary character instead of
// the surrogate pair when seeing a supplementary character in string,
- // stores the result in value, and returns the end of the current uft8 chararacter.
+ // stores the result in value, and returns the end of the current utf8 chararacter.
static char* next_character(const char* str, jint* value);
// Utility methods
@@ -79,6 +89,12 @@ class UNICODE : AllStatic {
// in resource area unless a buffer is provided.
static char* as_utf8(jchar* base, int length);
static char* as_utf8(jchar* base, int length, char* buf, int buflen);
+
+ // returns the quoted ascii length of a unicode string
+ static int quoted_ascii_length(jchar* base, int length);
+
+ // converts a utf8 string to quoted ascii
+ static void as_quoted_ascii(const jchar* base, int length, char* buf, int buflen);
};
#endif // SHARE_VM_UTILITIES_UTF8_HPP
diff --git a/src/share/vm/utilities/vmError.cpp b/src/share/vm/utilities/vmError.cpp
index 175fe382f..ddb6e1cf8 100644
--- a/src/share/vm/utilities/vmError.cpp
+++ b/src/share/vm/utilities/vmError.cpp
@@ -1009,6 +1009,15 @@ void VMError::report_and_die() {
OnError = NULL;
}
+ static bool skip_replay = false;
+ if (DumpReplayDataOnError && _thread && _thread->is_Compiler_thread() && !skip_replay) {
+ skip_replay = true;
+ ciEnv* env = ciEnv::current();
+ if (env != NULL) {
+ env->dump_replay_data();
+ }
+ }
+
static bool skip_bug_url = !should_report_bug(first_error->_id);
if (!skip_bug_url) {
skip_bug_url = true;