8268829: Provide an optimized way to walk the stack with Class object only
8210375: StackWalker::getCallerClass throws UnsupportedOperationException Reviewed-by: coleenp, dfuchs, bchristi
This commit is contained in:
parent
716201c77d
commit
111ecdbaf5
@ -57,6 +57,7 @@ JVM_DumpAllStacks
|
|||||||
JVM_DumpClassListToFile
|
JVM_DumpClassListToFile
|
||||||
JVM_DumpDynamicArchive
|
JVM_DumpDynamicArchive
|
||||||
JVM_DumpThreads
|
JVM_DumpThreads
|
||||||
|
JVM_ExpandStackFrameInfo
|
||||||
JVM_FillInStackTrace
|
JVM_FillInStackTrace
|
||||||
JVM_FindClassFromCaller
|
JVM_FindClassFromCaller
|
||||||
JVM_FindClassFromClass
|
JVM_FindClassFromClass
|
||||||
|
@ -2956,15 +2956,78 @@ void java_lang_StackTraceElement::decode(const methodHandle& method, int bci,
|
|||||||
}
|
}
|
||||||
#endif // INCLUDE_JVMCI
|
#endif // INCLUDE_JVMCI
|
||||||
|
|
||||||
|
// java_lang_ClassFrameInfo
|
||||||
|
|
||||||
|
int java_lang_ClassFrameInfo::_classOrMemberName_offset;
|
||||||
|
int java_lang_ClassFrameInfo::_flags_offset;
|
||||||
|
|
||||||
|
#define CLASSFRAMEINFO_FIELDS_DO(macro) \
|
||||||
|
macro(_classOrMemberName_offset, k, "classOrMemberName", object_signature, false); \
|
||||||
|
macro(_flags_offset, k, vmSymbols::flags_name(), int_signature, false)
|
||||||
|
|
||||||
|
void java_lang_ClassFrameInfo::compute_offsets() {
|
||||||
|
InstanceKlass* k = vmClasses::ClassFrameInfo_klass();
|
||||||
|
CLASSFRAMEINFO_FIELDS_DO(FIELD_COMPUTE_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INCLUDE_CDS
|
||||||
|
void java_lang_ClassFrameInfo::serialize_offsets(SerializeClosure* f) {
|
||||||
|
CLASSFRAMEINFO_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int get_flags(const methodHandle& m) {
|
||||||
|
int flags = (jushort)( m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS );
|
||||||
|
if (m->is_initializer()) {
|
||||||
|
flags |= java_lang_invoke_MemberName::MN_IS_CONSTRUCTOR;
|
||||||
|
} else {
|
||||||
|
flags |= java_lang_invoke_MemberName::MN_IS_METHOD;
|
||||||
|
}
|
||||||
|
if (m->caller_sensitive()) {
|
||||||
|
flags |= java_lang_invoke_MemberName::MN_CALLER_SENSITIVE;
|
||||||
|
}
|
||||||
|
if (m->is_hidden()) {
|
||||||
|
flags |= java_lang_invoke_MemberName::MN_HIDDEN_MEMBER;
|
||||||
|
}
|
||||||
|
assert((flags & 0xFF000000) == 0, "unexpected flags");
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
oop java_lang_ClassFrameInfo::classOrMemberName(oop obj) {
|
||||||
|
return obj->obj_field(_classOrMemberName_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int java_lang_ClassFrameInfo::flags(oop obj) {
|
||||||
|
return obj->int_field(_flags_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void java_lang_ClassFrameInfo::init_class(Handle stackFrame, const methodHandle& m) {
|
||||||
|
stackFrame->obj_field_put(_classOrMemberName_offset, m->method_holder()->java_mirror());
|
||||||
|
// flags is initialized when ClassFrameInfo object is constructed and retain the value
|
||||||
|
int flags = java_lang_ClassFrameInfo::flags(stackFrame()) | get_flags(m);
|
||||||
|
stackFrame->int_field_put(_flags_offset, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void java_lang_ClassFrameInfo::init_method(Handle stackFrame, const methodHandle& m, TRAPS) {
|
||||||
|
oop rmethod_name = java_lang_invoke_ResolvedMethodName::find_resolved_method(m, CHECK);
|
||||||
|
stackFrame->obj_field_put(_classOrMemberName_offset, rmethod_name);
|
||||||
|
// flags is initialized when ClassFrameInfo object is constructed and retain the value
|
||||||
|
int flags = java_lang_ClassFrameInfo::flags(stackFrame()) | get_flags(m);
|
||||||
|
stackFrame->int_field_put(_flags_offset, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// java_lang_StackFrameInfo
|
// java_lang_StackFrameInfo
|
||||||
|
|
||||||
int java_lang_StackFrameInfo::_memberName_offset;
|
int java_lang_StackFrameInfo::_type_offset;
|
||||||
|
int java_lang_StackFrameInfo::_name_offset;
|
||||||
int java_lang_StackFrameInfo::_bci_offset;
|
int java_lang_StackFrameInfo::_bci_offset;
|
||||||
int java_lang_StackFrameInfo::_version_offset;
|
int java_lang_StackFrameInfo::_version_offset;
|
||||||
int java_lang_StackFrameInfo::_contScope_offset;
|
int java_lang_StackFrameInfo::_contScope_offset;
|
||||||
|
|
||||||
#define STACKFRAMEINFO_FIELDS_DO(macro) \
|
#define STACKFRAMEINFO_FIELDS_DO(macro) \
|
||||||
macro(_memberName_offset, k, "memberName", object_signature, false); \
|
macro(_type_offset, k, "type", object_signature, false); \
|
||||||
|
macro(_name_offset, k, "name", string_signature, false); \
|
||||||
macro(_bci_offset, k, "bci", int_signature, false); \
|
macro(_bci_offset, k, "bci", int_signature, false); \
|
||||||
macro(_contScope_offset, k, "contScope", continuationscope_signature, false)
|
macro(_contScope_offset, k, "contScope", continuationscope_signature, false)
|
||||||
|
|
||||||
@ -2981,23 +3044,18 @@ void java_lang_StackFrameInfo::serialize_offsets(SerializeClosure* f) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Method* java_lang_StackFrameInfo::get_method(Handle stackFrame, InstanceKlass* holder, TRAPS) {
|
Method* java_lang_StackFrameInfo::get_method(oop obj) {
|
||||||
HandleMark hm(THREAD);
|
oop m = java_lang_ClassFrameInfo::classOrMemberName(obj);
|
||||||
Handle mname(THREAD, stackFrame->obj_field(_memberName_offset));
|
Method* method = java_lang_invoke_ResolvedMethodName::vmtarget(m);
|
||||||
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;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_StackFrameInfo::set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci, oop cont, TRAPS) {
|
void java_lang_StackFrameInfo::set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci, oop cont, TRAPS) {
|
||||||
// set Method* or mid/cpref
|
// set Method* or mid/cpref
|
||||||
HandleMark hm(THREAD);
|
HandleMark hm(THREAD);
|
||||||
Handle mname(THREAD, stackFrame->obj_field(_memberName_offset));
|
Handle cont_h(THREAD, cont);
|
||||||
Handle cont_h (THREAD, cont);
|
java_lang_ClassFrameInfo::init_method(stackFrame, method, CHECK);
|
||||||
InstanceKlass* ik = method->method_holder();
|
|
||||||
CallInfo info(method(), ik, CHECK);
|
|
||||||
MethodHandles::init_method_MemberName(mname, info);
|
|
||||||
// set bci
|
// set bci
|
||||||
java_lang_StackFrameInfo::set_bci(stackFrame(), bci);
|
java_lang_StackFrameInfo::set_bci(stackFrame(), bci);
|
||||||
// method may be redefined; store the version
|
// method may be redefined; store the version
|
||||||
@ -3012,28 +3070,42 @@ void java_lang_StackFrameInfo::set_method_and_bci(Handle stackFrame, const metho
|
|||||||
void java_lang_StackFrameInfo::to_stack_trace_element(Handle stackFrame, Handle stack_trace_element, TRAPS) {
|
void java_lang_StackFrameInfo::to_stack_trace_element(Handle stackFrame, Handle stack_trace_element, TRAPS) {
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
HandleMark hm(THREAD);
|
HandleMark hm(THREAD);
|
||||||
Handle mname(THREAD, stackFrame->obj_field(java_lang_StackFrameInfo::_memberName_offset));
|
|
||||||
Klass* clazz = java_lang_Class::as_Klass(java_lang_invoke_MemberName::clazz(mname()));
|
|
||||||
InstanceKlass* holder = InstanceKlass::cast(clazz);
|
|
||||||
Method* method = java_lang_StackFrameInfo::get_method(stackFrame, holder, CHECK);
|
|
||||||
|
|
||||||
|
Method* method = java_lang_StackFrameInfo::get_method(stackFrame());
|
||||||
|
InstanceKlass* holder = method->method_holder();
|
||||||
short version = stackFrame->short_field(_version_offset);
|
short version = stackFrame->short_field(_version_offset);
|
||||||
int bci = stackFrame->int_field(_bci_offset);
|
int bci = stackFrame->int_field(_bci_offset);
|
||||||
Symbol* name = method->name();
|
Symbol* name = method->name();
|
||||||
java_lang_StackTraceElement::fill_in(stack_trace_element, holder, methodHandle(THREAD, method), version, bci, name, CHECK);
|
java_lang_StackTraceElement::fill_in(stack_trace_element, holder, methodHandle(THREAD, method), version, bci, name, CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_StackFrameInfo::set_version(oop element, short value) {
|
oop java_lang_StackFrameInfo::type(oop obj) {
|
||||||
element->short_field_put(_version_offset, value);
|
return obj->obj_field(_type_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_StackFrameInfo::set_bci(oop element, int value) {
|
void java_lang_StackFrameInfo::set_type(oop obj, oop value) {
|
||||||
|
obj->obj_field_put(_type_offset, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
oop java_lang_StackFrameInfo::name(oop obj) {
|
||||||
|
return obj->obj_field(_name_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void java_lang_StackFrameInfo::set_name(oop obj, oop value) {
|
||||||
|
obj->obj_field_put(_name_offset, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void java_lang_StackFrameInfo::set_version(oop obj, short value) {
|
||||||
|
obj->short_field_put(_version_offset, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void java_lang_StackFrameInfo::set_bci(oop obj, int value) {
|
||||||
assert(value >= 0 && value < max_jushort, "must be a valid bci value");
|
assert(value >= 0 && value < max_jushort, "must be a valid bci value");
|
||||||
element->int_field_put(_bci_offset, value);
|
obj->int_field_put(_bci_offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_StackFrameInfo::set_contScope(oop element, oop value) {
|
void java_lang_StackFrameInfo::set_contScope(oop obj, oop value) {
|
||||||
element->obj_field_put(_contScope_offset, value);
|
obj->obj_field_put(_contScope_offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int java_lang_LiveStackFrameInfo::_monitors_offset;
|
int java_lang_LiveStackFrameInfo::_monitors_offset;
|
||||||
@ -3058,20 +3130,20 @@ void java_lang_LiveStackFrameInfo::serialize_offsets(SerializeClosure* f) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void java_lang_LiveStackFrameInfo::set_monitors(oop element, oop value) {
|
void java_lang_LiveStackFrameInfo::set_monitors(oop obj, oop value) {
|
||||||
element->obj_field_put(_monitors_offset, value);
|
obj->obj_field_put(_monitors_offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_LiveStackFrameInfo::set_locals(oop element, oop value) {
|
void java_lang_LiveStackFrameInfo::set_locals(oop obj, oop value) {
|
||||||
element->obj_field_put(_locals_offset, value);
|
obj->obj_field_put(_locals_offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_LiveStackFrameInfo::set_operands(oop element, oop value) {
|
void java_lang_LiveStackFrameInfo::set_operands(oop obj, oop value) {
|
||||||
element->obj_field_put(_operands_offset, value);
|
obj->obj_field_put(_operands_offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void java_lang_LiveStackFrameInfo::set_mode(oop element, int value) {
|
void java_lang_LiveStackFrameInfo::set_mode(oop obj, int value) {
|
||||||
element->int_field_put(_mode_offset, value);
|
obj->int_field_put(_mode_offset, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3947,14 +4019,19 @@ void java_lang_invoke_MemberName::serialize_offsets(SerializeClosure* f) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define RESOLVEDMETHOD_FIELDS_DO(macro) \
|
||||||
|
macro(_vmholder_offset, k, "vmholder", class_signature, false)
|
||||||
|
|
||||||
void java_lang_invoke_ResolvedMethodName::compute_offsets() {
|
void java_lang_invoke_ResolvedMethodName::compute_offsets() {
|
||||||
InstanceKlass* k = vmClasses::ResolvedMethodName_klass();
|
InstanceKlass* k = vmClasses::ResolvedMethodName_klass();
|
||||||
assert(k != nullptr, "jdk mismatch");
|
assert(k != nullptr, "jdk mismatch");
|
||||||
|
RESOLVEDMETHOD_FIELDS_DO(FIELD_COMPUTE_OFFSET);
|
||||||
RESOLVEDMETHOD_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
|
RESOLVEDMETHOD_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
void java_lang_invoke_ResolvedMethodName::serialize_offsets(SerializeClosure* f) {
|
void java_lang_invoke_ResolvedMethodName::serialize_offsets(SerializeClosure* f) {
|
||||||
|
RESOLVEDMETHOD_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
|
||||||
RESOLVEDMETHOD_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET);
|
RESOLVEDMETHOD_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -5217,6 +5294,7 @@ void java_lang_InternalError::serialize_offsets(SerializeClosure* f) {
|
|||||||
f(java_lang_reflect_Parameter) \
|
f(java_lang_reflect_Parameter) \
|
||||||
f(java_lang_Module) \
|
f(java_lang_Module) \
|
||||||
f(java_lang_StackTraceElement) \
|
f(java_lang_StackTraceElement) \
|
||||||
|
f(java_lang_ClassFrameInfo) \
|
||||||
f(java_lang_StackFrameInfo) \
|
f(java_lang_StackFrameInfo) \
|
||||||
f(java_lang_LiveStackFrameInfo) \
|
f(java_lang_LiveStackFrameInfo) \
|
||||||
f(jdk_internal_vm_ContinuationScope) \
|
f(jdk_internal_vm_ContinuationScope) \
|
||||||
|
@ -1199,7 +1199,6 @@ class jdk_internal_foreign_abi_CallConv: AllStatic {
|
|||||||
// (These are a private interface for Java code to query the class hierarchy.)
|
// (These are a private interface for Java code to query the class hierarchy.)
|
||||||
|
|
||||||
#define RESOLVEDMETHOD_INJECTED_FIELDS(macro) \
|
#define RESOLVEDMETHOD_INJECTED_FIELDS(macro) \
|
||||||
macro(java_lang_invoke_ResolvedMethodName, vmholder, object_signature, false) \
|
|
||||||
macro(java_lang_invoke_ResolvedMethodName, vmtarget, intptr_signature, false)
|
macro(java_lang_invoke_ResolvedMethodName, vmtarget, intptr_signature, false)
|
||||||
|
|
||||||
class java_lang_invoke_ResolvedMethodName : AllStatic {
|
class java_lang_invoke_ResolvedMethodName : AllStatic {
|
||||||
@ -1288,6 +1287,7 @@ class java_lang_invoke_MemberName: AllStatic {
|
|||||||
MN_IS_TYPE = 0x00080000, // nested type
|
MN_IS_TYPE = 0x00080000, // nested type
|
||||||
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
||||||
MN_TRUSTED_FINAL = 0x00200000, // trusted final field
|
MN_TRUSTED_FINAL = 0x00200000, // trusted final field
|
||||||
|
MN_HIDDEN_MEMBER = 0x00400000, // @Hidden annotation detected
|
||||||
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
||||||
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
|
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
|
||||||
MN_NESTMATE_CLASS = 0x00000001,
|
MN_NESTMATE_CLASS = 0x00000001,
|
||||||
@ -1591,6 +1591,26 @@ class Backtrace: AllStatic {
|
|||||||
friend class JavaClasses;
|
friend class JavaClasses;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class java_lang_ClassFrameInfo: AllStatic {
|
||||||
|
private:
|
||||||
|
static int _classOrMemberName_offset;
|
||||||
|
static int _flags_offset;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static oop classOrMemberName(oop info);
|
||||||
|
static int flags(oop info);
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
static void init_class(Handle stackFrame, const methodHandle& m);
|
||||||
|
static void init_method(Handle stackFrame, const methodHandle& m, TRAPS);
|
||||||
|
|
||||||
|
static void compute_offsets();
|
||||||
|
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
|
||||||
|
|
||||||
|
// Debugging
|
||||||
|
friend class JavaClasses;
|
||||||
|
};
|
||||||
|
|
||||||
// Interface to java.lang.StackFrameInfo objects
|
// Interface to java.lang.StackFrameInfo objects
|
||||||
|
|
||||||
#define STACKFRAMEINFO_INJECTED_FIELDS(macro) \
|
#define STACKFRAMEINFO_INJECTED_FIELDS(macro) \
|
||||||
@ -1598,16 +1618,22 @@ class Backtrace: AllStatic {
|
|||||||
|
|
||||||
class java_lang_StackFrameInfo: AllStatic {
|
class java_lang_StackFrameInfo: AllStatic {
|
||||||
private:
|
private:
|
||||||
static int _memberName_offset;
|
static int _type_offset;
|
||||||
|
static int _name_offset;
|
||||||
static int _bci_offset;
|
static int _bci_offset;
|
||||||
static int _version_offset;
|
static int _version_offset;
|
||||||
static int _contScope_offset;
|
static int _contScope_offset;
|
||||||
|
|
||||||
static Method* get_method(Handle stackFrame, InstanceKlass* holder, TRAPS);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Getters
|
||||||
|
static oop name(oop info);
|
||||||
|
static oop type(oop info);
|
||||||
|
static Method* get_method(oop info);
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
static void set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci, oop cont, TRAPS);
|
static void set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci, oop cont, TRAPS);
|
||||||
|
static void set_name(oop info, oop value);
|
||||||
|
static void set_type(oop info, oop value);
|
||||||
static void set_bci(oop info, int value);
|
static void set_bci(oop info, int value);
|
||||||
|
|
||||||
static void set_version(oop info, short value);
|
static void set_version(oop info, short value);
|
||||||
|
@ -160,6 +160,7 @@
|
|||||||
/* Stack Walking */ \
|
/* Stack Walking */ \
|
||||||
do_klass(StackWalker_klass, java_lang_StackWalker ) \
|
do_klass(StackWalker_klass, java_lang_StackWalker ) \
|
||||||
do_klass(AbstractStackWalker_klass, java_lang_StackStreamFactory_AbstractStackWalker ) \
|
do_klass(AbstractStackWalker_klass, java_lang_StackStreamFactory_AbstractStackWalker ) \
|
||||||
|
do_klass(ClassFrameInfo_klass, java_lang_ClassFrameInfo ) \
|
||||||
do_klass(StackFrameInfo_klass, java_lang_StackFrameInfo ) \
|
do_klass(StackFrameInfo_klass, java_lang_StackFrameInfo ) \
|
||||||
do_klass(LiveStackFrameInfo_klass, java_lang_LiveStackFrameInfo ) \
|
do_klass(LiveStackFrameInfo_klass, java_lang_LiveStackFrameInfo ) \
|
||||||
\
|
\
|
||||||
|
@ -378,6 +378,7 @@ class SerializeClosure;
|
|||||||
/* Support for JVMCI */ \
|
/* Support for JVMCI */ \
|
||||||
JVMCI_VM_SYMBOLS_DO(template, do_alias) \
|
JVMCI_VM_SYMBOLS_DO(template, do_alias) \
|
||||||
\
|
\
|
||||||
|
template(java_lang_ClassFrameInfo, "java/lang/ClassFrameInfo") \
|
||||||
template(java_lang_StackWalker, "java/lang/StackWalker") \
|
template(java_lang_StackWalker, "java/lang/StackWalker") \
|
||||||
template(java_lang_StackFrameInfo, "java/lang/StackFrameInfo") \
|
template(java_lang_StackFrameInfo, "java/lang/StackFrameInfo") \
|
||||||
template(java_lang_LiveStackFrameInfo, "java/lang/LiveStackFrameInfo") \
|
template(java_lang_LiveStackFrameInfo, "java/lang/LiveStackFrameInfo") \
|
||||||
|
@ -244,19 +244,21 @@ JVM_GetExtendedNPEMessage(JNIEnv *env, jthrowable throwable);
|
|||||||
* java.lang.StackWalker
|
* java.lang.StackWalker
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2,
|
JVM_STACKWALK_CLASS_INFO_ONLY = 0x2,
|
||||||
JVM_STACKWALK_GET_CALLER_CLASS = 0x04,
|
|
||||||
JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20,
|
JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20,
|
||||||
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100
|
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100
|
||||||
};
|
};
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
JVM_ExpandStackFrameInfo(JNIEnv *env, jobject obj);
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL
|
JNIEXPORT jobject JNICALL
|
||||||
JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
|
JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jint mode,
|
||||||
jint skip_frames, jobject contScope, jobject cont,
|
jint skip_frames, jobject contScope, jobject cont,
|
||||||
jint frame_count, jint start_index, jobjectArray frames);
|
jint frame_count, jint start_index, jobjectArray frames);
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor,
|
JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jint mode, jlong anchor,
|
||||||
jint frame_count, jint start_index,
|
jint frame_count, jint start_index,
|
||||||
jobjectArray frames);
|
jobjectArray frames);
|
||||||
|
|
||||||
|
@ -542,14 +542,28 @@ JVM_END
|
|||||||
JVM_ENTRY(void, JVM_InitStackTraceElement(JNIEnv* env, jobject element, jobject stackFrameInfo))
|
JVM_ENTRY(void, JVM_InitStackTraceElement(JNIEnv* env, jobject element, jobject stackFrameInfo))
|
||||||
Handle stack_frame_info(THREAD, JNIHandles::resolve_non_null(stackFrameInfo));
|
Handle stack_frame_info(THREAD, JNIHandles::resolve_non_null(stackFrameInfo));
|
||||||
Handle stack_trace_element(THREAD, JNIHandles::resolve_non_null(element));
|
Handle stack_trace_element(THREAD, JNIHandles::resolve_non_null(element));
|
||||||
java_lang_StackFrameInfo::to_stack_trace_element(stack_frame_info, stack_trace_element, THREAD);
|
java_lang_StackFrameInfo::to_stack_trace_element(stack_frame_info, stack_trace_element, CHECK);
|
||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
|
|
||||||
// java.lang.StackWalker //////////////////////////////////////////////////////
|
// java.lang.StackWalker //////////////////////////////////////////////////////
|
||||||
|
JVM_ENTRY(void, JVM_ExpandStackFrameInfo(JNIEnv *env, jobject obj))
|
||||||
|
Handle stack_frame_info(THREAD, JNIHandles::resolve_non_null(obj));
|
||||||
|
|
||||||
|
bool have_name = (java_lang_StackFrameInfo::name(stack_frame_info()) != nullptr);
|
||||||
|
bool have_type = (java_lang_StackFrameInfo::type(stack_frame_info()) != nullptr);
|
||||||
|
Method* method = java_lang_StackFrameInfo::get_method(stack_frame_info());
|
||||||
|
if (!have_name) {
|
||||||
|
oop name = StringTable::intern(method->name(), CHECK);
|
||||||
|
java_lang_StackFrameInfo::set_name(stack_frame_info(), name);
|
||||||
|
}
|
||||||
|
if (!have_type) {
|
||||||
|
Handle type = java_lang_String::create_from_symbol(method->signature(), CHECK);
|
||||||
|
java_lang_StackFrameInfo::set_type(stack_frame_info(), type());
|
||||||
|
}
|
||||||
|
JVM_END
|
||||||
|
|
||||||
JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
|
JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jint mode,
|
||||||
jint skip_frames, jobject contScope, jobject cont,
|
jint skip_frames, jobject contScope, jobject cont,
|
||||||
jint frame_count, jint start_index, jobjectArray frames))
|
jint frame_count, jint start_index, jobjectArray frames))
|
||||||
if (!thread->has_last_Java_frame()) {
|
if (!thread->has_last_Java_frame()) {
|
||||||
@ -559,7 +573,7 @@ JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mod
|
|||||||
Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
|
Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
|
||||||
Handle contScope_h(THREAD, JNIHandles::resolve(contScope));
|
Handle contScope_h(THREAD, JNIHandles::resolve(contScope));
|
||||||
Handle cont_h(THREAD, JNIHandles::resolve(cont));
|
Handle cont_h(THREAD, JNIHandles::resolve(cont));
|
||||||
// frames array is a Class<?>[] array when only getting caller reference,
|
// frames array is a ClassFrameInfo[] array when only getting caller reference,
|
||||||
// and a StackFrameInfo[] array (or derivative) otherwise. It should never
|
// and a StackFrameInfo[] array (or derivative) otherwise. It should never
|
||||||
// be null.
|
// be null.
|
||||||
objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
|
objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
|
||||||
@ -576,10 +590,10 @@ JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mod
|
|||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
|
|
||||||
JVM_ENTRY(jint, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor,
|
JVM_ENTRY(jint, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jint mode, jlong anchor,
|
||||||
jint frame_count, jint start_index,
|
jint frame_count, jint start_index,
|
||||||
jobjectArray frames))
|
jobjectArray frames))
|
||||||
// frames array is a Class<?>[] array when only getting caller reference,
|
// frames array is a ClassFrameInfo[] array when only getting caller reference,
|
||||||
// and a StackFrameInfo[] array (or derivative) otherwise. It should never
|
// and a StackFrameInfo[] array (or derivative) otherwise. It should never
|
||||||
// be null.
|
// be null.
|
||||||
objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
|
objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
|
||||||
|
@ -137,6 +137,7 @@ enum {
|
|||||||
IS_TYPE = java_lang_invoke_MemberName::MN_IS_TYPE,
|
IS_TYPE = java_lang_invoke_MemberName::MN_IS_TYPE,
|
||||||
CALLER_SENSITIVE = java_lang_invoke_MemberName::MN_CALLER_SENSITIVE,
|
CALLER_SENSITIVE = java_lang_invoke_MemberName::MN_CALLER_SENSITIVE,
|
||||||
TRUSTED_FINAL = java_lang_invoke_MemberName::MN_TRUSTED_FINAL,
|
TRUSTED_FINAL = java_lang_invoke_MemberName::MN_TRUSTED_FINAL,
|
||||||
|
HIDDEN_MEMBER = java_lang_invoke_MemberName::MN_HIDDEN_MEMBER,
|
||||||
REFERENCE_KIND_SHIFT = java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT,
|
REFERENCE_KIND_SHIFT = java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT,
|
||||||
REFERENCE_KIND_MASK = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK,
|
REFERENCE_KIND_MASK = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK,
|
||||||
LM_UNCONDITIONAL = java_lang_invoke_MemberName::MN_UNCONDITIONAL_MODE,
|
LM_UNCONDITIONAL = java_lang_invoke_MemberName::MN_UNCONDITIONAL_MODE,
|
||||||
@ -1003,6 +1004,7 @@ void MethodHandles::trace_method_handle_interpreter_entry(MacroAssembler* _masm,
|
|||||||
template(java_lang_invoke_MemberName,MN_IS_TYPE) \
|
template(java_lang_invoke_MemberName,MN_IS_TYPE) \
|
||||||
template(java_lang_invoke_MemberName,MN_CALLER_SENSITIVE) \
|
template(java_lang_invoke_MemberName,MN_CALLER_SENSITIVE) \
|
||||||
template(java_lang_invoke_MemberName,MN_TRUSTED_FINAL) \
|
template(java_lang_invoke_MemberName,MN_TRUSTED_FINAL) \
|
||||||
|
template(java_lang_invoke_MemberName,MN_HIDDEN_MEMBER) \
|
||||||
template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_SHIFT) \
|
template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_SHIFT) \
|
||||||
template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_MASK) \
|
template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_MASK) \
|
||||||
template(java_lang_invoke_MemberName,MN_NESTMATE_CLASS) \
|
template(java_lang_invoke_MemberName,MN_NESTMATE_CLASS) \
|
||||||
|
@ -80,7 +80,7 @@ void BaseFrameStream::set_continuation(Handle cont) {
|
|||||||
_continuation.replace(cont());
|
_continuation.replace(cont());
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaFrameStream::JavaFrameStream(JavaThread* thread, jlong mode, Handle cont_scope, Handle cont)
|
JavaFrameStream::JavaFrameStream(JavaThread* thread, jint mode, Handle cont_scope, Handle cont)
|
||||||
: BaseFrameStream(thread, cont),
|
: BaseFrameStream(thread, cont),
|
||||||
_vfst(cont.is_null()
|
_vfst(cont.is_null()
|
||||||
? vframeStream(thread, cont_scope)
|
? vframeStream(thread, cont_scope)
|
||||||
@ -154,15 +154,15 @@ BaseFrameStream* BaseFrameStream::from_current(JavaThread* thread, jlong magic,
|
|||||||
// BaseFrameStream stream of frames
|
// BaseFrameStream stream of frames
|
||||||
// max_nframes Maximum number of frames to be filled.
|
// max_nframes Maximum number of frames to be filled.
|
||||||
// start_index Start index to the user-supplied buffers.
|
// start_index Start index to the user-supplied buffers.
|
||||||
// frames_array Buffer to store Class or StackFrame in, starting at start_index.
|
// frames_array Buffer to store stack frame information in, starting at start_index.
|
||||||
// frames array is a Class<?>[] array when only getting caller
|
// frames array is a ClassFrameInfo[] array when only getting caller
|
||||||
// reference, and a StackFrameInfo[] array (or derivative)
|
// reference, and a StackFrameInfo[] array (or derivative)
|
||||||
// otherwise. It should never be null.
|
// otherwise. It should never be null.
|
||||||
// end_index End index to the user-supplied buffers with unpacked frames.
|
// end_index End index to the user-supplied buffers with unpacked frames.
|
||||||
//
|
//
|
||||||
// Returns the number of frames whose information was transferred into the buffers.
|
// Returns the number of frames whose information was transferred into the buffers.
|
||||||
//
|
//
|
||||||
int StackWalk::fill_in_frames(jlong mode, BaseFrameStream& stream,
|
int StackWalk::fill_in_frames(jint mode, BaseFrameStream& stream,
|
||||||
int max_nframes, int start_index,
|
int max_nframes, int start_index,
|
||||||
objArrayHandle frames_array,
|
objArrayHandle frames_array,
|
||||||
int& end_index, TRAPS) {
|
int& end_index, TRAPS) {
|
||||||
@ -186,16 +186,17 @@ int StackWalk::fill_in_frames(jlong mode, BaseFrameStream& stream,
|
|||||||
|
|
||||||
// skip hidden frames for default StackWalker option (i.e. SHOW_HIDDEN_FRAMES
|
// skip hidden frames for default StackWalker option (i.e. SHOW_HIDDEN_FRAMES
|
||||||
// not set) and when StackWalker::getCallerClass is called
|
// not set) and when StackWalker::getCallerClass is called
|
||||||
if (!ShowHiddenFrames && (skip_hidden_frames(mode) || get_caller_class(mode))) {
|
|
||||||
if (method->is_hidden()) {
|
|
||||||
LogTarget(Debug, stackwalk) lt;
|
LogTarget(Debug, stackwalk) lt;
|
||||||
|
if (!ShowHiddenFrames && skip_hidden_frames(mode)) {
|
||||||
|
if (method->is_hidden()) {
|
||||||
if (lt.is_enabled()) {
|
if (lt.is_enabled()) {
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
LogStream ls(lt);
|
LogStream ls(lt);
|
||||||
ls.print(" hidden method: ");
|
ls.print(" skip hidden method: ");
|
||||||
method->print_short_name(&ls);
|
method->print_short_name(&ls);
|
||||||
ls.cr();
|
ls.cr();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We end a batch on continuation bottom to let the Java side skip top frames of the next one
|
// We end a batch on continuation bottom to let the Java side skip top frames of the next one
|
||||||
if (stream.continuation() != nullptr && method->intrinsic_id() == vmIntrinsics::_Continuation_enter) break;
|
if (stream.continuation() != nullptr && method->intrinsic_id() == vmIntrinsics::_Continuation_enter) break;
|
||||||
continue;
|
continue;
|
||||||
@ -203,7 +204,6 @@ int StackWalk::fill_in_frames(jlong mode, BaseFrameStream& stream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int index = end_index++;
|
int index = end_index++;
|
||||||
LogTarget(Debug, stackwalk) lt;
|
|
||||||
if (lt.is_enabled()) {
|
if (lt.is_enabled()) {
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
LogStream ls(lt);
|
LogStream ls(lt);
|
||||||
@ -212,13 +212,6 @@ int StackWalk::fill_in_frames(jlong mode, BaseFrameStream& stream,
|
|||||||
ls.print_cr(" bci=%d", stream.bci());
|
ls.print_cr(" bci=%d", stream.bci());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!need_method_info(mode) && get_caller_class(mode) &&
|
|
||||||
index == start_index && method->caller_sensitive()) {
|
|
||||||
ResourceMark rm(THREAD);
|
|
||||||
THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(),
|
|
||||||
err_msg("StackWalker::getCallerClass called from @CallerSensitive '%s' method",
|
|
||||||
method->external_name()));
|
|
||||||
}
|
|
||||||
// fill in StackFrameInfo and initialize MemberName
|
// fill in StackFrameInfo and initialize MemberName
|
||||||
stream.fill_frame(index, frames_array, methodHandle(THREAD, method), CHECK_0);
|
stream.fill_frame(index, frames_array, methodHandle(THREAD, method), CHECK_0);
|
||||||
|
|
||||||
@ -256,7 +249,9 @@ void JavaFrameStream::fill_frame(int index, objArrayHandle frames_array,
|
|||||||
Handle stackFrame(THREAD, frames_array->obj_at(index));
|
Handle stackFrame(THREAD, frames_array->obj_at(index));
|
||||||
fill_stackframe(stackFrame, method, CHECK);
|
fill_stackframe(stackFrame, method, CHECK);
|
||||||
} else {
|
} else {
|
||||||
frames_array->obj_at_put(index, method->method_holder()->java_mirror());
|
HandleMark hm(THREAD);
|
||||||
|
Handle stackFrame(THREAD, frames_array->obj_at(index));
|
||||||
|
java_lang_ClassFrameInfo::init_class(stackFrame, method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +351,7 @@ objArrayHandle LiveFrameStream::monitors_to_object_array(GrowableArray<MonitorIn
|
|||||||
return array_h;
|
return array_h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill StackFrameInfo with bci and initialize memberName
|
// Fill StackFrameInfo with bci and initialize ResolvedMethodName
|
||||||
void BaseFrameStream::fill_stackframe(Handle stackFrame, const methodHandle& method, TRAPS) {
|
void BaseFrameStream::fill_stackframe(Handle stackFrame, const methodHandle& method, TRAPS) {
|
||||||
java_lang_StackFrameInfo::set_method_and_bci(stackFrame, method, bci(), cont(), THREAD);
|
java_lang_StackFrameInfo::set_method_and_bci(stackFrame, method, bci(), cont(), THREAD);
|
||||||
}
|
}
|
||||||
@ -405,21 +400,21 @@ void LiveFrameStream::fill_live_stackframe(Handle stackFrame,
|
|||||||
// cont_scope Continuation scope to walk (if not in this scope, we'll walk all the way).
|
// cont_scope Continuation scope to walk (if not in this scope, we'll walk all the way).
|
||||||
// frame_count Number of frames to be traversed.
|
// frame_count Number of frames to be traversed.
|
||||||
// start_index Start index to the user-supplied buffers.
|
// start_index Start index to the user-supplied buffers.
|
||||||
// frames_array Buffer to store StackFrame in, starting at start_index.
|
// frames_array Buffer to store stack frame info in, starting at start_index.
|
||||||
// frames array is a Class<?>[] array when only getting caller
|
// frames array is a ClassFrameInfo[] array when only getting caller
|
||||||
// reference, and a StackFrameInfo[] array (or derivative)
|
// reference, and a StackFrameInfo[] array (or derivative)
|
||||||
// otherwise. It should never be null.
|
// otherwise. It should never be null.
|
||||||
//
|
//
|
||||||
// Returns Object returned from AbstractStackWalker::doStackWalk call.
|
// Returns Object returned from AbstractStackWalker::doStackWalk call.
|
||||||
//
|
//
|
||||||
oop StackWalk::walk(Handle stackStream, jlong mode, int skip_frames, Handle cont_scope, Handle cont,
|
oop StackWalk::walk(Handle stackStream, jint mode, int skip_frames, Handle cont_scope, Handle cont,
|
||||||
int frame_count, int start_index, objArrayHandle frames_array,
|
int frame_count, int start_index, objArrayHandle frames_array,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
HandleMark hm(THREAD); // needed to store a continuation in the RegisterMap
|
HandleMark hm(THREAD); // needed to store a continuation in the RegisterMap
|
||||||
|
|
||||||
JavaThread* jt = THREAD;
|
JavaThread* jt = THREAD;
|
||||||
log_debug(stackwalk)("Start walking: mode " JLONG_FORMAT " skip %d frames batch size %d", mode, skip_frames, frame_count);
|
log_debug(stackwalk)("Start walking: mode " INT32_FORMAT_X " skip %d frames batch size %d", mode, skip_frames, frame_count);
|
||||||
LogTarget(Debug, stackwalk) lt;
|
LogTarget(Debug, stackwalk) lt;
|
||||||
if (lt.is_enabled()) {
|
if (lt.is_enabled()) {
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
@ -437,7 +432,6 @@ oop StackWalk::walk(Handle stackStream, jlong mode, int skip_frames, Handle cont
|
|||||||
|
|
||||||
// Setup traversal onto my stack.
|
// Setup traversal onto my stack.
|
||||||
if (live_frame_info(mode)) {
|
if (live_frame_info(mode)) {
|
||||||
assert(use_frames_array(mode), "Bad mode for get live frame");
|
|
||||||
RegisterMap regMap = cont.is_null() ? RegisterMap(jt,
|
RegisterMap regMap = cont.is_null() ? RegisterMap(jt,
|
||||||
RegisterMap::UpdateMap::include,
|
RegisterMap::UpdateMap::include,
|
||||||
RegisterMap::ProcessFrames::include,
|
RegisterMap::ProcessFrames::include,
|
||||||
@ -454,7 +448,7 @@ oop StackWalk::walk(Handle stackStream, jlong mode, int skip_frames, Handle cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
oop StackWalk::fetchFirstBatch(BaseFrameStream& stream, Handle stackStream,
|
oop StackWalk::fetchFirstBatch(BaseFrameStream& stream, Handle stackStream,
|
||||||
jlong mode, int skip_frames, int frame_count,
|
jint mode, int skip_frames, int frame_count,
|
||||||
int start_index, objArrayHandle frames_array, TRAPS) {
|
int start_index, objArrayHandle frames_array, TRAPS) {
|
||||||
methodHandle m_doStackWalk(THREAD, Universe::do_stack_walk_method());
|
methodHandle m_doStackWalk(THREAD, Universe::do_stack_walk_method());
|
||||||
|
|
||||||
@ -547,7 +541,7 @@ oop StackWalk::fetchFirstBatch(BaseFrameStream& stream, Handle stackStream,
|
|||||||
//
|
//
|
||||||
// Returns the end index of frame filled in the buffer.
|
// Returns the end index of frame filled in the buffer.
|
||||||
//
|
//
|
||||||
jint StackWalk::fetchNextBatch(Handle stackStream, jlong mode, jlong magic,
|
jint StackWalk::fetchNextBatch(Handle stackStream, jint mode, jlong magic,
|
||||||
int frame_count, int start_index,
|
int frame_count, int start_index,
|
||||||
objArrayHandle frames_array,
|
objArrayHandle frames_array,
|
||||||
TRAPS)
|
TRAPS)
|
||||||
|
@ -90,7 +90,7 @@ private:
|
|||||||
bool _need_method_info;
|
bool _need_method_info;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JavaFrameStream(JavaThread* thread, jlong mode, Handle cont_scope, Handle cont);
|
JavaFrameStream(JavaThread* thread, jint mode, Handle cont_scope, Handle cont);
|
||||||
|
|
||||||
const RegisterMap* reg_map() override { return _vfst.reg_map(); };
|
const RegisterMap* reg_map() override { return _vfst.reg_map(); };
|
||||||
|
|
||||||
@ -142,37 +142,32 @@ public:
|
|||||||
|
|
||||||
class StackWalk : public AllStatic {
|
class StackWalk : public AllStatic {
|
||||||
private:
|
private:
|
||||||
static int fill_in_frames(jlong mode, BaseFrameStream& stream,
|
static int fill_in_frames(jint mode, BaseFrameStream& stream,
|
||||||
int max_nframes, int start_index,
|
int max_nframes, int start_index,
|
||||||
objArrayHandle frames_array,
|
objArrayHandle frames_array,
|
||||||
int& end_index, TRAPS);
|
int& end_index, TRAPS);
|
||||||
|
|
||||||
static inline bool get_caller_class(jlong mode) {
|
static inline bool skip_hidden_frames(jint mode) {
|
||||||
return (mode & JVM_STACKWALK_GET_CALLER_CLASS) != 0;
|
|
||||||
}
|
|
||||||
static inline bool skip_hidden_frames(jlong mode) {
|
|
||||||
return (mode & JVM_STACKWALK_SHOW_HIDDEN_FRAMES) == 0;
|
return (mode & JVM_STACKWALK_SHOW_HIDDEN_FRAMES) == 0;
|
||||||
}
|
}
|
||||||
static inline bool live_frame_info(jlong mode) {
|
static inline bool live_frame_info(jint mode) {
|
||||||
return (mode & JVM_STACKWALK_FILL_LIVE_STACK_FRAMES) != 0;
|
return (mode & JVM_STACKWALK_FILL_LIVE_STACK_FRAMES) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static inline bool need_method_info(jlong mode) {
|
static inline bool need_method_info(jint mode) {
|
||||||
return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0;
|
return (mode & JVM_STACKWALK_CLASS_INFO_ONLY) == 0;
|
||||||
}
|
}
|
||||||
static inline bool use_frames_array(jlong mode) {
|
|
||||||
return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0;
|
static oop walk(Handle stackStream, jint mode, int skip_frames, Handle cont_scope, Handle cont,
|
||||||
}
|
|
||||||
static oop walk(Handle stackStream, jlong mode, int skip_frames, Handle cont_scope, Handle cont,
|
|
||||||
int frame_count, int start_index, objArrayHandle frames_array,
|
int frame_count, int start_index, objArrayHandle frames_array,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
static oop fetchFirstBatch(BaseFrameStream& stream, Handle stackStream,
|
static oop fetchFirstBatch(BaseFrameStream& stream, Handle stackStream,
|
||||||
jlong mode, int skip_frames, int frame_count,
|
jint mode, int skip_frames, int frame_count,
|
||||||
int start_index, objArrayHandle frames_array, TRAPS);
|
int start_index, objArrayHandle frames_array, TRAPS);
|
||||||
|
|
||||||
static jint fetchNextBatch(Handle stackStream, jlong mode, jlong magic,
|
static jint fetchNextBatch(Handle stackStream, jint mode, jlong magic,
|
||||||
int frame_count, int start_index,
|
int frame_count, int start_index,
|
||||||
objArrayHandle frames_array, TRAPS);
|
objArrayHandle frames_array, TRAPS);
|
||||||
|
|
||||||
|
135
src/java.base/share/classes/java/lang/ClassFrameInfo.java
Normal file
135
src/java.base/share/classes/java/lang/ClassFrameInfo.java
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package java.lang;
|
||||||
|
|
||||||
|
import jdk.internal.access.JavaLangInvokeAccess;
|
||||||
|
import jdk.internal.access.SharedSecrets;
|
||||||
|
import java.lang.StackWalker.StackFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassFrameInfo is an implementation of StackFrame that contains only
|
||||||
|
* the class name and declaring class.
|
||||||
|
*
|
||||||
|
* Methods that access the method information such as method name,
|
||||||
|
* will throw UnsupportedOperationException.
|
||||||
|
*
|
||||||
|
* @see StackWalker.Option#DROP_METHOD_INFO
|
||||||
|
*/
|
||||||
|
class ClassFrameInfo implements StackFrame {
|
||||||
|
static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||||
|
|
||||||
|
Object classOrMemberName; // Class or ResolvedMemberName initialized by VM
|
||||||
|
int flags; // updated by VM to set hidden and caller-sensitive bits
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct an empty ClassFrameInfo object that will be filled by the VM
|
||||||
|
* during stack walking.
|
||||||
|
*
|
||||||
|
* @see StackStreamFactory.AbstractStackWalker#callStackWalk
|
||||||
|
* @see StackStreamFactory.AbstractStackWalker#fetchStackFrames
|
||||||
|
*/
|
||||||
|
ClassFrameInfo(StackWalker walker) {
|
||||||
|
this.flags = walker.retainClassRef ? RETAIN_CLASS_REF_BIT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// package-private called by StackStreamFactory to skip
|
||||||
|
// the capability check
|
||||||
|
Class<?> declaringClass() {
|
||||||
|
return (Class<?>) classOrMemberName;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCallerSensitive() {
|
||||||
|
return JLIA.isCallerSensitive(flags & MEMBER_INFO_FLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isHidden() {
|
||||||
|
return JLIA.isHiddenMember(flags & MEMBER_INFO_FLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- implementation of StackFrame methods
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return declaringClass().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getDeclaringClass() {
|
||||||
|
ensureRetainClassRefEnabled();
|
||||||
|
return declaringClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getByteCodeIndex() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFileName() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLineNumber() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNativeMethod() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StackTraceElement toStackTraceElement() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String tags = isHidden() ? " hidden" : "";
|
||||||
|
if (isCallerSensitive()) {
|
||||||
|
tags += " caller sensitive";
|
||||||
|
}
|
||||||
|
return declaringClass().getName() + " " + tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int MEMBER_INFO_FLAGS = 0x00FFFFFF;
|
||||||
|
private static final int RETAIN_CLASS_REF_BIT = 0x08000000; // retainClassRef
|
||||||
|
|
||||||
|
boolean retainClassRef() {
|
||||||
|
return (flags & RETAIN_CLASS_REF_BIT) == RETAIN_CLASS_REF_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensureRetainClassRefEnabled() {
|
||||||
|
if (!retainClassRef()) {
|
||||||
|
throw new UnsupportedOperationException("No access to RETAIN_CLASS_REFERENCE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -24,20 +24,20 @@
|
|||||||
*/
|
*/
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import jdk.internal.access.JavaLangInvokeAccess;
|
|
||||||
import jdk.internal.access.SharedSecrets;
|
|
||||||
import jdk.internal.vm.ContinuationScope;
|
import jdk.internal.vm.ContinuationScope;
|
||||||
|
|
||||||
import java.lang.StackWalker.StackFrame;
|
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
class StackFrameInfo implements StackFrame {
|
/**
|
||||||
private static final JavaLangInvokeAccess JLIA =
|
* StackFrameInfo is an implementation of StackFrame that contains the
|
||||||
SharedSecrets.getJavaLangInvokeAccess();
|
* class and method information. This is used by stack walker configured
|
||||||
|
* without StackWalker.Option#DROP_METHOD_INFO.
|
||||||
private final boolean retainClassRef;
|
*/
|
||||||
private Object memberName; // MemberName initialized by VM
|
class StackFrameInfo extends ClassFrameInfo {
|
||||||
private int bci; // initialized by VM to >= 0
|
private String name;
|
||||||
|
private Object type; // String or MethodType
|
||||||
|
private int bci; // set by VM to >= 0
|
||||||
private ContinuationScope contScope;
|
private ContinuationScope contScope;
|
||||||
private volatile StackTraceElement ste;
|
private volatile StackTraceElement ste;
|
||||||
|
|
||||||
@ -49,14 +49,13 @@ class StackFrameInfo implements StackFrame {
|
|||||||
* @see StackStreamFactory.AbstractStackWalker#fetchStackFrames
|
* @see StackStreamFactory.AbstractStackWalker#fetchStackFrames
|
||||||
*/
|
*/
|
||||||
StackFrameInfo(StackWalker walker) {
|
StackFrameInfo(StackWalker walker) {
|
||||||
this.retainClassRef = walker.retainClassRef;
|
super(walker);
|
||||||
this.memberName = JLIA.newMemberName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// package-private called by StackStreamFactory to skip
|
// package-private called by StackStreamFactory to skip
|
||||||
// the capability check
|
// the capability check
|
||||||
Class<?> declaringClass() {
|
Class<?> declaringClass() {
|
||||||
return JLIA.getDeclaringClass(memberName);
|
return JLIA.getDeclaringClass(classOrMemberName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- implementation of StackFrame methods
|
// ----- implementation of StackFrame methods
|
||||||
@ -66,26 +65,43 @@ class StackFrameInfo implements StackFrame {
|
|||||||
return declaringClass().getName();
|
return declaringClass().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getDeclaringClass() {
|
|
||||||
ensureRetainClassRefEnabled();
|
|
||||||
return declaringClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMethodName() {
|
public String getMethodName() {
|
||||||
return JLIA.getName(memberName);
|
if (name == null) {
|
||||||
|
expandStackFrameInfo();
|
||||||
|
assert name != null;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodType getMethodType() {
|
public MethodType getMethodType() {
|
||||||
ensureRetainClassRefEnabled();
|
ensureRetainClassRefEnabled();
|
||||||
return JLIA.getMethodType(memberName);
|
|
||||||
|
if (type == null) {
|
||||||
|
expandStackFrameInfo();
|
||||||
|
assert type != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type instanceof MethodType mt) {
|
||||||
|
return mt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// type is not a MethodType yet. Convert it thread-safely.
|
||||||
|
synchronized (this) {
|
||||||
|
if (type instanceof String sig) {
|
||||||
|
type = JLIA.getMethodType(sig, declaringClass().getClassLoader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (MethodType)type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expand the name and type field of StackFrameInfo
|
||||||
|
private native void expandStackFrameInfo();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
return JLIA.getMethodDescriptor(memberName);
|
return getMethodType().descriptorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -114,7 +130,7 @@ class StackFrameInfo implements StackFrame {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNativeMethod() {
|
public boolean isNativeMethod() {
|
||||||
return JLIA.isNative(memberName);
|
return Modifier.isNative(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getContinuationScopeName() {
|
private String getContinuationScopeName() {
|
||||||
@ -139,10 +155,4 @@ class StackFrameInfo implements StackFrame {
|
|||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureRetainClassRefEnabled() {
|
|
||||||
if (!retainClassRef) {
|
|
||||||
throw new UnsupportedOperationException("No access to RETAIN_CLASS_REFERENCE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,8 +30,10 @@ import java.lang.StackWalker.Option;
|
|||||||
import java.lang.StackWalker.StackFrame;
|
import java.lang.StackWalker.StackFrame;
|
||||||
|
|
||||||
import java.lang.annotation.Native;
|
import java.lang.annotation.Native;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -74,10 +76,10 @@ final class StackStreamFactory {
|
|||||||
|
|
||||||
// These flags must match the values maintained in the VM
|
// These flags must match the values maintained in the VM
|
||||||
@Native private static final int DEFAULT_MODE = 0x0;
|
@Native private static final int DEFAULT_MODE = 0x0;
|
||||||
@Native private static final int FILL_CLASS_REFS_ONLY = 0x2;
|
@Native private static final int CLASS_INFO_ONLY = 0x2;
|
||||||
@Native private static final int GET_CALLER_CLASS = 0x4;
|
|
||||||
@Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM
|
@Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM
|
||||||
@Native private static final int FILL_LIVE_STACK_FRAMES = 0x100;
|
@Native private static final int FILL_LIVE_STACK_FRAMES = 0x100;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For Throwable to use StackWalker, set useNewThrowable to true.
|
* For Throwable to use StackWalker, set useNewThrowable to true.
|
||||||
* Performance work and extensive testing is needed to replace the
|
* Performance work and extensive testing is needed to replace the
|
||||||
@ -89,11 +91,12 @@ final class StackStreamFactory {
|
|||||||
static <T> StackFrameTraverser<T>
|
static <T> StackFrameTraverser<T>
|
||||||
makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
|
makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
|
||||||
{
|
{
|
||||||
if (walker.hasLocalsOperandsOption())
|
if (walker.hasLocalsOperandsOption()) {
|
||||||
return new LiveStackInfoTraverser<>(walker, function);
|
return new LiveStackInfoTraverser<>(walker, function);
|
||||||
else
|
} else {
|
||||||
return new StackFrameTraverser<>(walker, function);
|
return new StackFrameTraverser<>(walker, function);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a stack stream to find caller class.
|
* Gets a stack stream to find caller class.
|
||||||
@ -108,6 +111,17 @@ final class StackStreamFactory {
|
|||||||
CLOSED; // the stream is closed when the stack walking is done
|
CLOSED; // the stream is closed when the stack walking is done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int toStackWalkMode(StackWalker walker, int mode) {
|
||||||
|
int newMode = mode;
|
||||||
|
if (walker.hasOption(Option.DROP_METHOD_INFO))
|
||||||
|
newMode |= CLASS_INFO_ONLY;
|
||||||
|
if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES))
|
||||||
|
newMode |= SHOW_HIDDEN_FRAMES;
|
||||||
|
if (walker.hasLocalsOperandsOption())
|
||||||
|
newMode |= FILL_LIVE_STACK_FRAMES;
|
||||||
|
return newMode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclass of AbstractStackWalker implements a specific stack walking logic.
|
* Subclass of AbstractStackWalker implements a specific stack walking logic.
|
||||||
* It needs to set up the frame buffer and stack walking mode.
|
* It needs to set up the frame buffer and stack walking mode.
|
||||||
@ -124,7 +138,7 @@ final class StackStreamFactory {
|
|||||||
protected final StackWalker walker;
|
protected final StackWalker walker;
|
||||||
protected final Thread thread;
|
protected final Thread thread;
|
||||||
protected final int maxDepth;
|
protected final int maxDepth;
|
||||||
protected final long mode;
|
protected final int mode;
|
||||||
protected int depth; // traversed stack depth
|
protected int depth; // traversed stack depth
|
||||||
protected FrameBuffer<? extends T> frameBuffer;
|
protected FrameBuffer<? extends T> frameBuffer;
|
||||||
protected long anchor;
|
protected long anchor;
|
||||||
@ -137,7 +151,7 @@ final class StackStreamFactory {
|
|||||||
}
|
}
|
||||||
protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) {
|
protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) {
|
||||||
this.thread = Thread.currentThread();
|
this.thread = Thread.currentThread();
|
||||||
this.mode = toStackWalkMode(walker, mode);
|
this.mode = mode;
|
||||||
this.walker = walker;
|
this.walker = walker;
|
||||||
this.maxDepth = maxDepth;
|
this.maxDepth = maxDepth;
|
||||||
this.depth = 0;
|
this.depth = 0;
|
||||||
@ -151,16 +165,6 @@ final class StackStreamFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toStackWalkMode(StackWalker walker, int mode) {
|
|
||||||
int newMode = mode;
|
|
||||||
if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES) &&
|
|
||||||
(mode & FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY)
|
|
||||||
newMode |= SHOW_HIDDEN_FRAMES;
|
|
||||||
if (walker.hasLocalsOperandsOption())
|
|
||||||
newMode |= FILL_LIVE_STACK_FRAMES;
|
|
||||||
return newMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback method to consume the stack frames. This method is invoked
|
* A callback method to consume the stack frames. This method is invoked
|
||||||
* once stack walking begins (i.e. it is only invoked when walkFrames is called).
|
* once stack walking begins (i.e. it is only invoked when walkFrames is called).
|
||||||
@ -450,11 +454,11 @@ final class StackStreamFactory {
|
|||||||
* @param continuation the continuation to walk, or {@code null} if walking a thread.
|
* @param continuation the continuation to walk, or {@code null} if walking a thread.
|
||||||
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
||||||
* @param startIndex start index of the frame buffers to be filled.
|
* @param startIndex start index of the frame buffers to be filled.
|
||||||
* @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
|
* @param frames Either a {@link ClassFrameInfo} array, if mode is {@link #CLASS_INFO_ONLY}
|
||||||
* or a {@link StackFrameInfo} (or derivative) array otherwise.
|
* or a {@link StackFrameInfo} (or derivative) array otherwise.
|
||||||
* @return Result of AbstractStackWalker::doStackWalk
|
* @return Result of AbstractStackWalker::doStackWalk
|
||||||
*/
|
*/
|
||||||
private native R callStackWalk(long mode, int skipframes,
|
private native R callStackWalk(int mode, int skipframes,
|
||||||
ContinuationScope contScope, Continuation continuation,
|
ContinuationScope contScope, Continuation continuation,
|
||||||
int batchSize, int startIndex,
|
int batchSize, int startIndex,
|
||||||
T[] frames);
|
T[] frames);
|
||||||
@ -466,12 +470,12 @@ final class StackStreamFactory {
|
|||||||
* @param anchor
|
* @param anchor
|
||||||
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
||||||
* @param startIndex start index of the frame buffers to be filled.
|
* @param startIndex start index of the frame buffers to be filled.
|
||||||
* @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
|
* @param frames Either a {@link ClassFrameInfo} array, if mode is {@link #CLASS_INFO_ONLY}
|
||||||
* or a {@link StackFrameInfo} (or derivative) array otherwise.
|
* or a {@link StackFrameInfo} (or derivative) array otherwise.
|
||||||
*
|
*
|
||||||
* @return the end index to the frame buffers
|
* @return the end index to the frame buffers
|
||||||
*/
|
*/
|
||||||
private native int fetchStackFrames(long mode, long anchor,
|
private native int fetchStackFrames(int mode, long anchor,
|
||||||
int batchSize, int startIndex,
|
int batchSize, int startIndex,
|
||||||
T[] frames);
|
T[] frames);
|
||||||
|
|
||||||
@ -483,7 +487,7 @@ final class StackStreamFactory {
|
|||||||
*
|
*
|
||||||
* This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
|
* This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
|
||||||
*/
|
*/
|
||||||
static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrameInfo>
|
static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrame>
|
||||||
implements Spliterator<StackFrame>
|
implements Spliterator<StackFrame>
|
||||||
{
|
{
|
||||||
static {
|
static {
|
||||||
@ -491,70 +495,11 @@ final class StackStreamFactory {
|
|||||||
}
|
}
|
||||||
private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
|
private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
|
||||||
|
|
||||||
final class StackFrameBuffer extends FrameBuffer<StackFrameInfo> {
|
|
||||||
private StackFrameInfo[] stackFrames;
|
|
||||||
StackFrameBuffer(int initialBatchSize) {
|
|
||||||
super(initialBatchSize);
|
|
||||||
|
|
||||||
this.stackFrames = new StackFrameInfo[initialBatchSize];
|
|
||||||
for (int i = START_POS; i < initialBatchSize; i++) {
|
|
||||||
stackFrames[i] = new StackFrameInfo(walker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
StackFrameInfo[] frames() {
|
|
||||||
return stackFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void resize(int startIndex, int elements) {
|
|
||||||
if (!isActive())
|
|
||||||
throw new IllegalStateException("inactive frame buffer can't be resized");
|
|
||||||
|
|
||||||
assert startIndex == START_POS :
|
|
||||||
"bad start index " + startIndex + " expected " + START_POS;
|
|
||||||
|
|
||||||
int size = startIndex+elements;
|
|
||||||
if (stackFrames.length < size) {
|
|
||||||
StackFrameInfo[] newFrames = new StackFrameInfo[size];
|
|
||||||
// copy initial magic...
|
|
||||||
System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
|
|
||||||
stackFrames = newFrames;
|
|
||||||
}
|
|
||||||
for (int i = startIndex; i < size; i++) {
|
|
||||||
stackFrames[i] = new StackFrameInfo(walker);
|
|
||||||
}
|
|
||||||
currentBatchSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
StackFrameInfo nextStackFrame() {
|
|
||||||
if (isEmpty()) {
|
|
||||||
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
|
|
||||||
}
|
|
||||||
|
|
||||||
StackFrameInfo frame = stackFrames[origin];
|
|
||||||
origin++;
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
final Class<?> at(int index) {
|
|
||||||
return stackFrames[index].declaringClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Function<? super Stream<StackFrame>, ? extends T> function; // callback
|
final Function<? super Stream<StackFrame>, ? extends T> function; // callback
|
||||||
|
|
||||||
StackFrameTraverser(StackWalker walker,
|
StackFrameTraverser(StackWalker walker,
|
||||||
Function<? super Stream<StackFrame>, ? extends T> function) {
|
Function<? super Stream<StackFrame>, ? extends T> function) {
|
||||||
this(walker, function, DEFAULT_MODE);
|
super(walker, toStackWalkMode(walker, DEFAULT_MODE));
|
||||||
}
|
|
||||||
StackFrameTraverser(StackWalker walker,
|
|
||||||
Function<? super Stream<StackFrame>, ? extends T> function,
|
|
||||||
int mode) {
|
|
||||||
super(walker, mode);
|
|
||||||
this.function = function;
|
this.function = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,7 +512,7 @@ final class StackStreamFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
StackFrameInfo frame = frameBuffer.nextStackFrame();
|
StackFrame frame = frameBuffer.nextStackFrame();
|
||||||
depth++;
|
depth++;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
@ -584,7 +529,9 @@ final class StackStreamFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initFrameBuffer() {
|
protected void initFrameBuffer() {
|
||||||
this.frameBuffer = new StackFrameBuffer(getNextBatchSize());
|
this.frameBuffer = walker.hasOption(Option.DROP_METHOD_INFO)
|
||||||
|
? new ClassFrameBuffer(walker, getNextBatchSize())
|
||||||
|
: new StackFrameBuffer<>(StackFrameInfo.class, walker, getNextBatchSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -651,46 +598,44 @@ final class StackStreamFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static class StackFrameBuffer<T extends ClassFrameInfo> extends FrameBuffer<T> {
|
||||||
* CallerClassFinder is specialized to return Class<?> for each stack frame.
|
final StackWalker walker;
|
||||||
* StackFrame is not requested.
|
private final Class<T> type;
|
||||||
*/
|
private final Constructor<T> ctor;
|
||||||
static final class CallerClassFinder extends AbstractStackWalker<Integer, Class<?>> {
|
private T[] stackFrames;
|
||||||
static {
|
StackFrameBuffer(Class<T> type, StackWalker walker, int initialBatchSize) {
|
||||||
stackWalkImplClasses.add(CallerClassFinder.class);
|
super(initialBatchSize);
|
||||||
|
this.walker = walker;
|
||||||
|
this.type = type;
|
||||||
|
try {
|
||||||
|
this.ctor = type.getDeclaredConstructor(StackWalker.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new InternalError(e);
|
||||||
}
|
}
|
||||||
|
this.stackFrames = fill(allocateArray(initialBatchSize), START_POS, initialBatchSize);
|
||||||
private Class<?> caller;
|
|
||||||
|
|
||||||
CallerClassFinder(StackWalker walker) {
|
|
||||||
super(walker, FILL_CLASS_REFS_ONLY|GET_CALLER_CLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class ClassBuffer extends FrameBuffer<Class<?>> {
|
|
||||||
Class<?>[] classes; // caller class for fast path
|
|
||||||
ClassBuffer(int batchSize) {
|
|
||||||
super(batchSize);
|
|
||||||
classes = new Class<?>[batchSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Class<?>[] frames() { return classes;}
|
T[] frames() {
|
||||||
|
return stackFrames;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@SuppressWarnings("unchecked")
|
||||||
final Class<?> at(int index) { return classes[index];}
|
T[] allocateArray(int size) {
|
||||||
|
return (T[])Array.newInstance(type, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] fill(T[] array, int startIndex, int size) {
|
||||||
|
try {
|
||||||
|
for (int i = startIndex; i < size; i++) {
|
||||||
|
array[i] = ctor.newInstance(walker);
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
// ------ subclass may override the following methods -------
|
|
||||||
/**
|
|
||||||
* Resizes the buffers for VM to fill in the next batch of stack frames.
|
|
||||||
* The next batch will start at the given startIndex with the maximum number
|
|
||||||
* of elements.
|
|
||||||
*
|
|
||||||
* <p> Subclass may override this method to manage the allocated buffers.
|
|
||||||
*
|
|
||||||
* @param startIndex the start index for the first frame of the next batch to fill in.
|
|
||||||
* @param elements the number of elements for the next batch to fill in.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
void resize(int startIndex, int elements) {
|
void resize(int startIndex, int elements) {
|
||||||
if (!isActive())
|
if (!isActive())
|
||||||
@ -700,15 +645,69 @@ final class StackStreamFactory {
|
|||||||
"bad start index " + startIndex + " expected " + START_POS;
|
"bad start index " + startIndex + " expected " + START_POS;
|
||||||
|
|
||||||
int size = startIndex+elements;
|
int size = startIndex+elements;
|
||||||
if (classes.length < size) {
|
if (stackFrames.length < size) {
|
||||||
// copy the elements in classes array to the newly allocated one.
|
T[] newFrames = allocateArray(size);
|
||||||
// classes[0] is a Thread object
|
// copy initial magic...
|
||||||
Class<?>[] prev = classes;
|
System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
|
||||||
classes = new Class<?>[size];
|
stackFrames = newFrames;
|
||||||
System.arraycopy(prev, 0, classes, 0, startIndex);
|
|
||||||
}
|
}
|
||||||
|
fill(stackFrames, startIndex, size);
|
||||||
currentBatchSize = size;
|
currentBatchSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
T nextStackFrame() {
|
||||||
|
if (isEmpty()) {
|
||||||
|
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
T frame = stackFrames[origin];
|
||||||
|
origin++;
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final Class<?> at(int index) {
|
||||||
|
return stackFrames[index].declaringClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Buffer for ClassFrameInfo. It allocates ClassFrameInfo via bytecode
|
||||||
|
* invocation instead of via core reflection to minimize the overhead.
|
||||||
|
*/
|
||||||
|
static class ClassFrameBuffer extends StackFrameBuffer<ClassFrameInfo> {
|
||||||
|
ClassFrameBuffer(StackWalker walker, int initialBatchSize) {
|
||||||
|
super(ClassFrameInfo.class, walker, initialBatchSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ClassFrameInfo[] allocateArray(int size) {
|
||||||
|
return new ClassFrameInfo[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ClassFrameInfo[] fill(ClassFrameInfo[] array, int startIndex, int size) {
|
||||||
|
for (int i = startIndex; i < size; i++) {
|
||||||
|
array[i] = new ClassFrameInfo(walker);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CallerClassFinder is specialized to return Class<?> for each stack frame.
|
||||||
|
* StackFrame is not requested.
|
||||||
|
*/
|
||||||
|
static final class CallerClassFinder extends AbstractStackWalker<Integer, ClassFrameInfo> {
|
||||||
|
static {
|
||||||
|
stackWalkImplClasses.add(CallerClassFinder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> caller;
|
||||||
|
|
||||||
|
CallerClassFinder(StackWalker walker) {
|
||||||
|
super(walker, toStackWalkMode(walker, CLASS_INFO_ONLY));
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> findCaller() {
|
Class<?> findCaller() {
|
||||||
@ -720,25 +719,37 @@ final class StackStreamFactory {
|
|||||||
protected Integer consumeFrames() {
|
protected Integer consumeFrames() {
|
||||||
checkState(OPEN);
|
checkState(OPEN);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
Class<?>[] frames = new Class<?>[2];
|
ClassFrameInfo curFrame = null;
|
||||||
// skip the API calling this getCallerClass method
|
// StackWalker::getCallerClass method
|
||||||
// 0: StackWalker::getCallerClass
|
// 0: caller-sensitive method
|
||||||
// 1: caller-sensitive method
|
// 1: caller class
|
||||||
// 2: caller class
|
ClassFrameInfo[] frames = new ClassFrameInfo[2];
|
||||||
while (n < 2 && (caller = nextFrame()) != null) {
|
while (n < 2 && hasNext() && (curFrame = frameBuffer.nextStackFrame()) != null) {
|
||||||
if (isMethodHandleFrame(caller)) { continue; }
|
caller = curFrame.declaringClass();
|
||||||
if (isReflectionFrame(caller)) { continue; }
|
if (curFrame.isHidden() || isReflectionFrame(caller) || isMethodHandleFrame(caller)) {
|
||||||
frames[n++] = caller;
|
if (isDebug)
|
||||||
|
System.err.println(" skip: frame " + frameBuffer.getIndex() + " " + curFrame);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
frames[n++] = curFrame;
|
||||||
|
}
|
||||||
|
if (isDebug) {
|
||||||
|
System.err.println("0: " + frames[0]);
|
||||||
|
System.err.println("1: " + frames[1]);
|
||||||
}
|
}
|
||||||
if (frames[1] == null) {
|
if (frames[1] == null) {
|
||||||
throw new IllegalCallerException("no caller frame");
|
throw new IllegalCallerException("no caller frame: " + Arrays.toString(frames));
|
||||||
|
}
|
||||||
|
if (frames[0].isCallerSensitive()) {
|
||||||
|
throw new UnsupportedOperationException("StackWalker::getCallerClass called from @CallerSensitive "
|
||||||
|
+ Arrays.toString(frames));
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initFrameBuffer() {
|
protected void initFrameBuffer() {
|
||||||
this.frameBuffer = new ClassBuffer(getNextBatchSize());
|
this.frameBuffer = new ClassFrameBuffer(walker, getNextBatchSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -756,70 +767,15 @@ final class StackStreamFactory {
|
|||||||
static {
|
static {
|
||||||
stackWalkImplClasses.add(LiveStackInfoTraverser.class);
|
stackWalkImplClasses.add(LiveStackInfoTraverser.class);
|
||||||
}
|
}
|
||||||
// VM will fill in all method info and live stack info directly in StackFrameInfo
|
|
||||||
final class LiveStackFrameBuffer extends FrameBuffer<LiveStackFrameInfo> {
|
|
||||||
private LiveStackFrameInfo[] stackFrames;
|
|
||||||
LiveStackFrameBuffer(int initialBatchSize) {
|
|
||||||
super(initialBatchSize);
|
|
||||||
this.stackFrames = new LiveStackFrameInfo[initialBatchSize];
|
|
||||||
for (int i = START_POS; i < initialBatchSize; i++) {
|
|
||||||
stackFrames[i] = new LiveStackFrameInfo(walker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
LiveStackFrameInfo[] frames() {
|
|
||||||
return stackFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void resize(int startIndex, int elements) {
|
|
||||||
if (!isActive()) {
|
|
||||||
throw new IllegalStateException("inactive frame buffer can't be resized");
|
|
||||||
}
|
|
||||||
assert startIndex == START_POS :
|
|
||||||
"bad start index " + startIndex + " expected " + START_POS;
|
|
||||||
|
|
||||||
int size = startIndex + elements;
|
|
||||||
if (stackFrames.length < size) {
|
|
||||||
LiveStackFrameInfo[] newFrames = new LiveStackFrameInfo[size];
|
|
||||||
// copy initial magic...
|
|
||||||
System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
|
|
||||||
stackFrames = newFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = startIndex(); i < size; i++) {
|
|
||||||
stackFrames[i] = new LiveStackFrameInfo(walker);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBatchSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
LiveStackFrameInfo nextStackFrame() {
|
|
||||||
if (isEmpty()) {
|
|
||||||
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
|
|
||||||
}
|
|
||||||
|
|
||||||
LiveStackFrameInfo frame = stackFrames[origin];
|
|
||||||
origin++;
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
final Class<?> at(int index) {
|
|
||||||
return stackFrames[index].declaringClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LiveStackInfoTraverser(StackWalker walker,
|
LiveStackInfoTraverser(StackWalker walker,
|
||||||
Function<? super Stream<StackFrame>, ? extends T> function) {
|
Function<? super Stream<StackFrame>, ? extends T> function) {
|
||||||
super(walker, function, DEFAULT_MODE);
|
super(walker, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initFrameBuffer() {
|
protected void initFrameBuffer() {
|
||||||
this.frameBuffer = new LiveStackFrameBuffer(getNextBatchSize());
|
this.frameBuffer = new StackFrameBuffer<>(LiveStackFrameInfo.class, walker, getNextBatchSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,7 +807,7 @@ final class StackStreamFactory {
|
|||||||
* when walking the stack.
|
* when walking the stack.
|
||||||
*
|
*
|
||||||
* May be an array of {@code Class<?>} if the {@code AbstractStackWalker}
|
* May be an array of {@code Class<?>} if the {@code AbstractStackWalker}
|
||||||
* mode is {@link #FILL_CLASS_REFS_ONLY}, or an array of
|
* mode is {@link #CLASS_INFO_ONLY}, or an array of
|
||||||
* {@link StackFrameInfo} (or derivative) array otherwise.
|
* {@link StackFrameInfo} (or derivative) array otherwise.
|
||||||
*
|
*
|
||||||
* @return An array of frames that may be used to store frame objects
|
* @return An array of frames that may be used to store frame objects
|
||||||
@ -1021,22 +977,20 @@ final class StackStreamFactory {
|
|||||||
|
|
||||||
private static boolean filterStackWalkImpl(Class<?> c) {
|
private static boolean filterStackWalkImpl(Class<?> c) {
|
||||||
return stackWalkImplClasses.contains(c) ||
|
return stackWalkImplClasses.contains(c) ||
|
||||||
c.getName().startsWith("java.util.stream.");
|
c.getPackageName().equals("java.util.stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodHandle frames are not hidden and CallerClassFinder has
|
// MethodHandle frames are not hidden and CallerClassFinder has
|
||||||
// to filter them out
|
// to filter them out
|
||||||
private static boolean isMethodHandleFrame(Class<?> c) {
|
private static boolean isMethodHandleFrame(Class<?> c) {
|
||||||
return c.getName().startsWith("java.lang.invoke.");
|
return c.getPackageName().equals("java.lang.invoke");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isReflectionFrame(Class<?> c) {
|
private static boolean isReflectionFrame(Class<?> c) {
|
||||||
// ## should filter all @Hidden frames?
|
|
||||||
return c == Method.class ||
|
return c == Method.class ||
|
||||||
c == Constructor.class ||
|
c == Constructor.class ||
|
||||||
MethodAccessor.class.isAssignableFrom(c) ||
|
MethodAccessor.class.isAssignableFrom(c) ||
|
||||||
ConstructorAccessor.class.isAssignableFrom(c) ||
|
ConstructorAccessor.class.isAssignableFrom(c);
|
||||||
c.getName().startsWith("java.lang.invoke.LambdaForm");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -49,13 +49,15 @@ import jdk.internal.vm.ContinuationScope;
|
|||||||
* If an attempt is made to reuse the closed stream,
|
* If an attempt is made to reuse the closed stream,
|
||||||
* {@code IllegalStateException} will be thrown.
|
* {@code IllegalStateException} will be thrown.
|
||||||
*
|
*
|
||||||
* <p> The {@linkplain Option <em>stack walking options</em>} of a
|
* <p> {@linkplain Option <em>Stack walker options</em>} configure the stack frame
|
||||||
* {@code StackWalker} determines the information of
|
* information obtained by a {@code StackWalker}.
|
||||||
* {@link StackFrame StackFrame} objects to be returned.
|
* By default, the class name and method information are collected but
|
||||||
* By default, stack frames of the reflection API and implementation
|
* not the {@link StackFrame#getDeclaringClass() Class reference}.
|
||||||
* classes are {@linkplain Option#SHOW_HIDDEN_FRAMES hidden}
|
* The method information can be dropped via the {@link Option#DROP_METHOD_INFO
|
||||||
* and {@code StackFrame}s have the class name and method name
|
* DROP_METHOD_INFO} option. The {@code Class} object can be retained for
|
||||||
* available but not the {@link StackFrame#getDeclaringClass() Class reference}.
|
* access via the {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option.
|
||||||
|
* Stack frames of the reflection API and implementation classes are
|
||||||
|
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden} by default.
|
||||||
*
|
*
|
||||||
* <p> {@code StackWalker} is thread-safe. Multiple threads can share
|
* <p> {@code StackWalker} is thread-safe. Multiple threads can share
|
||||||
* a single {@code StackWalker} object to traverse its own stack.
|
* a single {@code StackWalker} object to traverse its own stack.
|
||||||
@ -66,20 +68,19 @@ import jdk.internal.vm.ContinuationScope;
|
|||||||
* @apiNote
|
* @apiNote
|
||||||
* Examples
|
* Examples
|
||||||
*
|
*
|
||||||
* <p>1. To find the first caller filtering a known list of implementation class:
|
* <p>1. To find the first caller filtering out a known list of implementation class:
|
||||||
* <pre>{@code
|
* {@snippet lang="java" :
|
||||||
* StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
|
* StackWalker walker = StackWalker.getInstance(Set.of(Option.DROP_METHOD_INFO, Option.RETAIN_CLASS_REFERENCE));
|
||||||
* Optional<Class<?>> callerClass = walker.walk(s ->
|
* Optional<Class<?>> callerClass = walker.walk(s ->
|
||||||
* s.map(StackFrame::getDeclaringClass)
|
* s.map(StackFrame::getDeclaringClass)
|
||||||
* .filter(interestingClasses::contains)
|
* .filter(Predicate.not(implClasses::contains))
|
||||||
* .findFirst());
|
* .findFirst());
|
||||||
* }</pre>
|
* }
|
||||||
*
|
*
|
||||||
* <p>2. To snapshot the top 10 stack frames of the current thread,
|
* <p>2. To snapshot the top 10 stack frames of the current thread,
|
||||||
* <pre>{@code
|
* {@snippet lang="java" :
|
||||||
* List<StackFrame> stack = StackWalker.getInstance().walk(s ->
|
* List<StackFrame> stack = StackWalker.getInstance().walk(s -> s.limit(10).toList());
|
||||||
* s.limit(10).collect(Collectors.toList()));
|
* }
|
||||||
* }</pre>
|
|
||||||
*
|
*
|
||||||
* Unless otherwise noted, passing a {@code null} argument to a
|
* Unless otherwise noted, passing a {@code null} argument to a
|
||||||
* constructor or method in this {@code StackWalker} class
|
* constructor or method in this {@code StackWalker} class
|
||||||
@ -93,41 +94,44 @@ public final class StackWalker {
|
|||||||
* A {@code StackFrame} object represents a method invocation returned by
|
* A {@code StackFrame} object represents a method invocation returned by
|
||||||
* {@link StackWalker}.
|
* {@link StackWalker}.
|
||||||
*
|
*
|
||||||
* <p> The {@link #getDeclaringClass()} method may be unsupported as determined
|
* <p> {@linkplain Option <em>Stack walker options</em>} configure the stack
|
||||||
* by the {@linkplain Option stack walking options} of a {@linkplain
|
* frame information obtained by a {@code StackWalker}.
|
||||||
* StackWalker stack walker}.
|
* If the stack walker is configured with {@link Option#DROP_METHOD_INFO
|
||||||
|
* DROP_METHOD_INFO} option, method information such as
|
||||||
|
* the {@linkplain StackFrame#getMethodName() method name},
|
||||||
|
* the {@linkplain StackFrame#getLineNumber() line number},
|
||||||
|
* the {@linkplain StackFrame#getByteCodeIndex() bytecode index}, etc
|
||||||
|
* will be dropped.
|
||||||
|
* If the stack walker is configured with {@link Option#RETAIN_CLASS_REFERENCE
|
||||||
|
* RETAIN_CLASS_REFERENCE} option, the {@link #getDeclaringClass() Class} object
|
||||||
|
* will be retained for access.
|
||||||
*
|
*
|
||||||
* @since 9
|
* @since 9
|
||||||
* @jvms 2.6
|
* @jvms 2.6 Frames
|
||||||
*/
|
*/
|
||||||
public interface StackFrame {
|
public interface StackFrame {
|
||||||
/**
|
/**
|
||||||
* Gets the <a href="ClassLoader.html#binary-name">binary name</a>
|
* {@return the {@linkplain ClassLoader##binary-name binary name}
|
||||||
* of the declaring class of the method represented by this stack frame.
|
* of the declaring class of the method represented by this stack frame}
|
||||||
*
|
|
||||||
* @return the binary name of the declaring class of the method
|
|
||||||
* represented by this stack frame
|
|
||||||
*
|
*
|
||||||
* @jls 13.1 The Form of a Binary
|
* @jls 13.1 The Form of a Binary
|
||||||
*/
|
*/
|
||||||
public String getClassName();
|
public String getClassName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of the method represented by this stack frame.
|
* {@return the name of the method represented by this stack frame}
|
||||||
* @return the name of the method represented by this stack frame
|
*
|
||||||
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
*/
|
*/
|
||||||
public String getMethodName();
|
public String getMethodName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the declaring {@code Class} for the method represented by
|
* {@return the declaring {@code Class} for the method represented by
|
||||||
* this stack frame.
|
* this stack frame}
|
||||||
*
|
*
|
||||||
* @return the declaring {@code Class} of the method represented by
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
* this stack frame
|
* without {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option
|
||||||
*
|
|
||||||
* @throws UnsupportedOperationException if this {@code StackWalker}
|
|
||||||
* is not configured with {@link Option#RETAIN_CLASS_REFERENCE
|
|
||||||
* Option.RETAIN_CLASS_REFERENCE}.
|
|
||||||
*/
|
*/
|
||||||
public Class<?> getDeclaringClass();
|
public Class<?> getDeclaringClass();
|
||||||
|
|
||||||
@ -138,11 +142,11 @@ public final class StackWalker {
|
|||||||
* @implSpec
|
* @implSpec
|
||||||
* The default implementation throws {@code UnsupportedOperationException}.
|
* The default implementation throws {@code UnsupportedOperationException}.
|
||||||
*
|
*
|
||||||
* @return the {@code MethodType} for this stack frame
|
* @return the {@code MethodType} of the method represented by this stack frame
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException if this {@code StackWalker}
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
* is not configured with {@link Option#RETAIN_CLASS_REFERENCE
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option or
|
||||||
* Option.RETAIN_CLASS_REFERENCE}.
|
* without {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option
|
||||||
*
|
*
|
||||||
* @since 10
|
* @since 10
|
||||||
*/
|
*/
|
||||||
@ -161,6 +165,9 @@ public final class StackWalker {
|
|||||||
* @return the descriptor of the method represented by
|
* @return the descriptor of the method represented by
|
||||||
* this stack frame
|
* this stack frame
|
||||||
*
|
*
|
||||||
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
|
*
|
||||||
* @see MethodType#fromMethodDescriptorString(String, ClassLoader)
|
* @see MethodType#fromMethodDescriptorString(String, ClassLoader)
|
||||||
* @see MethodType#toMethodDescriptorString()
|
* @see MethodType#toMethodDescriptorString()
|
||||||
* @jvms 4.3.3 Method Descriptor
|
* @jvms 4.3.3 Method Descriptor
|
||||||
@ -182,6 +189,9 @@ public final class StackWalker {
|
|||||||
* containing the execution point represented by this stack frame,
|
* containing the execution point represented by this stack frame,
|
||||||
* or a negative number if the method is native.
|
* or a negative number if the method is native.
|
||||||
*
|
*
|
||||||
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
|
*
|
||||||
* @jvms 4.7.3 The {@code Code} Attribute
|
* @jvms 4.7.3 The {@code Code} Attribute
|
||||||
*/
|
*/
|
||||||
public int getByteCodeIndex();
|
public int getByteCodeIndex();
|
||||||
@ -198,6 +208,9 @@ public final class StackWalker {
|
|||||||
* represented by this stack frame, or {@code null} if
|
* represented by this stack frame, or {@code null} if
|
||||||
* this information is unavailable.
|
* this information is unavailable.
|
||||||
*
|
*
|
||||||
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
|
*
|
||||||
* @jvms 4.7.10 The {@code SourceFile} Attribute
|
* @jvms 4.7.10 The {@code SourceFile} Attribute
|
||||||
*/
|
*/
|
||||||
public String getFileName();
|
public String getFileName();
|
||||||
@ -213,23 +226,27 @@ public final class StackWalker {
|
|||||||
* point represented by this stack frame, or a negative number if
|
* point represented by this stack frame, or a negative number if
|
||||||
* this information is unavailable.
|
* this information is unavailable.
|
||||||
*
|
*
|
||||||
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
|
*
|
||||||
* @jvms 4.7.12 The {@code LineNumberTable} Attribute
|
* @jvms 4.7.12 The {@code LineNumberTable} Attribute
|
||||||
*/
|
*/
|
||||||
public int getLineNumber();
|
public int getLineNumber();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if the method containing the execution point
|
* {@return {@code true} if the method containing the execution point
|
||||||
* represented by this stack frame is a native method.
|
* represented by this stack frame is a native method}
|
||||||
*
|
*
|
||||||
* @return {@code true} if the method containing the execution point
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
* represented by this stack frame is a native method.
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
*/
|
*/
|
||||||
public boolean isNativeMethod();
|
public boolean isNativeMethod();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@code StackTraceElement} for this stack frame.
|
* {@return {@code StackTraceElement} for this stack frame}
|
||||||
*
|
*
|
||||||
* @return {@code StackTraceElement} for this stack frame.
|
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||||
|
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||||
*/
|
*/
|
||||||
public StackTraceElement toStackTraceElement();
|
public StackTraceElement toStackTraceElement();
|
||||||
}
|
}
|
||||||
@ -250,6 +267,21 @@ public final class StackWalker {
|
|||||||
* {@link StackFrame#getDeclaringClass() StackFrame.getDeclaringClass()}.
|
* {@link StackFrame#getDeclaringClass() StackFrame.getDeclaringClass()}.
|
||||||
*/
|
*/
|
||||||
RETAIN_CLASS_REFERENCE,
|
RETAIN_CLASS_REFERENCE,
|
||||||
|
/**
|
||||||
|
* Drops the method information from {@code StackFrame}s
|
||||||
|
* walked by this {@code StackWalker}.
|
||||||
|
*
|
||||||
|
* <p> A {@code StackWalker} configured with this option will drop
|
||||||
|
* the {@linkplain StackFrame#getMethodName() method name},
|
||||||
|
* the {@linkplain StackFrame#getMethodType() method type},
|
||||||
|
* the {@linkplain StackFrame#getLineNumber() line number},
|
||||||
|
* the {@linkplain StackFrame#getByteCodeIndex() bytecode index},
|
||||||
|
* the {@linkplain StackFrame#getFileName() source file name} and
|
||||||
|
* {@linkplain StackFrame#isNativeMethod() native method or not}.
|
||||||
|
*
|
||||||
|
* @since 22
|
||||||
|
*/
|
||||||
|
DROP_METHOD_INFO,
|
||||||
/**
|
/**
|
||||||
* Shows all reflection frames.
|
* Shows all reflection frames.
|
||||||
*
|
*
|
||||||
@ -319,28 +351,9 @@ public final class StackWalker {
|
|||||||
return DEFAULT_WALKER;
|
return DEFAULT_WALKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@code StackWalker} instance.
|
|
||||||
*
|
|
||||||
* <p> This {@code StackWalker} is configured to skip all
|
|
||||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
|
|
||||||
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
|
||||||
*
|
|
||||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
|
||||||
*
|
|
||||||
* @return a {@code StackWalker} configured to skip all
|
|
||||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
|
|
||||||
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static StackWalker getInstance(ContinuationScope contScope) {
|
|
||||||
return getInstance(EnumSet.noneOf(Option.class), contScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@code StackWalker} instance with the given option specifying
|
* Returns a {@code StackWalker} instance with the given option specifying
|
||||||
* the stack frame information it can access.
|
* the stack frame information it can access.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* If a security manager is present and the given {@code option} is
|
* If a security manager is present and the given {@code option} is
|
||||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
||||||
@ -359,33 +372,13 @@ public final class StackWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@code StackWalker} instance with the given option specifying
|
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||||
* the stack frame information it can access.
|
* the stack frame information it can access.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If a security manager is present and the given {@code option} is
|
* If the given {@code options} is empty, this {@code StackWalker} is
|
||||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
* configured to skip all {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames}
|
||||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
* and no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
|
||||||
*
|
|
||||||
* @param option {@link Option stack walking option}
|
|
||||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
|
||||||
*
|
|
||||||
* @return a {@code StackWalker} configured with the given option
|
|
||||||
*
|
|
||||||
* @throws SecurityException if a security manager exists and its
|
|
||||||
* {@code checkPermission} method denies access.
|
|
||||||
*/
|
|
||||||
static StackWalker getInstance(Option option, ContinuationScope contScope) {
|
|
||||||
return getInstance(EnumSet.of(Objects.requireNonNull(option)), contScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
|
||||||
* the stack frame information it can access. If the given {@code options}
|
|
||||||
* is empty, this {@code StackWalker} is configured to skip all
|
|
||||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
|
||||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If a security manager is present and the given {@code options} contains
|
* If a security manager is present and the given {@code options} contains
|
||||||
@ -393,7 +386,7 @@ public final class StackWalker {
|
|||||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
||||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||||
*
|
*
|
||||||
* @param options {@link Option stack walking option}
|
* @param options {@link Option stack walking options}
|
||||||
*
|
*
|
||||||
* @return a {@code StackWalker} configured with the given options
|
* @return a {@code StackWalker} configured with the given options
|
||||||
*
|
*
|
||||||
@ -401,46 +394,23 @@ public final class StackWalker {
|
|||||||
* {@code checkPermission} method denies access.
|
* {@code checkPermission} method denies access.
|
||||||
*/
|
*/
|
||||||
public static StackWalker getInstance(Set<Option> options) {
|
public static StackWalker getInstance(Set<Option> options) {
|
||||||
return getInstance(options, null);
|
if (options.isEmpty()) {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
|
||||||
* the stack frame information it can access. If the given {@code options}
|
|
||||||
* is empty, this {@code StackWalker} is configured to skip all
|
|
||||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
|
||||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* If a security manager is present and the given {@code options} contains
|
|
||||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
|
||||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
|
||||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
|
||||||
*
|
|
||||||
* @param options {@link Option stack walking option}
|
|
||||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
|
||||||
*
|
|
||||||
* @return a {@code StackWalker} configured with the given options
|
|
||||||
*
|
|
||||||
* @throws SecurityException if a security manager exists and its
|
|
||||||
* {@code checkPermission} method denies access.
|
|
||||||
*/
|
|
||||||
static StackWalker getInstance(Set<Option> options, ContinuationScope contScope) {
|
|
||||||
if (options.isEmpty() && contScope == null) {
|
|
||||||
return DEFAULT_WALKER;
|
return DEFAULT_WALKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnumSet<Option> optionSet = toEnumSet(options);
|
EnumSet<Option> optionSet = toEnumSet(options);
|
||||||
checkPermission(optionSet);
|
checkPermission(optionSet);
|
||||||
return new StackWalker(optionSet, contScope);
|
return new StackWalker(optionSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||||
* the stack frame information it can access. If the given {@code options}
|
* the stack frame information it can access.
|
||||||
* is empty, this {@code StackWalker} is configured to skip all
|
*
|
||||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
* <p>
|
||||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
* If the given {@code options} is empty, this {@code StackWalker} is
|
||||||
|
* configured to skip all {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames}
|
||||||
|
* and no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If a security manager is present and the given {@code options} contains
|
* If a security manager is present and the given {@code options} contains
|
||||||
@ -475,24 +445,9 @@ public final class StackWalker {
|
|||||||
private StackWalker(EnumSet<Option> options) {
|
private StackWalker(EnumSet<Option> options) {
|
||||||
this(options, 0, null, null, null);
|
this(options, 0, null, null, null);
|
||||||
}
|
}
|
||||||
private StackWalker(EnumSet<Option> options, ContinuationScope contScope) {
|
|
||||||
this(options, 0, null, contScope, null);
|
|
||||||
}
|
|
||||||
private StackWalker(EnumSet<Option> options, ContinuationScope contScope, Continuation continuation) {
|
|
||||||
this(options, 0, null, contScope, continuation);
|
|
||||||
}
|
|
||||||
private StackWalker(EnumSet<Option> options, int estimateDepth) {
|
private StackWalker(EnumSet<Option> options, int estimateDepth) {
|
||||||
this(options, estimateDepth, null, null, null);
|
this(options, estimateDepth, null, null, null);
|
||||||
}
|
}
|
||||||
private StackWalker(EnumSet<Option> options, int estimateDepth, ContinuationScope contScope) {
|
|
||||||
this(options, estimateDepth, null, contScope, null);
|
|
||||||
}
|
|
||||||
private StackWalker(EnumSet<Option> options,
|
|
||||||
int estimateDepth,
|
|
||||||
ContinuationScope contScope,
|
|
||||||
Continuation continuation) {
|
|
||||||
this(options, estimateDepth, null, contScope, continuation);
|
|
||||||
}
|
|
||||||
private StackWalker(EnumSet<Option> options,
|
private StackWalker(EnumSet<Option> options,
|
||||||
int estimateDepth,
|
int estimateDepth,
|
||||||
ExtendedOption extendedOption,
|
ExtendedOption extendedOption,
|
||||||
@ -541,13 +496,12 @@ public final class StackWalker {
|
|||||||
* @apiNote
|
* @apiNote
|
||||||
* For example, to find the first 10 calling frames, first skipping those frames
|
* For example, to find the first 10 calling frames, first skipping those frames
|
||||||
* whose declaring class is in package {@code com.foo}:
|
* whose declaring class is in package {@code com.foo}:
|
||||||
* <blockquote>
|
* {@snippet lang="java" :
|
||||||
* <pre>{@code
|
|
||||||
* List<StackFrame> frames = StackWalker.getInstance().walk(s ->
|
* List<StackFrame> frames = StackWalker.getInstance().walk(s ->
|
||||||
* s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
|
* s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
|
||||||
* .limit(10)
|
* .limit(10)
|
||||||
* .collect(Collectors.toList()));
|
* .toList());
|
||||||
* }</pre></blockquote>
|
* }
|
||||||
*
|
*
|
||||||
* <p>This method takes a {@code Function} accepting a {@code Stream<StackFrame>},
|
* <p>This method takes a {@code Function} accepting a {@code Stream<StackFrame>},
|
||||||
* rather than returning a {@code Stream<StackFrame>} and allowing the
|
* rather than returning a {@code Stream<StackFrame>} and allowing the
|
||||||
@ -638,9 +592,10 @@ public final class StackWalker {
|
|||||||
* the class loader to load the resource bundle. The caller class
|
* the class loader to load the resource bundle. The caller class
|
||||||
* in this example is {@code MyTool}.
|
* in this example is {@code MyTool}.
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* {@snippet lang="java" :
|
||||||
* class Util {
|
* class Util {
|
||||||
* private final StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
|
* private final StackWalker walker =
|
||||||
|
* StackWalker.getInstance(Set.of(Option.DROP_METHOD_INFO, Option.RETAIN_CLASS_REFERENCE));
|
||||||
* public ResourceBundle getResourceBundle(String bundleName) {
|
* public ResourceBundle getResourceBundle(String bundleName) {
|
||||||
* Class<?> caller = walker.getCallerClass();
|
* Class<?> caller = walker.getCallerClass();
|
||||||
* return ResourceBundle.getBundle(bundleName, Locale.getDefault(), caller.getClassLoader());
|
* return ResourceBundle.getBundle(bundleName, Locale.getDefault(), caller.getClassLoader());
|
||||||
@ -653,18 +608,18 @@ public final class StackWalker {
|
|||||||
* ResourceBundle rb = util.getResourceBundle("mybundle");
|
* ResourceBundle rb = util.getResourceBundle("mybundle");
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* }</pre>
|
* }
|
||||||
*
|
*
|
||||||
* An equivalent way to find the caller class using the
|
* An equivalent way to find the caller class using the
|
||||||
* {@link StackWalker#walk walk} method is as follows
|
* {@link StackWalker#walk walk} method is as follows
|
||||||
* (filtering the reflection frames, {@code MethodHandle} and hidden frames
|
* (filtering the reflection frames, {@code MethodHandle} and hidden frames
|
||||||
* not shown below):
|
* not shown below):
|
||||||
* <pre>{@code
|
* {@snippet lang="java" :
|
||||||
* Optional<Class<?>> caller = walker.walk(s ->
|
* Optional<Class<?>> caller = walker.walk(s ->
|
||||||
* s.map(StackFrame::getDeclaringClass)
|
* s.map(StackFrame::getDeclaringClass)
|
||||||
* .skip(2)
|
* .skip(2)
|
||||||
* .findFirst());
|
* .findFirst());
|
||||||
* }</pre>
|
* }
|
||||||
*
|
*
|
||||||
* When the {@code getCallerClass} method is called from a method that
|
* When the {@code getCallerClass} method is called from a method that
|
||||||
* is the bottom most frame on the stack,
|
* is the bottom most frame on the stack,
|
||||||
@ -700,10 +655,12 @@ public final class StackWalker {
|
|||||||
return newInstance(options, extendedOption, null);
|
return newInstance(options, extendedOption, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static StackWalker newInstance(Set<Option> options, ContinuationScope contScope) {
|
||||||
|
return newInstance(options, null, contScope);
|
||||||
|
}
|
||||||
|
|
||||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope) {
|
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope) {
|
||||||
EnumSet<Option> optionSet = toEnumSet(options);
|
return newInstance(options, extendedOption, contScope, null);
|
||||||
checkPermission(optionSet);
|
|
||||||
return new StackWalker(optionSet, 0, extendedOption, contScope, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope, Continuation continuation) {
|
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope, Continuation continuation) {
|
||||||
|
@ -65,11 +65,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
|||||||
* and those seven fields omit much of the information in Method.
|
* and those seven fields omit much of the information in Method.
|
||||||
* @author jrose
|
* @author jrose
|
||||||
*/
|
*/
|
||||||
/*non-public*/
|
|
||||||
final class ResolvedMethodName {
|
|
||||||
//@Injected JVM_Method* vmtarget;
|
|
||||||
//@Injected Class<?> vmholder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*non-public*/
|
/*non-public*/
|
||||||
final class MemberName implements Member, Cloneable {
|
final class MemberName implements Member, Cloneable {
|
||||||
|
@ -57,6 +57,8 @@ import java.util.function.Function;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.lang.invoke.LambdaForm.*;
|
import static java.lang.invoke.LambdaForm.*;
|
||||||
|
import static java.lang.invoke.MethodHandleNatives.Constants.MN_CALLER_SENSITIVE;
|
||||||
|
import static java.lang.invoke.MethodHandleNatives.Constants.MN_HIDDEN_MEMBER;
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||||
@ -1543,37 +1545,22 @@ abstract class MethodHandleImpl {
|
|||||||
static {
|
static {
|
||||||
SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
|
SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
|
||||||
@Override
|
@Override
|
||||||
public Object newMemberName() {
|
public Class<?> getDeclaringClass(Object rmname) {
|
||||||
return new MemberName();
|
ResolvedMethodName method = (ResolvedMethodName)rmname;
|
||||||
|
return method.declaringClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName(Object mname) {
|
public MethodType getMethodType(String descriptor, ClassLoader loader) {
|
||||||
MemberName memberName = (MemberName)mname;
|
return MethodType.fromDescriptor(descriptor, loader);
|
||||||
return memberName.getName();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public Class<?> getDeclaringClass(Object mname) {
|
|
||||||
MemberName memberName = (MemberName)mname;
|
|
||||||
return memberName.getDeclaringClass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isCallerSensitive(int flags) {
|
||||||
public MethodType getMethodType(Object mname) {
|
return (flags & MN_CALLER_SENSITIVE) == MN_CALLER_SENSITIVE;
|
||||||
MemberName memberName = (MemberName)mname;
|
|
||||||
return memberName.getMethodType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isHiddenMember(int flags) {
|
||||||
public String getMethodDescriptor(Object mname) {
|
return (flags & MN_HIDDEN_MEMBER) == MN_HIDDEN_MEMBER;
|
||||||
MemberName memberName = (MemberName)mname;
|
|
||||||
return memberName.getMethodDescriptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isNative(Object mname) {
|
|
||||||
MemberName memberName = (MemberName)mname;
|
|
||||||
return memberName.isNative();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,6 +116,7 @@ class MethodHandleNatives {
|
|||||||
MN_IS_TYPE = 0x00080000, // nested type
|
MN_IS_TYPE = 0x00080000, // nested type
|
||||||
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
||||||
MN_TRUSTED_FINAL = 0x00200000, // trusted final field
|
MN_TRUSTED_FINAL = 0x00200000, // trusted final field
|
||||||
|
MN_HIDDEN_MEMBER = 0x00400000, // members defined in a hidden class or with @Hidden
|
||||||
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
||||||
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT;
|
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT;
|
||||||
|
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package java.lang.invoke;
|
||||||
|
|
||||||
|
final class ResolvedMethodName {
|
||||||
|
//@Injected JVM_Method* vmtarget;
|
||||||
|
private Class<?> vmholder;
|
||||||
|
|
||||||
|
Class<?> declaringClass() {
|
||||||
|
return vmholder;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -72,7 +72,7 @@ final class JceSecurityManager {
|
|||||||
INSTANCE = dummySecurityManager;
|
INSTANCE = dummySecurityManager;
|
||||||
|
|
||||||
PrivilegedAction<StackWalker> paWalker =
|
PrivilegedAction<StackWalker> paWalker =
|
||||||
() -> StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
|
() -> StackWalker.getInstance(Set.of(Option.DROP_METHOD_INFO, Option.RETAIN_CLASS_REFERENCE));
|
||||||
@SuppressWarnings("removal")
|
@SuppressWarnings("removal")
|
||||||
StackWalker dummyWalker = AccessController.doPrivileged(paWalker);
|
StackWalker dummyWalker = AccessController.doPrivileged(paWalker);
|
||||||
|
|
||||||
|
@ -40,38 +40,27 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
public interface JavaLangInvokeAccess {
|
public interface JavaLangInvokeAccess {
|
||||||
/**
|
/**
|
||||||
* Create a new MemberName instance. Used by {@code StackFrameInfo}.
|
* Returns the declaring class for the given ResolvedMethodName.
|
||||||
*/
|
|
||||||
Object newMemberName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name for the given MemberName. Used by {@code StackFrameInfo}.
|
|
||||||
*/
|
|
||||||
String getName(Object mname);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@code MethodType} for the given MemberName.
|
|
||||||
* Used by {@code StackFrameInfo}.
|
* Used by {@code StackFrameInfo}.
|
||||||
*/
|
*/
|
||||||
MethodType getMethodType(Object mname);
|
Class<?> getDeclaringClass(Object rmname);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the descriptor for the given MemberName.
|
* Returns the {@code MethodType} for the given method descriptor
|
||||||
|
* and class loader.
|
||||||
* Used by {@code StackFrameInfo}.
|
* Used by {@code StackFrameInfo}.
|
||||||
*/
|
*/
|
||||||
String getMethodDescriptor(Object mname);
|
MethodType getMethodType(String descriptor, ClassLoader loader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if the given MemberName is a native method.
|
* Returns true if the given flags has MN_CALLER_SENSITIVE flag set.
|
||||||
* Used by {@code StackFrameInfo}.
|
|
||||||
*/
|
*/
|
||||||
boolean isNative(Object mname);
|
boolean isCallerSensitive(int flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the declaring class for the given MemberName.
|
* Returns true if the given flags has MN_HIDDEN_MEMBER flag set.
|
||||||
* Used by {@code StackFrameInfo}.
|
|
||||||
*/
|
*/
|
||||||
Class<?> getDeclaringClass(Object mname);
|
boolean isHiddenMember(int flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a map of class name in internal forms to its corresponding
|
* Returns a map of class name in internal forms to its corresponding
|
||||||
|
37
src/java.base/share/native/libjava/StackFrameInfo.c
Normal file
37
src/java.base/share/native/libjava/StackFrameInfo.c
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "jvm.h"
|
||||||
|
|
||||||
|
#include "java_lang_StackFrameInfo.h"
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_java_lang_StackFrameInfo_expandStackFrameInfo
|
||||||
|
(JNIEnv *env, jobject obj) {
|
||||||
|
JVM_ExpandStackFrameInfo(env, obj);
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -44,7 +44,7 @@
|
|||||||
JNIEXPORT jboolean JNICALL Java_java_lang_StackStreamFactory_checkStackWalkModes
|
JNIEXPORT jboolean JNICALL Java_java_lang_StackStreamFactory_checkStackWalkModes
|
||||||
(JNIEnv *env, jclass dummy)
|
(JNIEnv *env, jclass dummy)
|
||||||
{
|
{
|
||||||
return JVM_STACKWALK_FILL_CLASS_REFS_ONLY == java_lang_StackStreamFactory_FILL_CLASS_REFS_ONLY &&
|
return JVM_STACKWALK_CLASS_INFO_ONLY == java_lang_StackStreamFactory_CLASS_INFO_ONLY &&
|
||||||
JVM_STACKWALK_SHOW_HIDDEN_FRAMES == java_lang_StackStreamFactory_SHOW_HIDDEN_FRAMES &&
|
JVM_STACKWALK_SHOW_HIDDEN_FRAMES == java_lang_StackStreamFactory_SHOW_HIDDEN_FRAMES &&
|
||||||
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES == java_lang_StackStreamFactory_FILL_LIVE_STACK_FRAMES;
|
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES == java_lang_StackStreamFactory_FILL_LIVE_STACK_FRAMES;
|
||||||
}
|
}
|
||||||
@ -52,10 +52,10 @@ JNIEXPORT jboolean JNICALL Java_java_lang_StackStreamFactory_checkStackWalkModes
|
|||||||
/*
|
/*
|
||||||
* Class: java_lang_StackStreamFactory_AbstractStackWalker
|
* Class: java_lang_StackStreamFactory_AbstractStackWalker
|
||||||
* Method: callStackWalk
|
* Method: callStackWalk
|
||||||
* Signature: (JILjdk/internal/vm/ContinuationScope;Ljdk/internal/vm/Continuation;II[Ljava/lang/Object;)Ljava/lang/Object;
|
* Signature: (IILjdk/internal/vm/ContinuationScope;Ljdk/internal/vm/Continuation;II[Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jobject JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_callStackWalk
|
JNIEXPORT jobject JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_callStackWalk
|
||||||
(JNIEnv *env, jobject stackstream, jlong mode, jint skipFrames, jobject contScope, jobject cont,
|
(JNIEnv *env, jobject stackstream, jint mode, jint skipFrames, jobject contScope, jobject cont,
|
||||||
jint batchSize, jint startIndex, jobjectArray frames)
|
jint batchSize, jint startIndex, jobjectArray frames)
|
||||||
{
|
{
|
||||||
return JVM_CallStackWalk(env, stackstream, mode, skipFrames, contScope, cont,
|
return JVM_CallStackWalk(env, stackstream, mode, skipFrames, contScope, cont,
|
||||||
@ -65,10 +65,10 @@ JNIEXPORT jobject JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWa
|
|||||||
/*
|
/*
|
||||||
* Class: java_lang_StackStreamFactory_AbstractStackWalker
|
* Class: java_lang_StackStreamFactory_AbstractStackWalker
|
||||||
* Method: fetchStackFrames
|
* Method: fetchStackFrames
|
||||||
* Signature: (JJII[Ljava/lang/Object;)I
|
* Signature: (IJII[Ljava/lang/Object;)I
|
||||||
*/
|
*/
|
||||||
JNIEXPORT jint JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_fetchStackFrames
|
JNIEXPORT jint JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_fetchStackFrames
|
||||||
(JNIEnv *env, jobject stackstream, jlong mode, jlong anchor,
|
(JNIEnv *env, jobject stackstream, jint mode, jlong anchor,
|
||||||
jint batchSize, jint startIndex,
|
jint batchSize, jint startIndex,
|
||||||
jobjectArray frames)
|
jobjectArray frames)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,7 +35,7 @@ public class LocalLongHelper {
|
|||||||
static Class<?> primitiveValueClass;
|
static Class<?> primitiveValueClass;
|
||||||
static Method primitiveSize;
|
static Method primitiveSize;
|
||||||
static Method getMethodType;
|
static Method getMethodType;
|
||||||
static Field memberName;
|
static Field classOrMemberName;
|
||||||
static Field offset;
|
static Field offset;
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
@ -67,16 +67,17 @@ public class LocalLongHelper {
|
|||||||
longValue = primitiveValueClass.getDeclaredMethod("longValue");
|
longValue = primitiveValueClass.getDeclaredMethod("longValue");
|
||||||
longValue.setAccessible(true);
|
longValue.setAccessible(true);
|
||||||
|
|
||||||
|
Class<?> classFrameInfoClass = Class.forName("java.lang.ClassFrameInfo");
|
||||||
|
classOrMemberName = classFrameInfoClass.getDeclaredField("classOrMemberName");
|
||||||
|
classOrMemberName.setAccessible(true);
|
||||||
Class<?> stackFrameInfoClass = Class.forName("java.lang.StackFrameInfo");
|
Class<?> stackFrameInfoClass = Class.forName("java.lang.StackFrameInfo");
|
||||||
memberName = stackFrameInfoClass.getDeclaredField("memberName");
|
|
||||||
memberName.setAccessible(true);
|
|
||||||
offset = stackFrameInfoClass.getDeclaredField("bci");
|
offset = stackFrameInfoClass.getDeclaredField("bci");
|
||||||
offset.setAccessible(true);
|
offset.setAccessible(true);
|
||||||
getMethodType = Class.forName("java.lang.invoke.MemberName").getDeclaredMethod("getMethodType");
|
getMethodType = Class.forName("java.lang.invoke.MemberName").getDeclaredMethod("getMethodType");
|
||||||
getMethodType.setAccessible(true);
|
getMethodType.setAccessible(true);
|
||||||
|
|
||||||
Class<?> extendedOptionClass = Class.forName("java.lang.StackWalker$ExtendedOption");
|
Class<?> extendedOptionClass = Class.forName("java.lang.StackWalker$ExtendedOption");
|
||||||
Method ewsNI = StackWalker.class.getDeclaredMethod("newInstance", Set.class, extendedOptionClass, jdk.internal.vm.ContinuationScope.class);
|
Method ewsNI = StackWalker.class.getDeclaredMethod("newInstance", Set.class, extendedOptionClass);
|
||||||
ewsNI.setAccessible(true);
|
ewsNI.setAccessible(true);
|
||||||
Field f = extendedOptionClass.getDeclaredField("LOCALS_AND_OPERANDS");
|
Field f = extendedOptionClass.getDeclaredField("LOCALS_AND_OPERANDS");
|
||||||
f.setAccessible(true);
|
f.setAccessible(true);
|
||||||
@ -84,6 +85,6 @@ public class LocalLongHelper {
|
|||||||
|
|
||||||
primitiveSize = primitiveValueClass.getDeclaredMethod("size");
|
primitiveSize = primitiveValueClass.getDeclaredMethod("size");
|
||||||
primitiveSize.setAccessible(true);
|
primitiveSize.setAccessible(true);
|
||||||
sw = (StackWalker) ewsNI.invoke(null, java.util.Collections.emptySet(), localsAndOperandsOption, null);
|
sw = (StackWalker) ewsNI.invoke(null, java.util.Collections.emptySet(), localsAndOperandsOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,12 +23,13 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8157464
|
* @bug 8157464 8210375
|
||||||
* @summary Basic test for StackWalker.getCallerClass()
|
* @summary Basic test for StackWalker.getCallerClass()
|
||||||
* @library src
|
* @library src
|
||||||
* @modules java.base/jdk.internal.reflect
|
* @modules java.base/jdk.internal.reflect
|
||||||
* @build java.base/java.util.CSM csm/*
|
* @build java.base/java.util.CSM csm/*
|
||||||
* @run main/othervm csm/jdk.test.CallerSensitiveTest
|
* @run main/othervm csm/jdk.test.CallerSensitiveTest
|
||||||
|
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames csm/jdk.test.CallerSensitiveTest
|
||||||
* @run main/othervm -Djava.security.manager=allow csm/jdk.test.CallerSensitiveTest sm
|
* @run main/othervm -Djava.security.manager=allow csm/jdk.test.CallerSensitiveTest sm
|
||||||
*/
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -44,6 +44,7 @@ import java.util.function.Supplier;
|
|||||||
public class CallerSensitiveTest {
|
public class CallerSensitiveTest {
|
||||||
static final Policy DEFAULT_POLICY = Policy.getPolicy();
|
static final Policy DEFAULT_POLICY = Policy.getPolicy();
|
||||||
private static final String NON_CSM_CALLER_METHOD = "getCallerClass";
|
private static final String NON_CSM_CALLER_METHOD = "getCallerClass";
|
||||||
|
private static final String REFLECTIVE_GET_CALLER_METHOD = "getCallerClassReflectively";
|
||||||
private static final String CSM_CALLER_METHOD = "caller";
|
private static final String CSM_CALLER_METHOD = "caller";
|
||||||
|
|
||||||
public static void main(String... args) throws Throwable {
|
public static void main(String... args) throws Throwable {
|
||||||
@ -68,6 +69,8 @@ public class CallerSensitiveTest {
|
|||||||
CallerSensitiveTest cstest = new CallerSensitiveTest();
|
CallerSensitiveTest cstest = new CallerSensitiveTest();
|
||||||
// test static call to java.util.CSM::caller and CSM::getCallerClass
|
// test static call to java.util.CSM::caller and CSM::getCallerClass
|
||||||
cstest.staticMethodCall();
|
cstest.staticMethodCall();
|
||||||
|
// test reflective call to StackWalker::getCallerClass
|
||||||
|
cstest.invokeMethod();
|
||||||
// test java.lang.reflect.Method call
|
// test java.lang.reflect.Method call
|
||||||
cstest.reflectMethodCall();
|
cstest.reflectMethodCall();
|
||||||
// test java.lang.invoke.MethodHandle
|
// test java.lang.invoke.MethodHandle
|
||||||
@ -95,7 +98,16 @@ public class CallerSensitiveTest {
|
|||||||
method1.invoke(null);
|
method1.invoke(null);
|
||||||
|
|
||||||
Method method2 = java.util.CSM.class.getMethod(NON_CSM_CALLER_METHOD);
|
Method method2 = java.util.CSM.class.getMethod(NON_CSM_CALLER_METHOD);
|
||||||
Result result = (Result) method2.invoke(null);
|
Result result2 = (Result) method2.invoke(null);
|
||||||
|
checkNonCSMCaller(CallerSensitiveTest.class, result2);
|
||||||
|
|
||||||
|
Method method3 = java.util.CSM.class.getMethod(REFLECTIVE_GET_CALLER_METHOD);
|
||||||
|
Result result3 = (Result) method3.invoke(null);
|
||||||
|
checkNonCSMCaller(CallerSensitiveTest.class, result3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invokeMethod() throws Throwable {
|
||||||
|
Result result = java.util.CSM.getCallerClassReflectively();
|
||||||
checkNonCSMCaller(CallerSensitiveTest.class, result);
|
checkNonCSMCaller(CallerSensitiveTest.class, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +118,13 @@ public class CallerSensitiveTest {
|
|||||||
|
|
||||||
MethodHandle mh2 = lookup.findStatic(java.util.CSM.class, NON_CSM_CALLER_METHOD,
|
MethodHandle mh2 = lookup.findStatic(java.util.CSM.class, NON_CSM_CALLER_METHOD,
|
||||||
MethodType.methodType(Result.class));
|
MethodType.methodType(Result.class));
|
||||||
Result result = (Result)mh2.invokeExact();
|
Result result2 = (Result)mh2.invokeExact();
|
||||||
checkNonCSMCaller(CallerSensitiveTest.class, result);
|
checkNonCSMCaller(CallerSensitiveTest.class, result2);
|
||||||
|
|
||||||
|
MethodHandle mh3 = lookup.findStatic(java.util.CSM.class, REFLECTIVE_GET_CALLER_METHOD,
|
||||||
|
MethodType.methodType(Result.class));
|
||||||
|
Result result3 = (Result)mh3.invokeExact();
|
||||||
|
checkNonCSMCaller(CallerSensitiveTest.class, result3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lambda() {
|
void lambda() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,6 +25,7 @@ package java.util;
|
|||||||
|
|
||||||
import static java.lang.StackWalker.Option.*;
|
import static java.lang.StackWalker.Option.*;
|
||||||
import java.lang.StackWalker.StackFrame;
|
import java.lang.StackWalker.StackFrame;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jdk.internal.reflect.CallerSensitive;
|
import jdk.internal.reflect.CallerSensitive;
|
||||||
@ -33,6 +34,7 @@ import jdk.internal.reflect.Reflection;
|
|||||||
public class CSM {
|
public class CSM {
|
||||||
private static StackWalker walker =
|
private static StackWalker walker =
|
||||||
StackWalker.getInstance(EnumSet.of(RETAIN_CLASS_REFERENCE,
|
StackWalker.getInstance(EnumSet.of(RETAIN_CLASS_REFERENCE,
|
||||||
|
DROP_METHOD_INFO,
|
||||||
SHOW_HIDDEN_FRAMES,
|
SHOW_HIDDEN_FRAMES,
|
||||||
SHOW_REFLECT_FRAMES));
|
SHOW_REFLECT_FRAMES));
|
||||||
|
|
||||||
@ -58,19 +60,40 @@ public class CSM {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> c2 = walker.getCallerClass();
|
Class<?> c2 = walker.getCallerClass();
|
||||||
throw new RuntimeException("Exception not thrown by StackWalker::getCallerClass");
|
throw new RuntimeException("Exception not thrown by StackWalker::getCallerClass. Returned " + c2.getName());
|
||||||
} catch (UnsupportedOperationException e) {}
|
} catch (UnsupportedOperationException e) {}
|
||||||
return c1;
|
return c1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the caller of this non-caller-sensitive method.
|
* Returns the caller of this non-caller-sensitive method returned
|
||||||
|
* by StackWalker::getCallerClass
|
||||||
*/
|
*/
|
||||||
public static Result getCallerClass() {
|
public static Result getCallerClass() {
|
||||||
Class<?> caller = walker.getCallerClass();
|
Class<?> caller = walker.getCallerClass();
|
||||||
return new Result(List.of(caller), dump());
|
return new Result(List.of(caller), dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Method GET_CALLER_CLASS;
|
||||||
|
static {
|
||||||
|
Method m = null;
|
||||||
|
try {
|
||||||
|
m = StackWalker.class.getMethod("getCallerClass");
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
GET_CALLER_CLASS = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the caller of this non-caller-sensitive method returned
|
||||||
|
* by StackWalker::getCallerClass invoked via core reflection
|
||||||
|
*/
|
||||||
|
public static Result getCallerClassReflectively() throws ReflectiveOperationException {
|
||||||
|
Class<?> caller = (Class<?>)GET_CALLER_CLASS.invoke(walker);
|
||||||
|
return new Result(List.of(caller), dump());
|
||||||
|
}
|
||||||
|
|
||||||
static List<StackFrame> dump() {
|
static List<StackFrame> dump() {
|
||||||
return walker.walk(s -> s.collect(Collectors.toList()));
|
return walker.walk(s -> s.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,9 +23,10 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8140450 8152893 8189291
|
* @bug 8140450 8152893 8189291 8210375
|
||||||
* @summary Basic test for StackWalker.getCallerClass()
|
* @summary Basic test for StackWalker.getCallerClass()
|
||||||
* @run main/othervm GetCallerClassTest
|
* @run main/othervm GetCallerClassTest
|
||||||
|
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames GetCallerClassTest
|
||||||
* @run main/othervm -Djava.security.manager=allow GetCallerClassTest sm
|
* @run main/othervm -Djava.security.manager=allow GetCallerClassTest sm
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -118,8 +119,10 @@ public class GetCallerClassTest {
|
|||||||
Class<?> expected,
|
Class<?> expected,
|
||||||
boolean expectUOE) {
|
boolean expectUOE) {
|
||||||
try {
|
try {
|
||||||
|
// Use reflection to call Method::invoke that invokes StackWalker::getCallerClass
|
||||||
Method m = StackWalker.class.getMethod("getCallerClass");
|
Method m = StackWalker.class.getMethod("getCallerClass");
|
||||||
Class<?> c = (Class<?>) m.invoke(stackWalker);
|
Method invoke = Method.class.getMethod("invoke", Object.class, Object[].class);
|
||||||
|
Class<?> c = (Class<?>) invoke.invoke(m, new Object[] { stackWalker, null });
|
||||||
assertEquals(c, expected);
|
assertEquals(c, expected);
|
||||||
if (expectUOE) { // Should have thrown
|
if (expectUOE) { // Should have thrown
|
||||||
throw new RuntimeException("Didn't get expected exception");
|
throw new RuntimeException("Didn't get expected exception");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,80 +23,93 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 8140450
|
* @bug 8140450 8268829
|
||||||
* @summary Sanity test for exception cases
|
* @summary Sanity test for exception cases
|
||||||
* @run testng SanityTest
|
* @run junit SanityTest
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import static java.lang.StackWalker.Option.*;
|
||||||
|
|
||||||
import org.testng.annotations.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class SanityTest {
|
public class SanityTest {
|
||||||
@Test
|
@Test
|
||||||
public static void testNPE() {
|
public void testNPE() {
|
||||||
try {
|
assertThrows(NullPointerException.class, () ->
|
||||||
StackWalker sw = StackWalker.getInstance((Set<StackWalker.Option>) null);
|
StackWalker.getInstance((Set<StackWalker.Option>) null));
|
||||||
throw new RuntimeException("NPE expected");
|
assertThrows(NullPointerException.class, () ->
|
||||||
} catch (NullPointerException e) {}
|
StackWalker.getInstance((StackWalker.Option) null));
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
private static Stream<StackWalker> noRetainClassRef() {
|
||||||
StackWalker sw = StackWalker.getInstance((StackWalker.Option) null);
|
return Stream.of(StackWalker.getInstance(), StackWalker.getInstance(DROP_METHOD_INFO));
|
||||||
throw new RuntimeException("NPE expected");
|
}
|
||||||
} catch (NullPointerException e) {}
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("noRetainClassRef")
|
||||||
|
public void testUOE(StackWalker sw) {
|
||||||
|
assertThrows(UnsupportedOperationException.class, () -> sw.getCallerClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public static void testUOE() {
|
public void testInvalidEstimateDepth() {
|
||||||
try {
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
StackWalker.getInstance().getCallerClass();
|
StackWalker.getInstance(Collections.emptySet(), 0));
|
||||||
throw new RuntimeException("UOE expected");
|
|
||||||
} catch (UnsupportedOperationException expected) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public static void testInvalidEstimateDepth() {
|
public void testNullFunction() {
|
||||||
try {
|
assertThrows(NullPointerException.class, () ->
|
||||||
StackWalker sw = StackWalker.getInstance(Collections.emptySet(), 0);
|
StackWalker.getInstance().walk(null));
|
||||||
throw new RuntimeException("Illegal estimateDepth should throw IAE");
|
|
||||||
} catch (IllegalArgumentException e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public static void testNullFuncation() {
|
public void testNullConsumer() {
|
||||||
try {
|
assertThrows(NullPointerException.class, () ->
|
||||||
StackWalker.getInstance().walk(null);
|
StackWalker.getInstance().forEach(null));
|
||||||
throw new RuntimeException("NPE expected");
|
|
||||||
} catch (NullPointerException e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public static void testNullConsumer() {
|
@MethodSource("noRetainClassRef")
|
||||||
try {
|
public void testUOEFromGetDeclaringClass(StackWalker sw) {
|
||||||
StackWalker.getInstance().forEach(null);
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
throw new RuntimeException("NPE expected");
|
sw.forEach(StackWalker.StackFrame::getDeclaringClass));
|
||||||
} catch (NullPointerException e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
@Test
|
@MethodSource("noRetainClassRef")
|
||||||
public static void testUOEFromGetDeclaringClass() {
|
public void testUOEFromGetMethodType(StackWalker sw) {
|
||||||
try {
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
StackWalker sw = StackWalker.getInstance();
|
sw.forEach(StackWalker.StackFrame::getMethodType));
|
||||||
sw.forEach(StackWalker.StackFrame::getDeclaringClass);
|
|
||||||
throw new RuntimeException("UOE expected");
|
|
||||||
} catch (UnsupportedOperationException expected) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private static Stream<StackWalker> noMethodInfo() {
|
||||||
public static void testUOEFromGetMethodType() {
|
return Stream.of(StackWalker.getInstance(DROP_METHOD_INFO),
|
||||||
try {
|
StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE)));
|
||||||
StackWalker sw = StackWalker.getInstance();
|
}
|
||||||
sw.forEach(StackWalker.StackFrame::getMethodType);
|
|
||||||
throw new RuntimeException("UOE expected");
|
@ParameterizedTest
|
||||||
} catch (UnsupportedOperationException expected) {}
|
@MethodSource("noMethodInfo")
|
||||||
|
public void testNoMethodInfo(StackWalker sw) {
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::getMethodName));
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::getMethodType));
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::getDescriptor));
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::getByteCodeIndex));
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::getFileName));
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::isNativeMethod));
|
||||||
|
assertThrows(UnsupportedOperationException.class, () ->
|
||||||
|
sw.forEach(StackWalker.StackFrame::toStackTraceElement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -66,7 +66,7 @@ public class StackRecorderUtil implements Iterable<StackRecorderUtil.TestFrame>
|
|||||||
if (!tf.declaringClass.equals(sf.getDeclaringClass())) {
|
if (!tf.declaringClass.equals(sf.getDeclaringClass())) {
|
||||||
throw new RuntimeException("Expected class: " +
|
throw new RuntimeException("Expected class: " +
|
||||||
tf.declaringClass.toString() + ", but got: " +
|
tf.declaringClass.toString() + ", but got: " +
|
||||||
sf.getDeclaringClass().toString());
|
sf.getDeclaringClass().toString() + ", index: " + index);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
boolean caught = false;
|
boolean caught = false;
|
||||||
@ -84,11 +84,11 @@ public class StackRecorderUtil implements Iterable<StackRecorderUtil.TestFrame>
|
|||||||
|
|
||||||
if (compareClassNames && !tf.className().equals(sf.getClassName())) {
|
if (compareClassNames && !tf.className().equals(sf.getClassName())) {
|
||||||
throw new RuntimeException("Expected class name: " + tf.className() +
|
throw new RuntimeException("Expected class name: " + tf.className() +
|
||||||
", but got: " + sf.getClassName());
|
", but got: " + sf.getClassName() + ", index: " + index);
|
||||||
}
|
}
|
||||||
if (compareMethodNames && !tf.methodName.equals(sf.getMethodName())) {
|
if (compareMethodNames && !tf.methodName.equals(sf.getMethodName())) {
|
||||||
throw new RuntimeException("Expected method name: " + tf.methodName +
|
throw new RuntimeException("Expected method name: " + tf.methodName +
|
||||||
", but got: " + sf.getMethodName());
|
", but got: " + sf.getMethodName() + ", index: " + index);
|
||||||
}
|
}
|
||||||
if (compareSTEs) {
|
if (compareSTEs) {
|
||||||
StackTraceElement ste = sf.toStackTraceElement();
|
StackTraceElement ste = sf.toStackTraceElement();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -21,17 +21,19 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static java.lang.StackWalker.Option.*;
|
|
||||||
import java.lang.StackWalker.StackFrame;
|
import java.lang.StackWalker.StackFrame;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.lang.StackWalker.Option.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 8140450
|
* @bug 8140450 8210375
|
||||||
* @summary Stack Stream Test
|
* @summary Stack Stream Test
|
||||||
* @modules java.logging
|
* @modules java.logging
|
||||||
* @run main/othervm StackStreamTest
|
* @run main/othervm StackStreamTest
|
||||||
@ -157,6 +159,19 @@ public class StackStreamTest {
|
|||||||
|
|
||||||
// Check STEs for correctness
|
// Check STEs for correctness
|
||||||
checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
|
checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
|
||||||
|
|
||||||
|
System.out.println("Collect classes");
|
||||||
|
List<Class<?>> classes = StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE))
|
||||||
|
.walk(s -> {
|
||||||
|
return s.map(StackFrame::getDeclaringClass).collect(Collectors.toList());
|
||||||
|
});
|
||||||
|
for (i=0; i < GOLDEN_CLASS_NAMES.size(); i++) {
|
||||||
|
Class<?> c = classes.get(i);
|
||||||
|
if (!GOLDEN_CLASS_NAMES.get(i).equals(c.getName())) {
|
||||||
|
throw new RuntimeException("unexpected class at " + i + " " + c.getName() +
|
||||||
|
" expected " + GOLDEN_CLASS_NAMES.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkStackTraceElements(List<String> classNames,
|
static void checkStackTraceElements(List<String> classNames,
|
||||||
|
@ -55,6 +55,7 @@ public class StackWalkTest {
|
|||||||
private static final int MAX_RANDOM_DEPTH = 1000;
|
private static final int MAX_RANDOM_DEPTH = 1000;
|
||||||
|
|
||||||
static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
|
static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
|
||||||
|
"jdk.internal.reflect.DirectMethodHandleAccessor",
|
||||||
"java.lang.reflect.Method",
|
"java.lang.reflect.Method",
|
||||||
"com.sun.javatest.regtest.MainWrapper$MainThread",
|
"com.sun.javatest.regtest.MainWrapper$MainThread",
|
||||||
"com.sun.javatest.regtest.agent.MainWrapper$MainThread",
|
"com.sun.javatest.regtest.agent.MainWrapper$MainThread",
|
||||||
|
@ -153,7 +153,7 @@ public class VerifyStackTrace {
|
|||||||
|
|
||||||
static final class TestCase4 extends TestCase3 {
|
static final class TestCase4 extends TestCase3 {
|
||||||
private final StackWalker walker = StackWalker.getInstance(
|
private final StackWalker walker = StackWalker.getInstance(
|
||||||
EnumSet.allOf(StackWalker.Option.class));
|
EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES, SHOW_REFLECT_FRAMES));
|
||||||
|
|
||||||
private final String description = "StackWalker.getInstance(" +
|
private final String description = "StackWalker.getInstance(" +
|
||||||
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
|
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
|
||||||
|
@ -41,10 +41,10 @@ public final class StackWalkerHelper {
|
|||||||
private static final Set<Option> OPTS = EnumSet.of(Option.SHOW_REFLECT_FRAMES); // EnumSet.noneOf(Option.class);
|
private static final Set<Option> OPTS = EnumSet.of(Option.SHOW_REFLECT_FRAMES); // EnumSet.noneOf(Option.class);
|
||||||
|
|
||||||
public static StackWalker getInstance(ContinuationScope scope) {
|
public static StackWalker getInstance(ContinuationScope scope) {
|
||||||
return StackWalker.getInstance(scope);
|
return StackWalker.newInstance(Set.of(), scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StackFrame[] getStackFrames(ContinuationScope scope) { return getStackFrames(StackWalker.getInstance(OPTS, scope)); }
|
public static StackFrame[] getStackFrames(ContinuationScope scope) { return getStackFrames(StackWalker.newInstance(OPTS, scope)); }
|
||||||
public static StackFrame[] getStackFrames(Continuation cont) { return getStackFrames(cont.stackWalker(OPTS)); }
|
public static StackFrame[] getStackFrames(Continuation cont) { return getStackFrames(cont.stackWalker(OPTS)); }
|
||||||
|
|
||||||
public static StackFrame[] getLiveStackFrames(ContinuationScope scope) { return getStackFrames(LiveStackFrame.getStackWalker(OPTS, scope)); }
|
public static StackFrame[] getLiveStackFrames(ContinuationScope scope) { return getStackFrames(LiveStackFrame.getStackWalker(OPTS, scope)); }
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -24,6 +24,8 @@ package org.openjdk.bench.java.lang;
|
|||||||
|
|
||||||
import java.lang.StackWalker.StackFrame;
|
import java.lang.StackWalker.StackFrame;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
import org.openjdk.jmh.annotations.Fork;
|
import org.openjdk.jmh.annotations.Fork;
|
||||||
@ -36,6 +38,8 @@ import org.openjdk.jmh.annotations.State;
|
|||||||
import org.openjdk.jmh.annotations.Warmup;
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
import org.openjdk.jmh.infra.Blackhole;
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
import static java.lang.StackWalker.Option.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Benchmarks for java.lang.StackWalker
|
* Benchmarks for java.lang.StackWalker
|
||||||
*/
|
*/
|
||||||
@ -46,10 +50,18 @@ import org.openjdk.jmh.infra.Blackhole;
|
|||||||
@Measurement(iterations = 5, time = 1)
|
@Measurement(iterations = 5, time = 1)
|
||||||
@Fork(3)
|
@Fork(3)
|
||||||
public class StackWalkBench {
|
public class StackWalkBench {
|
||||||
private static final StackWalker WALKER_DEFAULT = StackWalker.getInstance();
|
private static final StackWalker WALKER =
|
||||||
|
StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
|
||||||
|
private static final StackWalker WALKER_CLASS_ONLY =
|
||||||
|
StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE));
|
||||||
|
|
||||||
private static final StackWalker WALKER_CLASS =
|
static StackWalker walker(String name) {
|
||||||
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
return switch (name) {
|
||||||
|
case "class+method" -> WALKER;
|
||||||
|
case "class_only" -> WALKER_CLASS_ONLY;
|
||||||
|
default -> throw new IllegalArgumentException(name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// TestStack will add this number of calls to the call stack
|
// TestStack will add this number of calls to the call stack
|
||||||
@Param({"4", "100", "1000"})
|
@Param({"4", "100", "1000"})
|
||||||
@ -63,6 +75,9 @@ public class StackWalkBench {
|
|||||||
// @Param({"4"})
|
// @Param({"4"})
|
||||||
public int mark = 4;
|
public int mark = 4;
|
||||||
|
|
||||||
|
@Param({"class+method", "class_only"})
|
||||||
|
public String walker;
|
||||||
|
|
||||||
/** Build a call stack of a given size, then run trigger code in it.
|
/** Build a call stack of a given size, then run trigger code in it.
|
||||||
* (Does not account for existing frames higher up in the JMH machinery).
|
* (Does not account for existing frames higher up in the JMH machinery).
|
||||||
*/
|
*/
|
||||||
@ -175,7 +190,7 @@ public class StackWalkBench {
|
|||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
new TestStack(depth, new Runnable() {
|
new TestStack(depth, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
WALKER_DEFAULT.forEach(localBH::consume);
|
walker(walker).forEach(localBH::consume);
|
||||||
done[0] = true;
|
done[0] = true;
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
@ -193,7 +208,7 @@ public class StackWalkBench {
|
|||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
new TestStack(depth, new Runnable() {
|
new TestStack(depth, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
WALKER_DEFAULT.walk(s -> {
|
walker(walker).walk(s -> {
|
||||||
s.map(StackFrame::getClassName).forEach(localBH::consume);
|
s.map(StackFrame::getClassName).forEach(localBH::consume);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -212,9 +227,11 @@ public class StackWalkBench {
|
|||||||
public void walk_MethodNames(Blackhole bh) {
|
public void walk_MethodNames(Blackhole bh) {
|
||||||
final Blackhole localBH = bh;
|
final Blackhole localBH = bh;
|
||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
|
if (sw == WALKER_CLASS_ONLY) return;
|
||||||
new TestStack(depth, new Runnable() {
|
new TestStack(depth, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
WALKER_DEFAULT.walk( s -> {
|
sw.walk( s -> {
|
||||||
s.map(StackFrame::getMethodName).forEach(localBH::consume);
|
s.map(StackFrame::getMethodName).forEach(localBH::consume);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -233,9 +250,10 @@ public class StackWalkBench {
|
|||||||
public void walk_DeclaringClass(Blackhole bh) {
|
public void walk_DeclaringClass(Blackhole bh) {
|
||||||
final Blackhole localBH = bh;
|
final Blackhole localBH = bh;
|
||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
new TestStack(depth, new Runnable() {
|
new TestStack(depth, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
WALKER_CLASS.walk(s -> {
|
sw.walk(s -> {
|
||||||
s.map(StackFrame::getDeclaringClass).forEach(localBH::consume);
|
s.map(StackFrame::getDeclaringClass).forEach(localBH::consume);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -247,6 +265,27 @@ public class StackWalkBench {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use Stackwalker.walk() to fetch all instances
|
||||||
|
*/
|
||||||
|
@Benchmark
|
||||||
|
public void walk_StackFrame(Blackhole bh) {
|
||||||
|
final Blackhole localBH = bh;
|
||||||
|
final boolean[] done = {false};
|
||||||
|
new TestStack(depth, new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
walker(walker).walk(s -> {
|
||||||
|
s.forEach(localBH::consume);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
done[0] = true;
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
if (!done[0]) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use StackWalker.walk() to fetch StackTraceElements
|
* Use StackWalker.walk() to fetch StackTraceElements
|
||||||
*/
|
*/
|
||||||
@ -254,9 +293,12 @@ public class StackWalkBench {
|
|||||||
public void walk_StackTraceElements(Blackhole bh) {
|
public void walk_StackTraceElements(Blackhole bh) {
|
||||||
final Blackhole localBH = bh;
|
final Blackhole localBH = bh;
|
||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
|
if (sw == WALKER_CLASS_ONLY) return;
|
||||||
|
|
||||||
new TestStack(depth, new Runnable() {
|
new TestStack(depth, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
WALKER_DEFAULT.walk(s -> {
|
sw.walk(s -> {
|
||||||
s.map(StackFrame::toStackTraceElement).forEach(localBH::consume);
|
s.map(StackFrame::toStackTraceElement).forEach(localBH::consume);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -273,11 +315,41 @@ public class StackWalkBench {
|
|||||||
*/
|
*/
|
||||||
@Benchmark
|
@Benchmark
|
||||||
public void getCallerClass(Blackhole bh) {
|
public void getCallerClass(Blackhole bh) {
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
|
Class<?> c = sw.getCallerClass();
|
||||||
|
bh.consume(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StackWalker.getCallerClass() with generated call stack of
|
||||||
|
* the given depth.
|
||||||
|
*/
|
||||||
|
@Benchmark
|
||||||
|
public void getCallerClass_withTestStack(Blackhole bh) {
|
||||||
|
final Blackhole localBH = bh;
|
||||||
|
final boolean[] done = {false};
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
|
new TestStack(depth, new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
localBH.consume(sw.getCallerClass());
|
||||||
|
done[0] = true;
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
if (!done[0]) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline for generating a call stack of the given depth.
|
||||||
|
* (No StackWalk operations are executed)
|
||||||
|
*/
|
||||||
|
@Benchmark
|
||||||
|
public void makeCallStack(Blackhole bh) {
|
||||||
final Blackhole localBH = bh;
|
final Blackhole localBH = bh;
|
||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
new TestStack(depth, new Runnable() {
|
new TestStack(depth, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
localBH.consume(WALKER_CLASS.getCallerClass());
|
|
||||||
done[0] = true;
|
done[0] = true;
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
@ -295,11 +367,11 @@ public class StackWalkBench {
|
|||||||
public void walk_filterCallerClass(Blackhole bh) {
|
public void walk_filterCallerClass(Blackhole bh) {
|
||||||
final Blackhole localBH = bh;
|
final Blackhole localBH = bh;
|
||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
new MarkedTestStack(depth, mark, new Runnable() {
|
new MarkedTestStack(depth, mark, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// To be comparable with Reflection.getCallerClass(), return the Class object
|
// To be comparable with Reflection.getCallerClass(), return the Class object
|
||||||
WALKER_CLASS.walk(s -> {
|
sw.walk(s -> {
|
||||||
localBH.consume(s.filter(f -> TestMarker.class.equals(f.getDeclaringClass())).findFirst().get().getDeclaringClass());
|
localBH.consume(s.filter(f -> TestMarker.class.equals(f.getDeclaringClass())).findFirst().get().getDeclaringClass());
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -321,11 +393,11 @@ public class StackWalkBench {
|
|||||||
public void walk_filterCallerClassHalfStack(Blackhole bh) {
|
public void walk_filterCallerClassHalfStack(Blackhole bh) {
|
||||||
final Blackhole localBH = bh;
|
final Blackhole localBH = bh;
|
||||||
final boolean[] done = {false};
|
final boolean[] done = {false};
|
||||||
|
final StackWalker sw = walker(walker);
|
||||||
new MarkedTestStack(depth, depth / 2, new Runnable() {
|
new MarkedTestStack(depth, depth / 2, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// To be comparable with Reflection.getCallerClass(), return the Class object
|
// To be comparable with Reflection.getCallerClass(), return the Class object
|
||||||
WALKER_CLASS.walk(s -> {
|
sw.walk(s -> {
|
||||||
localBH.consume(s.filter((f) -> TestMarker.class.equals(f.getDeclaringClass())).findFirst().get().getDeclaringClass());
|
localBH.consume(s.filter((f) -> TestMarker.class.equals(f.getDeclaringClass())).findFirst().get().getDeclaringClass());
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user