8143911: Reintegrate JEP 259: Stack-Walking API
Co-authored-by: Brent Christian <brent.christian@oracle.com> Co-authored-by: Daniel Fuchs <daniel.fuchs@oracle.com> Co-authored-by: Hamlin Li <huaming.li@oracle.com> Reviewed-by: coleenp, dfuchs, bchristi, psandoz, sspitsyn
This commit is contained in:
parent
7171a533ff
commit
2b732b223e
@ -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;
|
||||
|
@ -1518,43 +1518,11 @@ void java_lang_Throwable::print(Handle throwable, outputStream* st) {
|
||||
// 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 @@ class BacktraceBuilder: public StackObj {
|
||||
}
|
||||
|
||||
_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 @@ class BacktraceBuilder: public StackObj {
|
||||
|
||||
};
|
||||
|
||||
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 @@ char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror,
|
||||
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 @@ char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror,
|
||||
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 @@ void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) {
|
||||
// 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 @@ oop java_lang_Throwable::get_stack_trace_element(oop throwable, int index, TRAPS
|
||||
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 @@ oop java_lang_StackTraceElement::create(Handle mirror, int method_id,
|
||||
}
|
||||
|
||||
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 @@ oop java_lang_StackTraceElement::create(Handle mirror, int method_id,
|
||||
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 @@ oop java_lang_StackTraceElement::create(const methodHandle& method, int bci, TRA
|
||||
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::declaringClass_offset;
|
||||
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 @@ void java_lang_StackTraceElement::set_lineNumber(oop element, int value) {
|
||||
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 @@ void JavaClasses::compute_offsets() {
|
||||
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();
|
||||
|
@ -1359,6 +1359,85 @@ class java_lang_StackTraceElement: AllStatic {
|
||||
};
|
||||
|
||||
|
||||
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 InjectedField {
|
||||
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
|
||||
|
||||
|
@ -73,4 +73,67 @@ inline bool java_lang_invoke_DirectMethodHandle::is_instance(oop obj) {
|
||||
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
|
||||
|
@ -180,11 +180,17 @@ class Ticks;
|
||||
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 ) \
|
||||
|
@ -311,6 +311,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>") \
|
||||
@ -411,6 +427,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") \
|
||||
@ -515,6 +543,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;") \
|
||||
|
@ -115,6 +115,7 @@ LatestMethodCache* Universe::_finalizer_register_cache = NULL;
|
||||
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 @@ void Universe::serialize(SerializeClosure* f, bool do_all) {
|
||||
_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 @@ jint universe_init() {
|
||||
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 @@ bool universe_post_init() {
|
||||
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.
|
||||
{
|
||||
|
@ -149,6 +149,7 @@ class Universe: AllStatic {
|
||||
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 @@ class Universe: AllStatic {
|
||||
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; }
|
||||
|
@ -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_ENTRY(jobject, JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint
|
||||
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 ///////////////////////////////////////////////
|
||||
|
||||
|
||||
|
@ -200,6 +200,37 @@ JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable);
|
||||
JNIEXPORT jobject JNICALL
|
||||
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
|
||||
*/
|
||||
|
470
hotspot/src/share/vm/prims/stackwalk.cpp
Normal file
470
hotspot/src/share/vm/prims/stackwalk.cpp
Normal file
@ -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;
|
||||
}
|
102
hotspot/src/share/vm/prims/stackwalk.hpp
Normal file
102
hotspot/src/share/vm/prims/stackwalk.hpp
Normal file
@ -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
|
@ -3115,6 +3115,12 @@ public:
|
||||
"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, \
|
||||
|
@ -317,10 +317,18 @@ class vframeStreamCommon : StackObj {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user