8269592: [JVMCI] Optimize c2v_iterateFrames

Reviewed-by: kvn, never, dlong
This commit is contained in:
Andreas Woess 2021-07-14 17:32:55 +00:00 committed by Tom Rodriguez
parent c0d4efff3c
commit b1bb05bcf4
6 changed files with 410 additions and 128 deletions

View File

@ -62,6 +62,7 @@
#include "runtime/stackFrameStream.inline.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe_hp.hpp"
#include "runtime/vframe.inline.hpp"
JVMCIKlassHandle::JVMCIKlassHandle(Thread* thread, Klass* klass) {
_thread = thread;
@ -1147,30 +1148,99 @@ C2V_VMENTRY_NULL(jobject, getSymbol, (JNIEnv* env, jobject, jlong symbol))
return JVMCIENV->get_jobject(sym);
C2V_END
bool matches(jobjectArray methods, Method* method, JVMCIEnv* JVMCIENV) {
/*
* Used by matches() to convert a ResolvedJavaMethod[] to an array of Method*.
*/
GrowableArray<Method*>* init_resolved_methods(jobjectArray methods, JVMCIEnv* JVMCIENV) {
objArrayOop methods_oop = (objArrayOop) JNIHandles::resolve(methods);
GrowableArray<Method*>* resolved_methods = new GrowableArray<Method*>(methods_oop->length());
for (int i = 0; i < methods_oop->length(); i++) {
oop resolved = methods_oop->obj_at(i);
if ((resolved->klass() == HotSpotJVMCI::HotSpotResolvedJavaMethodImpl::klass()) && HotSpotJVMCI::asMethod(JVMCIENV, resolved) == method) {
Method* resolved_method = NULL;
if (resolved->klass() == HotSpotJVMCI::HotSpotResolvedJavaMethodImpl::klass()) {
resolved_method = HotSpotJVMCI::asMethod(JVMCIENV, resolved);
}
resolved_methods->append(resolved_method);
}
return resolved_methods;
}
/*
* Used by c2v_iterateFrames to check if `method` matches one of the ResolvedJavaMethods in the `methods` array.
* The ResolvedJavaMethod[] array is converted to a Method* array that is then cached in the resolved_methods_ref in/out parameter.
* In case of a match, the matching ResolvedJavaMethod is returned in matched_jvmci_method_ref.
*/
bool matches(jobjectArray methods, Method* method, GrowableArray<Method*>** resolved_methods_ref, Handle* matched_jvmci_method_ref, Thread* THREAD, JVMCIEnv* JVMCIENV) {
GrowableArray<Method*>* resolved_methods = *resolved_methods_ref;
if (resolved_methods == NULL) {
resolved_methods = init_resolved_methods(methods, JVMCIENV);
*resolved_methods_ref = resolved_methods;
}
assert(method != NULL, "method should not be NULL");
assert(resolved_methods->length() == ((objArrayOop) JNIHandles::resolve(methods))->length(), "arrays must have the same length");
for (int i = 0; i < resolved_methods->length(); i++) {
Method* m = resolved_methods->at(i);
if (m == method) {
*matched_jvmci_method_ref = Handle(THREAD, ((objArrayOop) JNIHandles::resolve(methods))->obj_at(i));
return true;
}
}
return false;
}
void call_interface(JavaValue* result, Klass* spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) {
/*
* Resolves an interface call to a concrete method handle.
*/
methodHandle resolve_interface_call(Klass* spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS) {
CallInfo callinfo;
Handle receiver = args->receiver();
Klass* recvrKlass = receiver.is_null() ? (Klass*)NULL : receiver->klass();
LinkInfo link_info(spec_klass, name, signature);
LinkResolver::resolve_interface_call(
callinfo, receiver, recvrKlass, link_info, true, CHECK);
callinfo, receiver, recvrKlass, link_info, true, CHECK_(methodHandle()));
methodHandle method(THREAD, callinfo.selected_method());
assert(method.not_null(), "should have thrown exception");
return method;
}
// Invoke the method
JavaCalls::call(result, method, args, CHECK);
/*
* Used by c2v_iterateFrames to make a new vframeStream at the given compiled frame id (stack pointer) and vframe id.
*/
void resync_vframestream_to_compiled_frame(vframeStream& vfst, intptr_t* stack_pointer, int vframe_id, JavaThread* thread, TRAPS) {
vfst = vframeStream(thread);
while (vfst.frame_id() != stack_pointer && !vfst.at_end()) {
vfst.next();
}
if (vfst.frame_id() != stack_pointer) {
THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "stack frame not found after deopt")
}
if (vfst.is_interpreted_frame()) {
THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "compiled stack frame expected")
}
while (vfst.vframe_id() != vframe_id) {
if (vfst.at_end()) {
THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "vframe not found after deopt")
}
vfst.next();
assert(!vfst.is_interpreted_frame(), "Wrong frame type");
}
}
/*
* Used by c2v_iterateFrames. Returns an array of any unallocated scope objects or NULL if none.
*/
GrowableArray<ScopeValue*>* get_unallocated_objects_or_null(GrowableArray<ScopeValue*>* scope_objects) {
GrowableArray<ScopeValue*>* unallocated = NULL;
for (int i = 0; i < scope_objects->length(); i++) {
ObjectValue* sv = (ObjectValue*) scope_objects->at(i);
if (sv->value().is_null()) {
if (unallocated == NULL) {
unallocated = new GrowableArray<ScopeValue*>(scope_objects->length());
}
unallocated->append(sv);
}
}
return unallocated;
}
C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, jobjectArray initial_methods, jobjectArray match_methods, jint initialSkip, jobject visitor_handle))
@ -1183,90 +1253,100 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job
requireInHotSpot("iterateFrames", JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::klass()->initialize(CHECK_NULL);
Handle frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL);
StackFrameStream fst(thread, true /* update */, true /* process_frames */);
vframeStream vfst(thread);
jobjectArray methods = initial_methods;
methodHandle visitor_method;
GrowableArray<Method*>* resolved_methods = NULL;
int frame_number = 0;
vframe* vf = vframe::new_vframe(fst, thread);
while (true) {
// look for the given method
while (!vfst.at_end()) { // frame loop
bool realloc_called = false;
while (true) {
StackValueCollection* locals = NULL;
if (vf->is_compiled_frame()) {
// compiled method frame
compiledVFrame* cvf = compiledVFrame::cast(vf);
if (methods == NULL || matches(methods, cvf->method(), JVMCIENV)) {
if (initialSkip > 0) {
initialSkip--;
} else {
ScopeDesc* scope = cvf->scope();
// native wrappers do not have a scope
if (scope != NULL && scope->objects() != NULL) {
GrowableArray<ScopeValue*>* objects;
if (!realloc_called) {
objects = scope->objects();
} else {
// some object might already have been re-allocated, only reallocate the non-allocated ones
objects = new GrowableArray<ScopeValue*>(scope->objects()->length());
for (int i = 0; i < scope->objects()->length(); i++) {
ObjectValue* sv = (ObjectValue*) scope->objects()->at(i);
if (sv->value().is_null()) {
objects->append(sv);
}
}
}
bool realloc_failures = Deoptimization::realloc_objects(thread, fst.current(), fst.register_map(), objects, CHECK_NULL);
Deoptimization::reassign_fields(fst.current(), fst.register_map(), objects, realloc_failures, false);
realloc_called = true;
intptr_t* frame_id = vfst.frame_id();
GrowableArray<ScopeValue*>* local_values = scope->locals();
assert(local_values != NULL, "NULL locals");
typeArrayOop array_oop = oopFactory::new_boolArray(local_values->length(), CHECK_NULL);
typeArrayHandle array(THREAD, array_oop);
for (int i = 0; i < local_values->length(); i++) {
ScopeValue* value = local_values->at(i);
if (value->is_object()) {
array->bool_at_put(i, true);
}
}
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), array());
// Previous compiledVFrame of this frame; use with at_scope() to reuse scope object pool.
compiledVFrame* prev_cvf = NULL;
for (; !vfst.at_end() && vfst.frame_id() == frame_id; vfst.next()) { // vframe loop
int frame_number = 0;
Method *method = vfst.method();
int bci = vfst.bci();
Handle matched_jvmci_method;
if (methods == NULL || matches(methods, method, &resolved_methods, &matched_jvmci_method, THREAD, JVMCIENV)) {
if (initialSkip > 0) {
initialSkip--;
continue;
}
javaVFrame* vf;
if (prev_cvf != NULL && prev_cvf->frame_pointer()->id() == frame_id) {
assert(prev_cvf->is_compiled_frame(), "expected compiled Java frame");
vf = prev_cvf->at_scope(vfst.decode_offset(), vfst.vframe_id());
} else {
vf = vfst.asJavaVFrame();
}
StackValueCollection* locals = NULL;
typeArrayHandle localIsVirtual_h;
if (vf->is_compiled_frame()) {
// compiled method frame
compiledVFrame* cvf = compiledVFrame::cast(vf);
ScopeDesc* scope = cvf->scope();
// native wrappers do not have a scope
if (scope != NULL && scope->objects() != NULL) {
prev_cvf = cvf;
GrowableArray<ScopeValue*>* objects = NULL;
if (!realloc_called) {
objects = scope->objects();
} else {
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), NULL);
// some object might already have been re-allocated, only reallocate the non-allocated ones
objects = get_unallocated_objects_or_null(scope->objects());
}
locals = cvf->locals();
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), cvf->bci());
methodHandle mh(THREAD, cvf->method());
JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), JNIHandles::resolve(method.as_jobject()));
}
}
} else if (vf->is_interpreted_frame()) {
// interpreted method frame
interpretedVFrame* ivf = interpretedVFrame::cast(vf);
if (methods == NULL || matches(methods, ivf->method(), JVMCIENV)) {
if (initialSkip > 0) {
initialSkip--;
} else {
locals = ivf->locals();
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), ivf->bci());
methodHandle mh(THREAD, ivf->method());
JVMCIObject method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), JNIHandles::resolve(method.as_jobject()));
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), NULL);
}
}
}
if (objects != NULL) {
RegisterMap reg_map(vf->register_map());
bool realloc_failures = Deoptimization::realloc_objects(thread, vf->frame_pointer(), &reg_map, objects, CHECK_NULL);
Deoptimization::reassign_fields(vf->frame_pointer(), &reg_map, objects, realloc_failures, false);
realloc_called = true;
}
GrowableArray<ScopeValue*>* local_values = scope->locals();
for (int i = 0; i < local_values->length(); i++) {
ScopeValue* value = local_values->at(i);
if (value->is_object()) {
if (localIsVirtual_h.is_null()) {
typeArrayOop array_oop = oopFactory::new_boolArray(local_values->length(), CHECK_NULL);
localIsVirtual_h = typeArrayHandle(THREAD, array_oop);
}
localIsVirtual_h->bool_at_put(i, true);
}
}
}
locals = cvf->locals();
frame_number = cvf->vframe_id();
} else {
// interpreted method frame
interpretedVFrame* ivf = interpretedVFrame::cast(vf);
locals = ivf->locals();
}
assert(bci == vf->bci(), "wrong bci");
assert(method == vf->method(), "wrong method");
Handle frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::set_bci(JVMCIENV, frame_reference(), bci);
if (matched_jvmci_method.is_null()) {
methodHandle mh(THREAD, method);
JVMCIObject jvmci_method = JVMCIENV->get_jvmci_method(mh, JVMCI_CHECK_NULL);
matched_jvmci_method = Handle(THREAD, JNIHandles::resolve(jvmci_method.as_jobject()));
}
HotSpotJVMCI::HotSpotStackFrameReference::set_method(JVMCIENV, frame_reference(), matched_jvmci_method());
HotSpotJVMCI::HotSpotStackFrameReference::set_localIsVirtual(JVMCIENV, frame_reference(), localIsVirtual_h());
// locals != NULL means that we found a matching frame and result is already partially initialized
if (locals != NULL) {
methods = match_methods;
HotSpotJVMCI::HotSpotStackFrameReference::set_compilerToVM(JVMCIENV, frame_reference(), JNIHandles::resolve(compilerToVM));
HotSpotJVMCI::HotSpotStackFrameReference::set_stackPointer(JVMCIENV, frame_reference(), (jlong) fst.current()->sp());
HotSpotJVMCI::HotSpotStackFrameReference::set_stackPointer(JVMCIENV, frame_reference(), (jlong) frame_id);
HotSpotJVMCI::HotSpotStackFrameReference::set_frameNumber(JVMCIENV, frame_reference(), frame_number);
// initialize the locals array
@ -1283,51 +1363,30 @@ C2V_VMENTRY_NULL(jobject, iterateFrames, (JNIEnv* env, jobject compilerToVM, job
JavaValue result(T_OBJECT);
JavaCallArguments args(visitor);
if (visitor_method.is_null()) {
visitor_method = resolve_interface_call(HotSpotJVMCI::InspectedFrameVisitor::klass(), vmSymbols::visitFrame_name(), vmSymbols::visitFrame_signature(), &args, CHECK_NULL);
}
args.push_oop(frame_reference);
call_interface(&result, HotSpotJVMCI::InspectedFrameVisitor::klass(), vmSymbols::visitFrame_name(), vmSymbols::visitFrame_signature(), &args, CHECK_NULL);
JavaCalls::call(&result, visitor_method, &args, CHECK_NULL);
if (result.get_oop() != NULL) {
return JNIHandles::make_local(thread, result.get_oop());
}
if (methods == initial_methods) {
methods = match_methods;
if (resolved_methods != NULL && JNIHandles::resolve(match_methods) != JNIHandles::resolve(initial_methods)) {
resolved_methods = NULL;
}
}
assert(initialSkip == 0, "There should be no match before initialSkip == 0");
if (HotSpotJVMCI::HotSpotStackFrameReference::objectsMaterialized(JVMCIENV, frame_reference()) == JNI_TRUE) {
// the frame has been deoptimized, we need to re-synchronize the frame and vframe
prev_cvf = NULL;
intptr_t* stack_pointer = (intptr_t*) HotSpotJVMCI::HotSpotStackFrameReference::stackPointer(JVMCIENV, frame_reference());
fst = StackFrameStream(thread, true /* update */, true /* process_frames */);
while (fst.current()->sp() != stack_pointer && !fst.is_done()) {
fst.next();
}
if (fst.current()->sp() != stack_pointer) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalStateException(), "stack frame not found after deopt")
}
vf = vframe::new_vframe(fst, thread);
if (!vf->is_compiled_frame()) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalStateException(), "compiled stack frame expected")
}
for (int i = 0; i < frame_number; i++) {
if (vf->is_top()) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalStateException(), "vframe not found after deopt")
}
vf = vf->sender();
assert(vf->is_compiled_frame(), "Wrong frame type");
}
resync_vframestream_to_compiled_frame(vfst, stack_pointer, frame_number, thread, CHECK_NULL);
}
frame_reference = HotSpotJVMCI::HotSpotStackFrameReference::klass()->allocate_instance_handle(CHECK_NULL);
HotSpotJVMCI::HotSpotStackFrameReference::klass()->initialize(CHECK_NULL);
}
if (vf->is_top()) {
break;
}
frame_number++;
vf = vf->sender();
} // end of vframe loop
if (fst.is_done()) {
break;
}
fst.next();
vf = vframe::new_vframe(fst, thread);
frame_number = 0;
} // end of frame loop
// the end was reached without finding a matching method
@ -1426,10 +1485,10 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
// look for the given stack frame
StackFrameStream fst(thread, false /* update */, true /* process_frames */);
intptr_t* stack_pointer = (intptr_t*) JVMCIENV->get_HotSpotStackFrameReference_stackPointer(hs_frame);
while (fst.current()->sp() != stack_pointer && !fst.is_done()) {
while (fst.current()->id() != stack_pointer && !fst.is_done()) {
fst.next();
}
if (fst.current()->sp() != stack_pointer) {
if (fst.current()->id() != stack_pointer) {
JVMCI_THROW_MSG(IllegalStateException, "stack frame not found");
}
@ -1443,10 +1502,10 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
Deoptimization::deoptimize(thread, *fst.current(), Deoptimization::Reason_none);
// look for the frame again as it has been updated by deopt (pc, deopt state...)
StackFrameStream fstAfterDeopt(thread, true /* update */, true /* process_frames */);
while (fstAfterDeopt.current()->sp() != stack_pointer && !fstAfterDeopt.is_done()) {
while (fstAfterDeopt.current()->id() != stack_pointer && !fstAfterDeopt.is_done()) {
fstAfterDeopt.next();
}
if (fstAfterDeopt.current()->sp() != stack_pointer) {
if (fstAfterDeopt.current()->id() != stack_pointer) {
JVMCI_THROW_MSG(IllegalStateException, "stack frame not found after deopt");
}

View File

@ -574,31 +574,36 @@ void vframeStreamCommon::skip_prefixed_method_and_wrappers() {
javaVFrame* vframeStreamCommon::asJavaVFrame() {
javaVFrame* result = NULL;
if (_mode == compiled_mode) {
guarantee(_frame.is_compiled_frame(), "expected compiled Java frame");
assert(_frame.is_compiled_frame() || _frame.is_native_frame(), "expected compiled Java frame");
// lazy update to register map
bool update_map = true;
RegisterMap map(_thread, update_map);
frame f = _prev_frame.sender(&map);
guarantee(f.is_compiled_frame(), "expected compiled Java frame");
assert(f.is_compiled_frame() || f.is_native_frame(), "expected compiled Java frame");
compiledVFrame* cvf = compiledVFrame::cast(vframe::new_vframe(&f, &map, _thread));
guarantee(cvf->cb() == cb(), "wrong code blob");
assert(cvf->cb() == cb(), "wrong code blob");
// get the same scope as this stream
cvf = cvf->at_scope(_decode_offset, _vframe_id);
if (cvf->scope() == NULL) {
// native nmethods have no scope
assert(f.is_native_frame(), "expected native frame");
} else {
// get the same scope as this stream
cvf = cvf->at_scope(_decode_offset, _vframe_id);
guarantee(cvf->scope()->decode_offset() == _decode_offset, "wrong scope");
guarantee(cvf->scope()->sender_decode_offset() == _sender_decode_offset, "wrong scope");
guarantee(cvf->vframe_id() == _vframe_id, "wrong vframe");
assert(cvf->scope()->decode_offset() == _decode_offset, "wrong scope");
assert(cvf->scope()->sender_decode_offset() == _sender_decode_offset, "wrong scope");
}
assert(cvf->vframe_id() == _vframe_id, "wrong vframe");
result = cvf;
} else {
result = javaVFrame::cast(vframe::new_vframe(&_frame, &_reg_map, _thread));
}
guarantee(result->method() == method(), "wrong method");
assert(result->method() == method(), "wrong method");
return result;
}

View File

@ -310,6 +310,8 @@ class vframeStreamCommon : StackObj {
int bci() const { return _bci; }
inline intptr_t* frame_id() const;
address frame_pc() const { return _frame.pc(); }
inline int vframe_id() const;
inline int decode_offset() const;
CodeBlob* cb() const { return _frame.cb(); }
CompiledMethod* nm() const {

View File

@ -36,6 +36,16 @@ inline vframeStreamCommon::vframeStreamCommon(JavaThread* thread, bool process_f
inline intptr_t* vframeStreamCommon::frame_id() const { return _frame.id(); }
inline int vframeStreamCommon::vframe_id() const {
assert(_mode == compiled_mode, "unexpected mode: %d", _mode);
return _vframe_id;
}
inline int vframeStreamCommon::decode_offset() const {
assert(_mode == compiled_mode, "unexpected mode: %d", _mode);
return _decode_offset;
}
inline bool vframeStreamCommon::is_interpreted_frame() const { return _frame.is_interpreted_frame(); }
inline bool vframeStreamCommon::is_entry_frame() const { return _frame.is_entry_frame(); }

View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8269592
*
* @requires vm.jvmci & vm.compMode == "Xmixed"
* @requires vm.opt.final.EliminateAllocations == true
*
* @comment no "-Xcomp -XX:-TieredCompilation" combination allowed until JDK-8140018 is resolved
* @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true
*
* @library / /test/lib
* @library ../common/patches
* @modules java.base/jdk.internal.misc
* @modules java.base/jdk.internal.org.objectweb.asm
* java.base/jdk.internal.org.objectweb.asm.tree
* jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.code
* jdk.internal.vm.ci/jdk.vm.ci.code.stack
* jdk.internal.vm.ci/jdk.vm.ci.meta
*
* @build jdk.internal.vm.ci/jdk.vm.ci.hotspot.CompilerToVMHelper sun.hotspot.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -Xbatch -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI
* -XX:+DoEscapeAnalysis -XX:-UseCounterDecay
* compiler.jvmci.compilerToVM.IterateFramesNative
*/
package compiler.jvmci.compilerToVM;
import compiler.jvmci.common.CTVMUtilities;
import compiler.testlibrary.CompilerUtils;
import compiler.whitebox.CompilerWhiteBoxTest;
import jdk.test.lib.Asserts;
import jdk.vm.ci.code.stack.InspectedFrame;
import jdk.vm.ci.hotspot.CompilerToVMHelper;
import jdk.vm.ci.hotspot.HotSpotStackFrameReference;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jtreg.SkippedException;
import sun.hotspot.WhiteBox;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;
public class IterateFramesNative {
private static final WhiteBox WB;
private static final int COMPILE_THRESHOLD;
private static final ResolvedJavaMethod NATIVE_METHOD_RESOLVED;
private static final ResolvedJavaMethod NATIVE_CALLBACK_METHOD_RESOLVED;
static {
Method nativeMethod;
Method nativeCallbackMethod;
WB = WhiteBox.getWhiteBox();
try {
nativeMethod = IterateFramesNative.class.getDeclaredMethod("callerNative",
Runnable.class);
nativeCallbackMethod = IterateFramesNative.class.getDeclaredMethod("testNativeFrameCallback",
Helper.class, int.class);
} catch (NoSuchMethodException e) {
throw new Error("Can't get executable for test method", e);
}
NATIVE_METHOD_RESOLVED = CTVMUtilities.getResolvedMethod(nativeMethod);
NATIVE_CALLBACK_METHOD_RESOLVED = CTVMUtilities.getResolvedMethod(nativeCallbackMethod);
COMPILE_THRESHOLD = WB.getBooleanVMFlag("TieredCompilation")
? CompilerWhiteBoxTest.THRESHOLD
: CompilerWhiteBoxTest.THRESHOLD * 2;
loadNativeLibrary();
}
public static void main(String[] args) {
int levels[] = CompilerUtils.getAvailableCompilationLevels();
// we need compilation level 4 to use EscapeAnalysis
if (levels.length < 1 || levels[levels.length - 1] != 4) {
throw new SkippedException("Test needs compilation level 4");
}
new IterateFramesNative().test();
}
private void test() {
for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD + 1; i++) {
testNativeFrame("someString", i);
}
}
/**
* Loads native library(libIterateFramesNative.so)
*/
protected static void loadNativeLibrary() {
System.loadLibrary("IterateFramesNative");
}
private void testNativeFrame(String str, int iteration) {
Helper innerHelper = new Helper("foo");
callerNative(() -> testNativeFrameCallback(innerHelper, iteration));
Asserts.assertEQ(innerHelper.string, NATIVE_METHOD_RESOLVED.getName(),
"Native frame not found?: " + NATIVE_METHOD_RESOLVED.getName());
}
public static native void callerNative(Runnable runnable);
private void testNativeFrameCallback(Helper helper, int iteration) {
AtomicInteger frameCounter = new AtomicInteger();
ResolvedJavaMethod[] methods = new ResolvedJavaMethod[] {NATIVE_METHOD_RESOLVED, NATIVE_CALLBACK_METHOD_RESOLVED};
CompilerToVMHelper.iterateFrames(
methods,
methods,
0,
f -> {
HotSpotStackFrameReference frame = (HotSpotStackFrameReference) f;
Asserts.assertNotNull(frame, "got null frame for native method");
int index = frameCounter.getAndIncrement();
if (index == 0) {
Asserts.assertTrue(frame.isMethod(NATIVE_CALLBACK_METHOD_RESOLVED),
"unexpected method: " + frame.getMethod().getName());
} else if (index == 1) {
Asserts.assertTrue(frame.isMethod(NATIVE_METHOD_RESOLVED),
"unexpected method: " + frame.getMethod().getName());
helper.string = frame.getMethod().getName();
Asserts.assertFalse(frame.hasVirtualObjects(),
"native frames do not have virtual objects");
return frame; // stop
}
return null;
});
}
private class Helper {
public String string;
public Helper(String s) {
this.string = s;
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdio.h>
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CHECK_EXCEPTIONS if ((*env)->ExceptionCheck(env)) return
JNIEXPORT void JNICALL Java_compiler_jvmci_compilerToVM_IterateFramesNative_callerNative(JNIEnv *env, jobject obj, jobject runnable) {
jclass cls = (*env)->GetObjectClass(env, runnable);
jmethodID runMethodID = (*env)->GetMethodID(env, cls, "run", "()V");
CHECK_EXCEPTIONS;
(*env)->CallVoidMethod(env, runnable, runMethodID);
CHECK_EXCEPTIONS;
}
#ifdef __cplusplus
}
#endif