8140450: Implement JEP 259: Stack-Walking API
Reviewed-by: coleenp, dfuchs, bchristi, psandoz, sspitsyn
Contributed-by: Mandy Chung <mandy.chung@oracle.com>, Brent Christian <brent.christian@oracle.com>, Daniel Fuchs <daniel.fuchs@oracle.com>, Hamlin Li <huaming.li@oracle.com>
--- a/hotspot/make/share/makefiles/mapfile-vers Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/make/share/makefiles/mapfile-vers Mon Nov 23 12:44:43 2015 -0800
@@ -7,6 +7,7 @@
JVM_ActiveProcessorCount;
JVM_ArrayCopy;
JVM_AssertionStatusDirectives;
+ JVM_CallStackWalk;
JVM_ClassDepth;
JVM_ClassLoaderDepth;
JVM_Clone;
@@ -36,6 +37,7 @@
JVM_DumpAllStacks;
JVM_DumpThreads;
JVM_FillInStackTrace;
+ JVM_FillStackFrames;
JVM_FindClassFromCaller;
JVM_FindClassFromClass;
JVM_FindClassFromBootLoader;
@@ -133,6 +135,7 @@
JVM_MonitorNotify;
JVM_MonitorNotifyAll;
JVM_MonitorWait;
+ JVM_MoreStackWalk;
JVM_NanoTime;
JVM_NativePath;
JVM_NewArray;
@@ -150,6 +153,7 @@
JVM_SetClassSigners;
JVM_SetNativeThreadName;
JVM_SetPrimitiveArrayElement;
+ JVM_SetMethodInfo;
JVM_SetThreadPriority;
JVM_Sleep;
JVM_StartThread;
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Mon Nov 23 12:44:43 2015 -0800
@@ -1518,43 +1518,11 @@
// 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) {
assert(version < MAX_VERSION, "version is too big");
return method != NULL && (method->constants()->version() == 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 {
- // 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 line_number;
-}
-
// This class provides a simple wrapper over the internal structure of
// exception backtrace to insulate users of the backtrace from needing
// to know what it looks like.
@@ -1676,7 +1644,7 @@
}
_methods->short_at_put(_index, method->orig_method_idnum());
- _bcis->int_at_put(_index, merge_bci_and_version(bci, method->constants()->version()));
+ _bcis->int_at_put(_index, Backtrace::merge_bci_and_version(bci, method->constants()->version()));
_cprefs->short_at_put(_index, method->name_index());
// We need to save the mirrors in the backtrace to keep the class
@@ -1688,19 +1656,6 @@
};
-Symbol* get_source_file_name(InstanceKlass* holder, int version) {
- // Find the specific ik version that contains this source_file_name_index
- // via the previous versions list, but use the current version's
- // constant pool to look it up. The previous version's index has been
- // merged for the current constant pool.
- InstanceKlass* ik = holder->get_klass_version(version);
- // This version has been cleaned up.
- if (ik == NULL) return NULL;
- int source_file_name_index = ik->source_file_name_index();
- return (source_file_name_index == 0) ?
- (Symbol*)NULL : holder->constants()->symbol_at(source_file_name_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, int cpref) {
@@ -1718,7 +1673,7 @@
buf_len += (int)strlen(method_name);
char* source_file_name = NULL;
- Symbol* source = get_source_file_name(holder, version);
+ Symbol* source = Backtrace::get_source_file_name(holder, version);
if (source != NULL) {
source_file_name = source->as_C_string();
buf_len += (int)strlen(source_file_name);
@@ -1733,7 +1688,7 @@
if (!version_matches(method, version)) {
strcat(buf, "(Redefined)");
} else {
- int line_number = get_line_number(method, bci);
+ int line_number = Backtrace::get_line_number(method, bci);
if (line_number == -2) {
strcat(buf, "(Native Method)");
} else {
@@ -1802,8 +1757,8 @@
// 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));
+ int version = Backtrace::version_at(bcis->int_at(index));
+ int bci = Backtrace::bci_at(bcis->int_at(index));
int cpref = cprefs->short_at(index);
print_stack_element(st, mirror, method, version, bci, cpref);
}
@@ -2090,8 +2045,8 @@
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));
+ int version = Backtrace::version_at(bcis->int_at(chunk_index));
+ int bci = Backtrace::bci_at(bcis->int_at(chunk_index));
int cpref = cprefs->short_at(chunk_index);
Handle mirror(THREAD, mirrors->obj_at(chunk_index));
@@ -2114,6 +2069,7 @@
}
Handle element = ik->allocate_instance_handle(CHECK_0);
+
// Fill in class name
ResourceMark rm(THREAD);
InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror()));
@@ -2136,13 +2092,13 @@
java_lang_StackTraceElement::set_lineNumber(element(), -1);
} else {
// Fill in source file name and line number.
- Symbol* source = get_source_file_name(holder, version);
+ Symbol* source = Backtrace::get_source_file_name(holder, version);
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);
+ int line_number = Backtrace::get_line_number(method, bci);
java_lang_StackTraceElement::set_lineNumber(element(), line_number);
}
return element();
@@ -2155,6 +2111,108 @@
return create(mirror, method_id, method->constants()->version(), bci, cpref, THREAD);
}
+Method* java_lang_StackFrameInfo::get_method(Handle stackFrame, InstanceKlass* holder, TRAPS) {
+ if (MemberNameInStackFrame) {
+ Handle mname(THREAD, stackFrame->obj_field(_memberName_offset));
+ Method* method = (Method*)java_lang_invoke_MemberName::vmtarget(mname());
+ // we should expand MemberName::name when Throwable uses StackTrace
+ // MethodHandles::expand_MemberName(mname, MethodHandles::_suppress_defc|MethodHandles::_suppress_type, CHECK_NULL);
+ return method;
+ } else {
+ short mid = stackFrame->short_field(_mid_offset);
+ short version = stackFrame->short_field(_version_offset);
+ return holder->method_with_orig_idnum(mid, version);
+ }
+}
+
+Symbol* java_lang_StackFrameInfo::get_file_name(Handle stackFrame, InstanceKlass* holder) {
+ if (MemberNameInStackFrame) {
+ return holder->source_file_name();
+ } else {
+ short version = stackFrame->short_field(_version_offset);
+ return Backtrace::get_source_file_name(holder, version);
+ }
+}
+
+void java_lang_StackFrameInfo::set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci) {
+ // set Method* or mid/cpref
+ if (MemberNameInStackFrame) {
+ oop mname = stackFrame->obj_field(_memberName_offset);
+ InstanceKlass* ik = method->method_holder();
+ CallInfo info(method(), ik);
+ MethodHandles::init_method_MemberName(mname, info);
+ } else {
+ int mid = method->orig_method_idnum();
+ int cpref = method->name_index();
+ assert((jushort)mid == mid, "mid should be short");
+ assert((jushort)cpref == cpref, "cpref should be short");
+ java_lang_StackFrameInfo::set_mid(stackFrame(), (short)mid);
+ java_lang_StackFrameInfo::set_cpref(stackFrame(), (short)cpref);
+ }
+ // set bci
+ java_lang_StackFrameInfo::set_bci(stackFrame(), bci);
+ // method may be redefined; store the version
+ int version = method->constants()->version();
+ assert((jushort)version == version, "version should be short");
+ java_lang_StackFrameInfo::set_version(stackFrame(), (short)version);
+}
+
+void java_lang_StackFrameInfo::fill_methodInfo(Handle stackFrame, TRAPS) {
+ ResourceMark rm(THREAD);
+ oop k = stackFrame->obj_field(_declaringClass_offset);
+ InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(k));
+ Method* method = java_lang_StackFrameInfo::get_method(stackFrame, holder, CHECK);
+ int bci = stackFrame->int_field(_bci_offset);
+
+ // The method can be NULL if the requested class version is gone
+ Symbol* sym = (method != NULL) ? method->name() : NULL;
+ if (MemberNameInStackFrame) {
+ assert(sym != NULL, "MemberName must have method name");
+ } else {
+ // The method can be NULL if the requested class version is gone
+ if (sym == NULL) {
+ short cpref = stackFrame->short_field(_cpref_offset);
+ sym = holder->constants()->symbol_at(cpref);
+ }
+ }
+
+ // set method name
+ oop methodname = StringTable::intern(sym, CHECK);
+ java_lang_StackFrameInfo::set_methodName(stackFrame(), methodname);
+
+ // set file name and line number
+ Symbol* source = get_file_name(stackFrame, holder);
+ if (source != NULL) {
+ oop filename = StringTable::intern(source, CHECK);
+ java_lang_StackFrameInfo::set_fileName(stackFrame(), filename);
+ }
+
+ // if the method has been redefined, the bci is no longer applicable
+ short version = stackFrame->short_field(_version_offset);
+ if (version_matches(method, version)) {
+ int line_number = Backtrace::get_line_number(method, bci);
+ java_lang_StackFrameInfo::set_lineNumber(stackFrame(), line_number);
+ }
+}
+
+void java_lang_StackFrameInfo::compute_offsets() {
+ Klass* k = SystemDictionary::StackFrameInfo_klass();
+ compute_offset(_declaringClass_offset, k, vmSymbols::declaringClass_name(), vmSymbols::class_signature());
+ compute_offset(_memberName_offset, k, vmSymbols::memberName_name(), vmSymbols::object_signature());
+ compute_offset(_bci_offset, k, vmSymbols::bci_name(), vmSymbols::int_signature());
+ compute_offset(_methodName_offset, k, vmSymbols::methodName_name(), vmSymbols::string_signature());
+ compute_offset(_fileName_offset, k, vmSymbols::fileName_name(), vmSymbols::string_signature());
+ compute_offset(_lineNumber_offset, k, vmSymbols::lineNumber_name(), vmSymbols::int_signature());
+ STACKFRAMEINFO_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
+}
+
+void java_lang_LiveStackFrameInfo::compute_offsets() {
+ Klass* k = SystemDictionary::LiveStackFrameInfo_klass();
+ compute_offset(_monitors_offset, k, vmSymbols::monitors_name(), vmSymbols::object_array_signature());
+ compute_offset(_locals_offset, k, vmSymbols::locals_name(), vmSymbols::object_array_signature());
+ compute_offset(_operands_offset, k, vmSymbols::operands_name(), vmSymbols::object_array_signature());
+}
+
void java_lang_reflect_AccessibleObject::compute_offsets() {
Klass* k = SystemDictionary::reflect_AccessibleObject_klass();
compute_offset(override_offset, k, vmSymbols::override_name(), vmSymbols::bool_signature());
@@ -3471,6 +3529,18 @@
int java_lang_StackTraceElement::methodName_offset;
int java_lang_StackTraceElement::fileName_offset;
int java_lang_StackTraceElement::lineNumber_offset;
+int java_lang_StackFrameInfo::_declaringClass_offset;
+int java_lang_StackFrameInfo::_memberName_offset;
+int java_lang_StackFrameInfo::_bci_offset;
+int java_lang_StackFrameInfo::_methodName_offset;
+int java_lang_StackFrameInfo::_fileName_offset;
+int java_lang_StackFrameInfo::_lineNumber_offset;
+int java_lang_StackFrameInfo::_mid_offset;
+int java_lang_StackFrameInfo::_version_offset;
+int java_lang_StackFrameInfo::_cpref_offset;
+int java_lang_LiveStackFrameInfo::_monitors_offset;
+int java_lang_LiveStackFrameInfo::_locals_offset;
+int java_lang_LiveStackFrameInfo::_operands_offset;
int java_lang_AssertionStatusDirectives::classes_offset;
int java_lang_AssertionStatusDirectives::classEnabled_offset;
int java_lang_AssertionStatusDirectives::packages_offset;
@@ -3500,6 +3570,50 @@
element->int_field_put(lineNumber_offset, value);
}
+// Support for java_lang_StackFrameInfo
+void java_lang_StackFrameInfo::set_declaringClass(oop element, oop value) {
+ element->obj_field_put(_declaringClass_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_mid(oop element, short value) {
+ element->short_field_put(_mid_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_version(oop element, short value) {
+ element->short_field_put(_version_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_cpref(oop element, short value) {
+ element->short_field_put(_cpref_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_bci(oop element, int value) {
+ element->int_field_put(_bci_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_fileName(oop element, oop value) {
+ element->obj_field_put(_fileName_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_methodName(oop element, oop value) {
+ element->obj_field_put(_methodName_offset, value);
+}
+
+void java_lang_StackFrameInfo::set_lineNumber(oop element, int value) {
+ element->int_field_put(_lineNumber_offset, value);
+}
+
+void java_lang_LiveStackFrameInfo::set_monitors(oop element, oop value) {
+ element->obj_field_put(_monitors_offset, value);
+}
+
+void java_lang_LiveStackFrameInfo::set_locals(oop element, oop value) {
+ element->obj_field_put(_locals_offset, value);
+}
+
+void java_lang_LiveStackFrameInfo::set_operands(oop element, oop value) {
+ element->obj_field_put(_operands_offset, value);
+}
// Support for java Assertions - java_lang_AssertionStatusDirectives.
@@ -3633,6 +3747,8 @@
sun_reflect_ConstantPool::compute_offsets();
sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets();
java_lang_reflect_Parameter::compute_offsets();
+ java_lang_StackFrameInfo::compute_offsets();
+ java_lang_LiveStackFrameInfo::compute_offsets();
// generated interpreter code wants to know about the offsets we just computed:
AbstractAssembler::update_delayed_values();
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -1359,6 +1359,85 @@
};
+class Backtrace: AllStatic {
+ public:
+ // Helper backtrace functions to store bci|version together.
+ static int merge_bci_and_version(int bci, int version);
+ static int merge_mid_and_cpref(int mid, int cpref);
+ static int bci_at(unsigned int merged);
+ static int version_at(unsigned int merged);
+ static int mid_at(unsigned int merged);
+ static int cpref_at(unsigned int merged);
+ static int get_line_number(const methodHandle& method, int bci);
+ static Symbol* get_source_file_name(InstanceKlass* holder, int version);
+
+ // Debugging
+ friend class JavaClasses;
+};
+
+// Interface to java.lang.StackFrameInfo objects
+
+#define STACKFRAMEINFO_INJECTED_FIELDS(macro) \
+ macro(java_lang_StackFrameInfo, mid, short_signature, false) \
+ macro(java_lang_StackFrameInfo, version, short_signature, false) \
+ macro(java_lang_StackFrameInfo, cpref, short_signature, false)
+
+class java_lang_StackFrameInfo: AllStatic {
+private:
+ static int _declaringClass_offset;
+ static int _memberName_offset;
+ static int _bci_offset;
+ static int _methodName_offset;
+ static int _fileName_offset;
+ static int _lineNumber_offset;
+
+ static int _mid_offset;
+ static int _version_offset;
+ static int _cpref_offset;
+
+ static Method* get_method(Handle stackFrame, InstanceKlass* holder, TRAPS);
+ static Symbol* get_file_name(Handle stackFrame, InstanceKlass* holder);
+
+public:
+ // Setters
+ static void set_declaringClass(oop info, oop value);
+ static void set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci);
+ static void set_bci(oop info, int value);
+
+ // set method info in an instance of StackFrameInfo
+ static void fill_methodInfo(Handle info, TRAPS);
+ static void set_methodName(oop info, oop value);
+ static void set_fileName(oop info, oop value);
+ static void set_lineNumber(oop info, int value);
+
+ // these injected fields are only used if -XX:-MemberNameInStackFrame set
+ static void set_mid(oop info, short value);
+ static void set_version(oop info, short value);
+ static void set_cpref(oop info, short value);
+
+ static void compute_offsets();
+
+ // Debugging
+ friend class JavaClasses;
+};
+
+class java_lang_LiveStackFrameInfo: AllStatic {
+ private:
+ static int _monitors_offset;
+ static int _locals_offset;
+ static int _operands_offset;
+
+ public:
+ static void set_monitors(oop info, oop value);
+ static void set_locals(oop info, oop value);
+ static void set_operands(oop info, oop value);
+
+ static void compute_offsets();
+
+ // Debugging
+ friend class JavaClasses;
+};
+
// Interface to java.lang.AssertionStatusDirectives objects
class java_lang_AssertionStatusDirectives: AllStatic {
@@ -1442,7 +1521,9 @@
CLASS_INJECTED_FIELDS(macro) \
CLASSLOADER_INJECTED_FIELDS(macro) \
MEMBERNAME_INJECTED_FIELDS(macro) \
- CALLSITECONTEXT_INJECTED_FIELDS(macro)
+ CALLSITECONTEXT_INJECTED_FIELDS(macro) \
+ STACKFRAMEINFO_INJECTED_FIELDS(macro)
+
// Interface to hard-coded offset checking
--- a/hotspot/src/share/vm/classfile/javaClasses.inline.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/classfile/javaClasses.inline.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -73,4 +73,67 @@
return obj != NULL && is_subclass(obj->klass());
}
+inline int Backtrace::merge_bci_and_version(int bci, int version) {
+ // only store u2 for version, checking for overflow.
+ if (version > USHRT_MAX || version < 0) version = USHRT_MAX;
+ assert((jushort)bci == bci, "bci should be short");
+ return build_int_from_shorts(version, bci);
+}
+
+inline int Backtrace::merge_mid_and_cpref(int mid, int cpref) {
+ // only store u2 for mid and cpref, checking for overflow.
+ assert((jushort)mid == mid, "mid should be short");
+ assert((jushort)cpref == cpref, "cpref should be short");
+ return build_int_from_shorts(cpref, mid);
+}
+
+inline int Backtrace::bci_at(unsigned int merged) {
+ return extract_high_short_from_int(merged);
+}
+
+inline int Backtrace::version_at(unsigned int merged) {
+ return extract_low_short_from_int(merged);
+}
+
+inline int Backtrace::mid_at(unsigned int merged) {
+ return extract_high_short_from_int(merged);
+}
+
+inline int Backtrace::cpref_at(unsigned int merged) {
+ return extract_low_short_from_int(merged);
+}
+
+inline int Backtrace::get_line_number(const methodHandle& 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 {
+ // 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 line_number;
+}
+
+/*
+ * Returns the source file name of a given InstanceKlass and version
+ */
+inline Symbol* Backtrace::get_source_file_name(InstanceKlass* holder, int version) {
+ // Find the specific ik version that contains this source_file_name_index
+ // via the previous versions list, but use the current version's
+ // constant pool to look it up. The previous version's index has been
+ // merged for the current constant pool.
+ InstanceKlass* ik = holder->get_klass_version(version);
+ // This version has been cleaned up.
+ if (ik == NULL) return NULL;
+ int source_file_name_index = ik->source_file_name_index();
+ return (source_file_name_index == 0) ?
+ (Symbol*)NULL : holder->constants()->symbol_at(source_file_name_index);
+}
+
#endif // SHARE_VM_CLASSFILE_JAVACLASSES_INLINE_HPP
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -179,11 +179,17 @@
do_klass(sun_misc_Launcher_klass, sun_misc_Launcher, Pre ) \
do_klass(CodeSource_klass, java_security_CodeSource, Pre ) \
\
- /* It's NULL in non-1.4 JDKs. */ \
do_klass(StackTraceElement_klass, java_lang_StackTraceElement, Opt ) \
+ \
/* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \
do_klass(nio_Buffer_klass, java_nio_Buffer, Opt ) \
\
+ /* Stack Walking */ \
+ do_klass(StackWalker_klass, java_lang_StackWalker, Opt ) \
+ do_klass(AbstractStackWalker_klass, java_lang_StackStreamFactory_AbstractStackWalker, Opt ) \
+ do_klass(StackFrameInfo_klass, java_lang_StackFrameInfo, Opt ) \
+ do_klass(LiveStackFrameInfo_klass, java_lang_LiveStackFrameInfo, Opt ) \
+ \
/* Preload boxing klasses */ \
do_klass(Boolean_klass, java_lang_Boolean, Pre ) \
do_klass(Character_klass, java_lang_Character, Pre ) \
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -310,6 +310,22 @@
/* Support for JVMCI */ \
JVMCI_VM_SYMBOLS_DO(template, do_alias) \
\
+ template(java_lang_StackWalker, "java/lang/StackWalker") \
+ template(java_lang_StackFrameInfo, "java/lang/StackFrameInfo") \
+ template(java_lang_LiveStackFrameInfo, "java/lang/LiveStackFrameInfo") \
+ template(java_lang_StackStreamFactory_AbstractStackWalker, "java/lang/StackStreamFactory$AbstractStackWalker") \
+ template(doStackWalk_name, "doStackWalk") \
+ template(doStackWalk_signature, "(JIIII)Ljava/lang/Object;") \
+ template(asPrimitive_name, "asPrimitive") \
+ template(asPrimitive_int_signature, "(I)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_long_signature, "(J)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_short_signature, "(S)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_byte_signature, "(B)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_char_signature, "(C)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_float_signature, "(F)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_double_signature, "(D)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ template(asPrimitive_boolean_signature, "(Z)Ljava/lang/LiveStackFrame$PrimitiveValue;") \
+ \
/* common method and field names */ \
template(object_initializer_name, "<init>") \
template(class_initializer_name, "<clinit>") \
@@ -410,6 +426,18 @@
template(append_name, "append") \
template(klass_name, "klass") \
template(array_klass_name, "array_klass") \
+ template(declaringClass_name, "declaringClass") \
+ template(memberName_name, "memberName") \
+ template(mid_name, "mid") \
+ template(cpref_name, "cpref") \
+ template(version_name, "version") \
+ template(bci_name, "bci") \
+ template(methodName_name, "methodName") \
+ template(fileName_name, "fileName") \
+ template(lineNumber_name, "lineNumber") \
+ template(monitors_name, "monitors") \
+ template(locals_name, "locals") \
+ template(operands_name, "operands") \
template(oop_size_name, "oop_size") \
template(static_oop_field_count_name, "static_oop_field_count") \
template(protection_domain_name, "protection_domain") \
@@ -514,6 +542,7 @@
template(class_array_signature, "[Ljava/lang/Class;") \
template(classloader_signature, "Ljava/lang/ClassLoader;") \
template(object_signature, "Ljava/lang/Object;") \
+ template(object_array_signature, "[Ljava/lang/Object;") \
template(class_signature, "Ljava/lang/Class;") \
template(string_signature, "Ljava/lang/String;") \
template(reference_signature, "Ljava/lang/ref/Reference;") \
--- a/hotspot/src/share/vm/memory/universe.cpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/memory/universe.cpp Mon Nov 23 12:44:43 2015 -0800
@@ -115,6 +115,7 @@
LatestMethodCache* Universe::_loader_addClass_cache = NULL;
LatestMethodCache* Universe::_pd_implies_cache = NULL;
LatestMethodCache* Universe::_throw_illegal_access_error_cache = NULL;
+LatestMethodCache* Universe::_do_stack_walk_cache = NULL;
oop Universe::_out_of_memory_error_java_heap = NULL;
oop Universe::_out_of_memory_error_metaspace = NULL;
oop Universe::_out_of_memory_error_class_metaspace = NULL;
@@ -240,6 +241,7 @@
_loader_addClass_cache->serialize(f);
_pd_implies_cache->serialize(f);
_throw_illegal_access_error_cache->serialize(f);
+ _do_stack_walk_cache->serialize(f);
}
void Universe::check_alignment(uintx size, uintx alignment, const char* name) {
@@ -674,6 +676,7 @@
Universe::_loader_addClass_cache = new LatestMethodCache();
Universe::_pd_implies_cache = new LatestMethodCache();
Universe::_throw_illegal_access_error_cache = new LatestMethodCache();
+ Universe::_do_stack_walk_cache = new LatestMethodCache();
if (UseSharedSpaces) {
// Read the data structures supporting the shared spaces (shared
@@ -1048,6 +1051,17 @@
SystemDictionary::ProtectionDomain_klass(), m);
}
+ // Setup method for stack walking
+ InstanceKlass::cast(SystemDictionary::AbstractStackWalker_klass())->link_class(CHECK_false);
+ m = InstanceKlass::cast(SystemDictionary::AbstractStackWalker_klass())->
+ find_method(vmSymbols::doStackWalk_name(),
+ vmSymbols::doStackWalk_signature());
+ // Allow NULL which should only happen with bootstrapping.
+ if (m != NULL) {
+ Universe::_do_stack_walk_cache->init(
+ SystemDictionary::AbstractStackWalker_klass(), m);
+ }
+
// This needs to be done before the first scavenge/gc, since
// it's an input to soft ref clearing policy.
{
--- a/hotspot/src/share/vm/memory/universe.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/memory/universe.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -149,6 +149,7 @@
static LatestMethodCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector
static LatestMethodCache* _pd_implies_cache; // method for checking protection domain attributes
static LatestMethodCache* _throw_illegal_access_error_cache; // Unsafe.throwIllegalAccessError() method
+ static LatestMethodCache* _do_stack_walk_cache; // method for stack walker callback
// preallocated error objects (no backtrace)
static oop _out_of_memory_error_java_heap;
@@ -314,6 +315,8 @@
static Method* protection_domain_implies_method() { return _pd_implies_cache->get_method(); }
static Method* throw_illegal_access_error() { return _throw_illegal_access_error_cache->get_method(); }
+ static Method* do_stack_walk_method() { return _do_stack_walk_cache->get_method(); }
+
static oop null_ptr_exception_instance() { return _null_ptr_exception_instance; }
static oop arithmetic_exception_instance() { return _arithmetic_exception_instance; }
static oop virtual_machine_error_instance() { return _virtual_machine_error_instance; }
--- a/hotspot/src/share/vm/prims/jvm.cpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/prims/jvm.cpp Mon Nov 23 12:44:43 2015 -0800
@@ -46,6 +46,7 @@
#include "prims/jvmtiThreadState.hpp"
#include "prims/nativeLookup.hpp"
#include "prims/privilegedStack.hpp"
+#include "prims/stackwalk.hpp"
#include "runtime/arguments.hpp"
#include "runtime/atomic.inline.hpp"
#include "runtime/handles.inline.hpp"
@@ -547,6 +548,94 @@
JVM_END
+// java.lang.StackWalker //////////////////////////////////////////////////////
+
+
+JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
+ jint skip_frames, jint frame_count, jint start_index,
+ jobjectArray classes,
+ jobjectArray frames))
+ JVMWrapper("JVM_CallStackWalk");
+ JavaThread* jt = (JavaThread*) THREAD;
+ if (!jt->is_Java_thread() || !jt->has_last_Java_frame()) {
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: no stack trace", NULL);
+ }
+
+ Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
+ objArrayOop ca = objArrayOop(JNIHandles::resolve_non_null(classes));
+ objArrayHandle classes_array_h(THREAD, ca);
+
+ // frames array is null when only getting caller reference
+ objArrayOop fa = objArrayOop(JNIHandles::resolve(frames));
+ objArrayHandle frames_array_h(THREAD, fa);
+
+ int limit = start_index + frame_count;
+ if (classes_array_h->length() < limit) {
+ THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), "not enough space in buffers", NULL);
+ }
+
+ Handle result = StackWalk::walk(stackStream_h, mode, skip_frames, frame_count,
+ start_index, classes_array_h,
+ frames_array_h, CHECK_NULL);
+ return JNIHandles::make_local(env, result());
+JVM_END
+
+
+JVM_ENTRY(jint, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor,
+ jint frame_count, jint start_index,
+ jobjectArray classes,
+ jobjectArray frames))
+ JVMWrapper("JVM_MoreStackWalk");
+ JavaThread* jt = (JavaThread*) THREAD;
+ objArrayOop ca = objArrayOop(JNIHandles::resolve_non_null(classes));
+ objArrayHandle classes_array_h(THREAD, ca);
+
+ // frames array is null when only getting caller reference
+ objArrayOop fa = objArrayOop(JNIHandles::resolve(frames));
+ objArrayHandle frames_array_h(THREAD, fa);
+
+ int limit = start_index+frame_count;
+ if (classes_array_h->length() < limit) {
+ THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "not enough space in buffers");
+ }
+
+ Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
+ return StackWalk::moreFrames(stackStream_h, mode, anchor, frame_count,
+ start_index, classes_array_h,
+ frames_array_h, THREAD);
+JVM_END
+
+JVM_ENTRY(void, JVM_FillStackFrames(JNIEnv *env, jclass stackStream,
+ jint start_index,
+ jobjectArray frames,
+ jint from_index, jint to_index))
+ JVMWrapper("JVM_FillStackFrames");
+ if (TraceStackWalk) {
+ tty->print("JVM_FillStackFrames() start_index=%d from_index=%d to_index=%d\n",
+ start_index, from_index, to_index);
+ }
+
+ JavaThread* jt = (JavaThread*) THREAD;
+
+ objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
+ objArrayHandle frames_array_h(THREAD, fa);
+
+ if (frames_array_h->length() < to_index) {
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "array length not matched");
+ }
+
+ for (int i = from_index; i < to_index; i++) {
+ Handle stackFrame(THREAD, frames_array_h->obj_at(i));
+ java_lang_StackFrameInfo::fill_methodInfo(stackFrame, CHECK);
+ }
+JVM_END
+
+JVM_ENTRY(void, JVM_SetMethodInfo(JNIEnv *env, jobject frame))
+ JVMWrapper("JVM_SetMethodInfo");
+ Handle stackFrame(THREAD, JNIHandles::resolve(frame));
+ java_lang_StackFrameInfo::fill_methodInfo(stackFrame, THREAD);
+JVM_END
+
// java.lang.Object ///////////////////////////////////////////////
--- a/hotspot/src/share/vm/prims/jvm.h Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/prims/jvm.h Mon Nov 23 12:44:43 2015 -0800
@@ -201,6 +201,37 @@
JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index);
/*
+ * java.lang.StackWalker
+ */
+enum {
+ JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2,
+ JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE = 0x10,
+ JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20,
+ JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100
+};
+
+JNIEXPORT jobject JNICALL
+JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
+ jint skip_frames, jint frame_count, jint start_index,
+ jobjectArray classes,
+ jobjectArray frames);
+
+JNIEXPORT jint JNICALL
+JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor,
+ jint frame_count, jint start_index,
+ jobjectArray classes,
+ jobjectArray frames);
+
+JNIEXPORT void JNICALL
+JVM_FillStackFrames(JNIEnv* env, jclass cls,
+ jint start_index,
+ jobjectArray frames,
+ jint from_index, jint toIndex);
+
+JNIEXPORT void JNICALL
+JVM_SetMethodInfo(JNIEnv* env, jobject frame);
+
+/*
* java.lang.Thread
*/
JNIEXPORT void JNICALL
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/prims/stackwalk.cpp Mon Nov 23 12:44:43 2015 -0800
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2015, 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 "classfile/javaClasses.hpp"
+#include "classfile/javaClasses.inline.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "memory/oopFactory.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/objArrayOop.inline.hpp"
+#include "prims/stackwalk.hpp"
+#include "runtime/globals.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/vframe.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// setup and cleanup actions
+void StackWalkAnchor::setup_magic_on_entry(objArrayHandle classes_array) {
+ classes_array->obj_at_put(magic_pos, _thread->threadObj());
+ _anchor = address_value();
+ assert(check_magic(classes_array), "invalid magic");
+}
+
+bool StackWalkAnchor::check_magic(objArrayHandle classes_array) {
+ oop m1 = classes_array->obj_at(magic_pos);
+ jlong m2 = _anchor;
+ if (m1 == _thread->threadObj() && m2 == address_value()) return true;
+ return false;
+}
+
+bool StackWalkAnchor::cleanup_magic_on_exit(objArrayHandle classes_array) {
+ bool ok = check_magic(classes_array);
+ classes_array->obj_at_put(magic_pos, NULL);
+ _anchor = 0L;
+ return ok;
+}
+
+// Returns StackWalkAnchor for the current stack being traversed.
+//
+// Parameters:
+// thread Current Java thread.
+// magic Magic value used for each stack walking
+// classes_array User-supplied buffers. The 0th element is reserved
+// to this StackWalkAnchor to use
+//
+StackWalkAnchor* StackWalkAnchor::from_current(JavaThread* thread, jlong magic,
+ objArrayHandle classes_array)
+{
+ assert(thread != NULL && thread->is_Java_thread(), "");
+ oop m1 = classes_array->obj_at(magic_pos);
+ if (m1 != thread->threadObj()) return NULL;
+ if (magic == 0L) return NULL;
+ StackWalkAnchor* anchor = (StackWalkAnchor*) (intptr_t) magic;
+ if (!anchor->is_valid_in(thread, classes_array)) return NULL;
+ return anchor;
+}
+
+// Unpacks one or more frames into user-supplied buffers.
+// Updates the end index, and returns the number of unpacked frames.
+// Always start with the existing vfst.method and bci.
+// Do not call vfst.next to advance over the last returned value.
+// In other words, do not leave any stale data in the vfst.
+//
+// Parameters:
+// mode Restrict which frames to be decoded.
+// vfst vFrameStream.
+// max_nframes Maximum number of frames to be filled.
+// start_index Start index to the user-supplied buffers.
+// classes_array Buffer to store classes in, starting at start_index.
+// frames_array Buffer to store StackFrame in, starting at start_index.
+// NULL if not used.
+// end_index End index to the user-supplied buffers with unpacked frames.
+//
+// Returns the number of frames whose information was transferred into the buffers.
+//
+int StackWalk::fill_in_frames(jlong mode, vframeStream& vfst,
+ int max_nframes, int start_index,
+ objArrayHandle classes_array,
+ objArrayHandle frames_array,
+ int& end_index, TRAPS) {
+ if (TraceStackWalk) {
+ tty->print_cr("fill_in_frames limit=%d start=%d frames length=%d",
+ max_nframes, start_index, classes_array->length());
+ }
+ assert(max_nframes > 0, "invalid max_nframes");
+ assert(start_index + max_nframes <= classes_array->length(), "oob");
+
+ int frames_decoded = 0;
+ for (; !vfst.at_end(); vfst.next()) {
+ Method* method = vfst.method();
+ int bci = vfst.bci();
+
+ if (method == NULL) continue;
+ if (!ShowHiddenFrames && StackWalk::skip_hidden_frames(mode)) {
+ if (method->is_hidden()) {
+ if (TraceStackWalk) {
+ tty->print(" hidden method: "); method->print_short_name();
+ tty->print("\n");
+ }
+ continue;
+ }
+ }
+
+ int index = end_index++;
+ if (TraceStackWalk) {
+ tty->print(" %d: frame method: ", index); method->print_short_name();
+ tty->print_cr(" bci=%d", bci);
+ }
+
+ classes_array->obj_at_put(index, method->method_holder()->java_mirror());
+ // fill in StackFrameInfo and initialize MemberName
+ if (live_frame_info(mode)) {
+ Handle stackFrame(frames_array->obj_at(index));
+ fill_live_stackframe(stackFrame, method, bci, vfst.java_frame(), CHECK_0);
+ } else if (need_method_info(mode)) {
+ Handle stackFrame(frames_array->obj_at(index));
+ fill_stackframe(stackFrame, method, bci);
+ }
+ if (++frames_decoded >= max_nframes) break;
+ }
+ return frames_decoded;
+}
+
+static oop create_primitive_value_instance(StackValueCollection* values, int i, TRAPS) {
+ Klass* k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_LiveStackFrameInfo(), CHECK_NULL);
+ instanceKlassHandle ik (THREAD, k);
+
+ JavaValue result(T_OBJECT);
+ JavaCallArguments args;
+ Symbol* signature = NULL;
+
+ // ## TODO: type is only available in LocalVariable table, if present.
+ // ## StackValue type is T_INT or T_OBJECT.
+ switch (values->at(i)->type()) {
+ case T_INT:
+ args.push_int(values->int_at(i));
+ signature = vmSymbols::asPrimitive_int_signature();
+ break;
+
+ case T_LONG:
+ args.push_long(values->long_at(i));
+ signature = vmSymbols::asPrimitive_long_signature();
+ break;
+
+ case T_FLOAT:
+ args.push_float(values->float_at(i));
+ signature = vmSymbols::asPrimitive_float_signature();
+ break;
+
+ case T_DOUBLE:
+ args.push_double(values->double_at(i));
+ signature = vmSymbols::asPrimitive_double_signature();
+ break;
+
+ case T_BYTE:
+ args.push_int(values->int_at(i));
+ signature = vmSymbols::asPrimitive_byte_signature();
+ break;
+
+ case T_SHORT:
+ args.push_int(values->int_at(i));
+ signature = vmSymbols::asPrimitive_short_signature();
+ break;
+
+ case T_CHAR:
+ args.push_int(values->int_at(i));
+ signature = vmSymbols::asPrimitive_char_signature();
+ break;
+
+ case T_BOOLEAN:
+ args.push_int(values->int_at(i));
+ signature = vmSymbols::asPrimitive_boolean_signature();
+ break;
+
+ case T_OBJECT:
+ return values->obj_at(i)();
+
+ case T_CONFLICT:
+ // put a non-null slot
+ args.push_int(0);
+ signature = vmSymbols::asPrimitive_int_signature();
+ break;
+
+ default: ShouldNotReachHere();
+ }
+ JavaCalls::call_static(&result,
+ ik,
+ vmSymbols::asPrimitive_name(),
+ signature,
+ &args,
+ CHECK_NULL);
+ return (instanceOop) result.get_jobject();
+}
+
+static objArrayHandle values_to_object_array(StackValueCollection* values, TRAPS) {
+ objArrayHandle empty;
+ int length = values->size();
+ objArrayOop array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(),
+ length, CHECK_(empty));
+ objArrayHandle array_h(THREAD, array_oop);
+ for (int i = 0; i < values->size(); i++) {
+ StackValue* st = values->at(i);
+ oop obj = create_primitive_value_instance(values, i, CHECK_(empty));
+ if (obj != NULL)
+ array_h->obj_at_put(i, obj);
+ }
+ return array_h;
+}
+
+static objArrayHandle monitors_to_object_array(GrowableArray<MonitorInfo*>* monitors, TRAPS) {
+ int length = monitors->length();
+ objArrayOop array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(),
+ length, CHECK_(objArrayHandle()));
+ objArrayHandle array_h(THREAD, array_oop);
+ for (int i = 0; i < length; i++) {
+ MonitorInfo* monitor = monitors->at(i);
+ array_h->obj_at_put(i, monitor->owner());
+ }
+ return array_h;
+}
+
+// Fill StackFrameInfo with declaringClass and bci and initialize memberName
+void StackWalk::fill_stackframe(Handle stackFrame, const methodHandle& method, int bci) {
+ java_lang_StackFrameInfo::set_declaringClass(stackFrame(), method->method_holder()->java_mirror());
+ java_lang_StackFrameInfo::set_method_and_bci(stackFrame(), method, bci);
+}
+
+// Fill LiveStackFrameInfo with locals, monitors, and expressions
+void StackWalk::fill_live_stackframe(Handle stackFrame, const methodHandle& method,
+ int bci, javaVFrame* jvf, TRAPS) {
+ fill_stackframe(stackFrame, method, bci);
+ if (jvf != NULL) {
+ StackValueCollection* locals = jvf->locals();
+ StackValueCollection* expressions = jvf->expressions();
+ GrowableArray<MonitorInfo*>* monitors = jvf->monitors();
+
+ if (!locals->is_empty()) {
+ objArrayHandle locals_h = values_to_object_array(locals, CHECK);
+ java_lang_LiveStackFrameInfo::set_locals(stackFrame(), locals_h());
+ }
+ if (!expressions->is_empty()) {
+ objArrayHandle expressions_h = values_to_object_array(expressions, CHECK);
+ java_lang_LiveStackFrameInfo::set_operands(stackFrame(), expressions_h());
+ }
+ if (monitors->length() > 0) {
+ objArrayHandle monitors_h = monitors_to_object_array(monitors, CHECK);
+ java_lang_LiveStackFrameInfo::set_monitors(stackFrame(), monitors_h());
+ }
+ }
+}
+
+// Begins stack walking.
+//
+// Parameters:
+// stackStream StackStream object
+// mode Stack walking mode.
+// skip_frames Number of frames to be skipped.
+// frame_count Number of frames to be traversed.
+// start_index Start index to the user-supplied buffers.
+// classes_array Buffer to store classes in, starting at start_index.
+// frames_array Buffer to store StackFrame in, starting at start_index.
+// NULL if not used.
+//
+// Returns Object returned from AbstractStackWalker::doStackWalk call.
+//
+oop StackWalk::walk(Handle stackStream, jlong mode,
+ int skip_frames, int frame_count, int start_index,
+ objArrayHandle classes_array,
+ objArrayHandle frames_array,
+ TRAPS) {
+ JavaThread* jt = (JavaThread*)THREAD;
+ if (TraceStackWalk) {
+ tty->print_cr("Start walking: mode " JLONG_FORMAT " skip %d frames batch size %d",
+ mode, skip_frames, frame_count);
+ }
+
+ if (need_method_info(mode)) {
+ if (frames_array.is_null()) {
+ THROW_MSG_(vmSymbols::java_lang_NullPointerException(), "frames_array is NULL", NULL);
+ }
+ }
+
+ Klass* stackWalker_klass = SystemDictionary::StackWalker_klass();
+ Klass* abstractStackWalker_klass = SystemDictionary::AbstractStackWalker_klass();
+
+ methodHandle m_doStackWalk(THREAD, Universe::do_stack_walk_method());
+
+ // Open up a traversable stream onto my stack.
+ // This stream will be made available by *reference* to the inner Java call.
+ StackWalkAnchor anchor(jt);
+ vframeStream& vfst = anchor.vframe_stream();
+
+ {
+ // Skip all methods from AbstractStackWalker and StackWalk (enclosing method)
+ if (!fill_in_stacktrace(mode)) {
+ while (!vfst.at_end()) {
+ InstanceKlass* ik = vfst.method()->method_holder();
+ if (ik != stackWalker_klass &&
+ ik != abstractStackWalker_klass && ik->super() != abstractStackWalker_klass) {
+ break;
+ }
+
+ if (TraceStackWalk) {
+ tty->print(" skip "); vfst.method()->print_short_name(); tty->print("\n");
+ }
+ vfst.next();
+ }
+ }
+
+ // For exceptions, skip Throwable::fillInStackTrace and <init> methods
+ // of the exception class and superclasses
+ if (fill_in_stacktrace(mode)) {
+ bool skip_to_fillInStackTrace = false;
+ bool skip_throwableInit_check = false;
+ while (!vfst.at_end() && !skip_throwableInit_check) {
+ InstanceKlass* ik = vfst.method()->method_holder();
+ Method* method = vfst.method();
+ if (!skip_to_fillInStackTrace) {
+ if (ik == SystemDictionary::Throwable_klass() &&
+ method->name() == vmSymbols::fillInStackTrace_name()) {
+ // this frame will be skipped
+ skip_to_fillInStackTrace = true;
+ }
+ } else if (!(ik->is_subclass_of(SystemDictionary::Throwable_klass()) &&
+ method->name() == vmSymbols::object_initializer_name())) {
+ // there are none or we've seen them all - either way stop checking
+ skip_throwableInit_check = true;
+ break;
+ }
+
+ if (TraceStackWalk) {
+ tty->print("stack walk: skip "); vfst.method()->print_short_name(); tty->print("\n");
+ }
+ vfst.next();
+ }
+ }
+
+ // stack frame has been traversed individually and resume stack walk
+ // from the stack frame at depth == skip_frames.
+ for (int n=0; n < skip_frames && !vfst.at_end(); vfst.next(), n++) {
+ if (TraceStackWalk) {
+ tty->print(" skip "); vfst.method()->print_short_name();
+ tty->print_cr(" frame id: " PTR_FORMAT " pc: " PTR_FORMAT,
+ p2i(vfst.frame_id()), p2i(vfst.frame_pc()));
+ }
+ }
+ }
+
+ // The Method* pointer in the vfst has a very short shelf life. Grab it now.
+ int end_index = start_index;
+ int numFrames = 0;
+ if (!vfst.at_end()) {
+ numFrames = fill_in_frames(mode, vfst, frame_count, start_index, classes_array,
+ frames_array, end_index, CHECK_NULL);
+ if (numFrames < 1) {
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "stack walk: decode failed", NULL);
+ }
+ }
+
+ // JVM_CallStackWalk walks the stack and fills in stack frames, then calls to
+ // Java method java.lang.StackStreamFactory.AbstractStackWalker::doStackWalk
+ // which calls the implementation to consume the stack frames.
+ // When JVM_CallStackWalk returns, it invalidates the stack stream.
+ JavaValue result(T_OBJECT);
+ JavaCallArguments args(stackStream);
+ args.push_long(anchor.address_value());
+ args.push_int(skip_frames);
+ args.push_int(frame_count);
+ args.push_int(start_index);
+ args.push_int(end_index);
+
+ // Link the thread and vframe stream into the callee-visible object
+ anchor.setup_magic_on_entry(classes_array);
+
+ JavaCalls::call(&result, m_doStackWalk, &args, THREAD);
+
+ // Do this before anything else happens, to disable any lingering stream objects
+ bool ok = anchor.cleanup_magic_on_exit(classes_array);
+
+ // Throw pending exception if we must
+ (void) (CHECK_NULL);
+
+ if (!ok) {
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: corrupted buffers on exit", NULL);
+ }
+
+ // Return normally
+ return (oop)result.get_jobject();
+
+}
+
+// Walk the next batch of stack frames
+//
+// Parameters:
+// stackStream StackStream object
+// mode Stack walking mode.
+// magic Must be valid value to continue the stack walk
+// frame_count Number of frames to be decoded.
+// start_index Start index to the user-supplied buffers.
+// classes_array Buffer to store classes in, starting at start_index.
+// frames_array Buffer to store StackFrame in, starting at start_index.
+// NULL if not used.
+//
+// Returns the end index of frame filled in the buffer.
+//
+jint StackWalk::moreFrames(Handle stackStream, jlong mode, jlong magic,
+ int frame_count, int start_index,
+ objArrayHandle classes_array,
+ objArrayHandle frames_array,
+ TRAPS)
+{
+ JavaThread* jt = (JavaThread*)THREAD;
+ StackWalkAnchor* existing_anchor = StackWalkAnchor::from_current(jt, magic, classes_array);
+ if (existing_anchor == NULL) {
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: corrupted buffers", 0L);
+ }
+
+ if ((need_method_info(mode) || live_frame_info(mode)) && frames_array.is_null()) {
+ THROW_MSG_(vmSymbols::java_lang_NullPointerException(), "frames_array is NULL", 0L);
+ }
+
+ if (TraceStackWalk) {
+ tty->print_cr("StackWalk::moreFrames frame_count %d existing_anchor " PTR_FORMAT " start %d frames %d",
+ frame_count, p2i(existing_anchor), start_index, classes_array->length());
+ }
+ int end_index = start_index;
+ if (frame_count <= 0) {
+ return end_index; // No operation.
+ }
+
+ int count = frame_count + start_index;
+ assert (classes_array->length() >= count, "not enough space in buffers");
+
+ StackWalkAnchor& anchor = (*existing_anchor);
+ vframeStream& vfst = anchor.vframe_stream();
+ if (!vfst.at_end()) {
+ vfst.next(); // this was the last frame decoded in the previous batch
+ if (!vfst.at_end()) {
+ int n = fill_in_frames(mode, vfst, frame_count, start_index, classes_array,
+ frames_array, end_index, CHECK_0);
+ if (n < 1) {
+ THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: later decode failed", 0L);
+ }
+ return end_index;
+ }
+ }
+ return end_index;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/prims/stackwalk.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 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_PRIMS_STACKWALK_HPP
+#define SHARE_VM_PRIMS_STACKWALK_HPP
+
+#include "oops/oop.hpp"
+#include "runtime/vframe.hpp"
+
+class StackWalkAnchor : public StackObj {
+private:
+ enum {
+ magic_pos = 0
+ };
+
+ JavaThread* _thread;
+ vframeStream _vfst;
+ jlong _anchor;
+public:
+ StackWalkAnchor(JavaThread* thread)
+ : _thread(thread), _vfst(thread), _anchor(0L) {}
+
+ vframeStream& vframe_stream() { return _vfst; }
+ JavaThread* thread() { return _thread; }
+
+ void setup_magic_on_entry(objArrayHandle classes_array);
+ bool check_magic(objArrayHandle classes_array);
+ bool cleanup_magic_on_exit(objArrayHandle classes_array);
+
+ bool is_valid_in(Thread* thread, objArrayHandle classes_array) {
+ return (_thread == thread && check_magic(classes_array));
+ }
+
+ jlong address_value() {
+ return (jlong) castable_address(this);
+ }
+
+ static StackWalkAnchor* from_current(JavaThread* thread, jlong anchor, objArrayHandle frames_array);
+};
+
+class StackWalk : public AllStatic {
+private:
+ static int fill_in_frames(jlong mode, vframeStream& vfst,
+ int max_nframes, int start_index,
+ objArrayHandle classes_array,
+ objArrayHandle frames_array,
+ int& end_index, TRAPS);
+
+ static void fill_stackframe(Handle stackFrame, const methodHandle& method, int bci);
+
+ static void fill_live_stackframe(Handle stackFrame, const methodHandle& method, int bci,
+ javaVFrame* jvf, TRAPS);
+
+ static inline bool skip_hidden_frames(int mode) {
+ return (mode & JVM_STACKWALK_SHOW_HIDDEN_FRAMES) == 0;
+ }
+ static inline bool need_method_info(int mode) {
+ return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0;
+ }
+ static inline bool live_frame_info(int mode) {
+ return (mode & JVM_STACKWALK_FILL_LIVE_STACK_FRAMES) != 0;
+ }
+ static inline bool fill_in_stacktrace(int mode) {
+ return (mode & JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE) != 0;
+ }
+
+public:
+ static oop walk(Handle stackStream, jlong mode,
+ int skip_frames, int frame_count, int start_index,
+ objArrayHandle classes_array,
+ objArrayHandle frames_array,
+ TRAPS);
+
+ static jint moreFrames(Handle stackStream, jlong mode, jlong magic,
+ int frame_count, int start_index,
+ objArrayHandle classes_array,
+ objArrayHandle frames_array,
+ TRAPS);
+};
+#endif // SHARE_VM_PRIMS_STACKWALK_HPP
--- a/hotspot/src/share/vm/runtime/globals.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/runtime/globals.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -3118,6 +3118,12 @@
"exceptions (0 means all)") \
range(0, max_jint/2) \
\
+ develop(bool, TraceStackWalk, false, \
+ "Trace stack walking") \
+ \
+ product(bool, MemberNameInStackFrame, true, \
+ "Use MemberName in StackFrame") \
+ \
/* notice: the max range value here is max_jint, not max_intx */ \
/* because of overflow issue */ \
NOT_EMBEDDED(diagnostic(intx, GuaranteedSafepointInterval, 1000, \
--- a/hotspot/src/share/vm/runtime/vframe.hpp Fri Nov 20 12:42:21 2015 +0100
+++ b/hotspot/src/share/vm/runtime/vframe.hpp Mon Nov 23 12:44:43 2015 -0800
@@ -317,10 +317,18 @@
intptr_t* frame_id() const { return _frame.id(); }
address frame_pc() const { return _frame.pc(); }
+ javaVFrame* java_frame() {
+ vframe* vf = vframe::new_vframe(&_frame, &_reg_map, _thread);
+ if (vf->is_java_frame()) {
+ return (javaVFrame*)vf;
+ }
+ return NULL;
+ }
+
CodeBlob* cb() const { return _frame.cb(); }
nmethod* nm() const {
- assert( cb() != NULL && cb()->is_nmethod(), "usage");
- return (nmethod*) cb();
+ assert( cb() != NULL && cb()->is_nmethod(), "usage");
+ return (nmethod*) cb();
}
// Frame type