8230677: Should disable Escape Analysis if JVMTI capability can_get_owned_monitor_info was taken

Reviewed-by: sspitsyn, dholmes, kvn
This commit is contained in:
Richard Reingruber 2019-10-08 15:30:39 +02:00
parent 9e17946ab2
commit a683592254
10 changed files with 1007 additions and 2 deletions

View File

@ -238,6 +238,7 @@ void ciEnv::cache_jvmti_state() {
_jvmti_can_access_local_variables = JvmtiExport::can_access_local_variables();
_jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions();
_jvmti_can_pop_frame = JvmtiExport::can_pop_frame();
_jvmti_can_get_owned_monitor_info = JvmtiExport::can_get_owned_monitor_info();
}
bool ciEnv::jvmti_state_changed() const {
@ -262,6 +263,10 @@ bool ciEnv::jvmti_state_changed() const {
JvmtiExport::can_pop_frame()) {
return true;
}
if (!_jvmti_can_get_owned_monitor_info &&
JvmtiExport::can_get_owned_monitor_info()) {
return true;
}
return false;
}

View File

@ -73,6 +73,7 @@ private:
bool _jvmti_can_access_local_variables;
bool _jvmti_can_post_on_exceptions;
bool _jvmti_can_pop_frame;
bool _jvmti_can_get_owned_monitor_info; // includes can_get_owned_monitor_stack_depth_info
// Cache DTrace flags
bool _dtrace_extended_probes;
@ -347,6 +348,7 @@ public:
}
bool jvmti_can_hotswap_or_post_breakpoint() const { return _jvmti_can_hotswap_or_post_breakpoint; }
bool jvmti_can_post_on_exceptions() const { return _jvmti_can_post_on_exceptions; }
bool jvmti_can_get_owned_monitor_info() const { return _jvmti_can_get_owned_monitor_info; }
// Cache DTrace flags
void cache_dtrace_flags();

View File

@ -102,7 +102,8 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, Dir
assert(is_initialized(), "Compiler thread must be initialized");
bool subsume_loads = SubsumeLoads;
bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables();
bool do_escape_analysis = DoEscapeAnalysis && !env->should_retain_local_variables()
&& !env->jvmti_can_get_owned_monitor_info();
bool eliminate_boxing = EliminateAutoBox;
while (!env->failing()) {

View File

@ -1202,6 +1202,7 @@ bool JvmtiExport::_can_post_method_entry = fals
bool JvmtiExport::_can_post_method_exit = false;
bool JvmtiExport::_can_pop_frame = false;
bool JvmtiExport::_can_force_early_return = false;
bool JvmtiExport::_can_get_owned_monitor_info = false;
bool JvmtiExport::_early_vmstart_recorded = false;

View File

@ -91,6 +91,7 @@ class JvmtiExport : public AllStatic {
JVMTI_SUPPORT_FLAG(can_force_early_return)
JVMTI_SUPPORT_FLAG(early_vmstart_recorded)
JVMTI_SUPPORT_FLAG(can_get_owned_monitor_info) // includes can_get_owned_monitor_stack_depth_info
friend class JvmtiEventControllerPrivate; // should only modify these flags
JVMTI_SUPPORT_FLAG(should_post_single_step)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -367,6 +367,8 @@ void JvmtiManageCapabilities::update() {
JvmtiExport::set_can_pop_frame(avail.can_pop_frame);
JvmtiExport::set_can_force_early_return(avail.can_force_early_return);
JvmtiExport::set_should_clean_up_heap_objects(avail.can_generate_breakpoint_events);
JvmtiExport::set_can_get_owned_monitor_info(avail.can_get_owned_monitor_info ||
avail.can_get_owned_monitor_stack_depth_info);
}
#ifndef PRODUCT

View File

@ -0,0 +1,327 @@
/*
* Copyright (c) 2019 SAP SE. 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 8230677
* @summary Test JVMTI's GetOwnedMonitorInfo with scalar replaced objects and eliminated locks on stack (optimizations based on escape analysis).
* @comment Without RFE 8227745 escape analysis needs to be switched off to pass the test. For the implementation of RFE 8227745 it serves as a regression test.
* @requires (vm.compMode != "Xcomp" & vm.compiler2.enabled)
* @library /test/lib
* @compile GetOwnedMonitorInfoWithEATest.java
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
* GetOwnedMonitorInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking -XX:-UseOptoBiasInlining
* GetOwnedMonitorInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
* GetOwnedMonitorInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
* GetOwnedMonitorInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
* GetOwnedMonitorInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
* GetOwnedMonitorInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
* GetOwnedMonitorInfoWithEATest
*/
import jdk.test.lib.Asserts;
public class GetOwnedMonitorInfoWithEATest {
public static final int COMPILE_THRESHOLD = 20000;
/**
* Native wrapper arround JVMTI's GetOwnedMonitorInfo().
* @param t The thread for which the owned monitors information should be retrieved.
* @param ownedMonitors Array filled in by the call with the objects associated
* with the monitors owned by the given thread.
* @param depths Per owned monitor the depth of the frame were it was locked.
* Filled in by the call
* @return Number of monitors owned by the given thread.
*/
public static native int getOwnedMonitorInfo(Thread t, Object[] ownedMonitors);
public static void main(String[] args) throws Exception {
new GetOwnedMonitorInfoWithEATest().runTest();
}
public void runTest() throws Exception {
new TestCase_1().run();
new TestCase_2().run();
}
public static abstract class TestCaseBase implements Runnable {
public long checkSum;
public boolean doLoop;
public volatile long loopCount;
public volatile boolean targetIsInLoop;
public void run() {
try {
msgHL("Executing test case " + getClass().getName());
warmUp();
runTest();
} catch (Exception e) {
Asserts.fail("Unexpected Exception", e);
}
}
public void warmUp() {
int callCount = COMPILE_THRESHOLD + 1000;
doLoop = true;
while (callCount-- > 0) {
dontinline_testMethod();
}
}
public abstract void runTest() throws Exception;
public abstract void dontinline_testMethod();
public long dontinline_endlessLoop() {
long cs = checkSum;
while (doLoop && loopCount-- > 0) {
targetIsInLoop = true;
checkSum += checkSum % ++cs;
}
loopCount = 3;
targetIsInLoop = false;
return checkSum;
}
public void waitUntilTargetThreadHasEnteredEndlessLoop() throws Exception {
while(!targetIsInLoop) {
msg("Target has not yet entered the loop. Sleep 200ms.");
try { Thread.sleep(200); } catch (InterruptedException e) { /*ignore */ }
}
msg("Target has entered the loop.");
}
public void terminateEndlessLoop() throws Exception {
msg("Terminate endless loop");
do {
doLoop = false;
} while(targetIsInLoop);
}
public void msg(String m) {
System.out.println();
System.out.println("### " + m);
System.out.println();
}
public void msgHL(String m) {
System.out.println();
System.out.println("#####################################################");
System.out.println("### " + m);
System.out.println("###");
System.out.println();
}
}
/**
* Starts target thread T and then queries monitor information for T using JVMTI's GetOwnedMonitorInfo().
* With escape analysis enabled the jit compiled method {@link #dontinline_testMethod()} has
* scalar replaced objects with eliminated (nested) locking in scope when the monitor
* information is retrieved. Effectively the objects escape through the JVMTI call. This works
* only with RFE 8227745. Without it escape analysis needs to be disabled.
*/
public static class TestCase_1 extends TestCaseBase {
public void runTest() throws Exception {
loopCount = 1L << 62; // endless loop
Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread");
t1.start();
try {
waitUntilTargetThreadHasEnteredEndlessLoop();
int expectedMonitorCount = 1;
int resultSize = expectedMonitorCount + 3;
Object[] ownedMonitors = new Object[resultSize];
msg("Get monitor info");
int monitorCount = getOwnedMonitorInfo(t1, ownedMonitors);
terminateEndlessLoop();
t1.join();
Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed");
msg("Monitor info:");
for (int i = 0; i < monitorCount; i++) {
System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null));
}
Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()");
Asserts.assertNotNull(ownedMonitors[0]);
Asserts.assertSame(ownedMonitors[0].getClass(), LockCls.class);
} finally {
terminateEndlessLoop();
t1.join();
}
}
public void dontinline_testMethod() {
LockCls l1 = new LockCls(); // to be scalar replaced
synchronized (l1) {
inlinedTestMethodWithNestedLocking(l1);
}
}
public void inlinedTestMethodWithNestedLocking(LockCls l1) {
synchronized (l1) { // nested
dontinline_endlessLoop();
}
}
}
/**
* Similar to {@link TestCase_1}. Additionally the target thread T has got eliminated locking
* for a synchronized method of a different type {@linkplain LockCls2}.
*/
public static class TestCase_2 extends TestCaseBase {
public void runTest() throws Exception {
loopCount = 1L << 62; // endless loop
Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread");
t1.start();
try {
waitUntilTargetThreadHasEnteredEndlessLoop();
int expectedMonitorCount = 2;
int resultSize = expectedMonitorCount + 3;
Object[] ownedMonitors = new Object[resultSize];
msg("Get monitor info");
int monitorCount = getOwnedMonitorInfo(t1, ownedMonitors);
terminateEndlessLoop();
t1.join();
Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed");
msg("Monitor info:");
for (int i = 0; i < monitorCount; i++) {
System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null));
}
Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()");
Asserts.assertNotNull(ownedMonitors[0]);
Asserts.assertSame(ownedMonitors[0].getClass(), LockCls2.class);
Asserts.assertNotNull(ownedMonitors[1]);
Asserts.assertSame(ownedMonitors[1].getClass(), LockCls.class);
} finally {
terminateEndlessLoop();
t1.join();
}
}
public void dontinline_testMethod() {
LockCls l1 = new LockCls();
synchronized (l1) {
inlinedTestMethodWithNestedLocking(l1);
}
}
public void inlinedTestMethodWithNestedLocking(LockCls l1) {
synchronized (l1) {
dontinline_testMethod2();
}
}
public void dontinline_testMethod2() {
// Call synchronized method. Receiver of the call will be scalar replaced,
// and locking will be eliminated. Here we use a different type.
new LockCls2().inline_synchronized_testMethod(this);
}
}
public static class LockCls {
}
public static class LockCls2 {
public synchronized void inline_synchronized_testMethod(TestCaseBase testCase) {
testCase.dontinline_endlessLoop();
}
}
}

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2019 SAP SE. 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 <string.h>
#include "jvmti.h"
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef JNI_ENV_ARG
#ifdef __cplusplus
#define JNI_ENV_ARG(x, y) y
#define JNI_ENV_PTR(x) x
#else
#define JNI_ENV_ARG(x,y) x, y
#define JNI_ENV_PTR(x) (*x)
#endif
#endif
#define FAILED -1
static jvmtiEnv *jvmti;
static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) {
char *errMsg;
jvmtiError result;
result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg);
if (result == JVMTI_ERROR_NONE) {
fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode);
(*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg);
} else {
fprintf(stderr, "%s (%d)\n", message, errCode);
}
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved) {
jint res;
JNIEnv *env;
res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env),
JNI_VERSION_9);
if (res != JNI_OK || env == NULL) {
fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res);
return JNI_ERR;
}
return JNI_VERSION_9;
}
static
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
jint res;
jvmtiError err;
jvmtiCapabilities caps;
printf("Agent_OnLoad started\n");
memset(&caps, 0, sizeof(caps));
res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
JVMTI_VERSION_9);
if (res != JNI_OK || jvmti == NULL) {
fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
return JNI_ERR;
}
caps.can_get_owned_monitor_info = 1;
err = (*jvmti)->AddCapabilities(jvmti, &caps);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"Agent_OnLoad: error in JVMTI AddCapabilities");
return JNI_ERR;
}
err = (*jvmti)->GetCapabilities(jvmti, &caps);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"Agent_OnLoad: error in JVMTI GetCapabilities");
return JNI_ERR;
}
if (!caps.can_get_owned_monitor_info) {
fprintf(stderr, "Warning: GetOwnedMonitorInfo is not implemented\n");
return JNI_ERR;
}
printf("Agent_OnLoad finished\n");
return JNI_OK;
}
JNIEXPORT jint JNICALL
Java_GetOwnedMonitorInfoWithEATest_getOwnedMonitorInfo(JNIEnv *env, jclass cls, jobject targetThread, jobjectArray resOwnedMonitors) {
jvmtiError err;
jvmtiThreadInfo threadInfo;
jint monitorCount;
jobject* monitors;
jint idx;
err = (*jvmti)->GetThreadInfo(jvmti, targetThread, &threadInfo);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"getOwnedMonitorsFor: error in JVMTI GetThreadInfo");
return FAILED;
}
err = (*jvmti)->GetOwnedMonitorInfo(jvmti, targetThread, &monitorCount, &monitors);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"getOwnedMonitorsFor: error in JVMTI GetOwnedMonitorInfo");
return FAILED;
}
printf("getOwnedMonitorsFor: %s owns %d monitor(s)\n", threadInfo.name, monitorCount);
for (idx = 0; idx < monitorCount; idx++) {
(*env)->SetObjectArrayElement(env, resOwnedMonitors, idx, monitors[idx]);
}
(*jvmti)->Deallocate(jvmti, (unsigned char *) monitors);
(*jvmti)->Deallocate(jvmti, (unsigned char *) threadInfo.name);
return monitorCount;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,330 @@
/*
* Copyright (c) 2019 SAP SE. 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 8230677
* @summary Test JVMTI's GetOwnedMonitorStackDepthInfo with scalar replaced objects and eliminated locks on stack (optimizations based on escape analysis).
* @comment Without RFE 8227745 escape analysis needs to be switched off to pass the test. For the implementation of RFE 8227745 it serves as a regression test.
* @requires (vm.compMode != "Xcomp" & vm.compiler2.enabled)
* @library /test/lib
* @compile GetOwnedMonitorStackDepthInfoWithEATest.java
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
* GetOwnedMonitorStackDepthInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking -XX:-UseOptoBiasInlining
* GetOwnedMonitorStackDepthInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
* GetOwnedMonitorStackDepthInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
* GetOwnedMonitorStackDepthInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
* GetOwnedMonitorStackDepthInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
* GetOwnedMonitorStackDepthInfoWithEATest
* @run main/othervm/native
* -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
* -XX:+UnlockDiagnosticVMOptions
* -Xms32m -Xmx32m
* -XX:CompileCommand=dontinline,*::dontinline_*
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:-TieredCompilation
* -Xbatch
* -XX:CICompilerCount=1
* -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
* GetOwnedMonitorStackDepthInfoWithEATest
*/
import jdk.test.lib.Asserts;
public class GetOwnedMonitorStackDepthInfoWithEATest {
public static final int COMPILE_THRESHOLD = 20000;
/**
* Native wrapper arround JVMTI's GetOwnedMonitorStackDepthInfo().
* @param t The thread for which the owned monitors information should be retrieved.
* @param ownedMonitors Array filled in by the call with the objects associated
* with the monitors owned by the given thread.
* @param depths Per owned monitor the depth of the frame were it was locked.
* Filled in by the call
* @return Number of monitors owned by the given thread.
*/
public static native int getOwnedMonitorStackDepthInfo(Thread t, Object[] ownedMonitors, int[] depths);
public static void main(String[] args) throws Exception {
new GetOwnedMonitorStackDepthInfoWithEATest().runTest();
}
public void runTest() throws Exception {
new TestCase_1().run();
new TestCase_2().run();
}
public static abstract class TestCaseBase implements Runnable {
public long checkSum;
public boolean doLoop;
public volatile long loopCount;
public volatile boolean targetIsInLoop;
public void run() {
try {
msgHL("Executing test case " + getClass().getName());
warmUp();
runTest();
} catch (Exception e) {
Asserts.fail("Unexpected Exception", e);
}
}
public void warmUp() {
int callCount = COMPILE_THRESHOLD + 1000;
doLoop = true;
while (callCount-- > 0) {
dontinline_testMethod();
}
}
public abstract void runTest() throws Exception;
public abstract void dontinline_testMethod();
public long dontinline_endlessLoop() {
long cs = checkSum;
while (doLoop && loopCount-- > 0) {
targetIsInLoop = true;
checkSum += checkSum % ++cs;
}
loopCount = 3;
targetIsInLoop = false;
return checkSum;
}
public void waitUntilTargetThreadHasEnteredEndlessLoop() throws Exception {
while(!targetIsInLoop) {
msg("Target has not yet entered the loop. Sleep 200ms.");
try { Thread.sleep(200); } catch (InterruptedException e) { /*ignore */ }
}
msg("Target has entered the loop.");
}
public void terminateEndlessLoop() throws Exception {
msg("Terminate endless loop");
do {
doLoop = false;
} while(targetIsInLoop);
}
public void msg(String m) {
System.out.println();
System.out.println("### " + m);
System.out.println();
}
public void msgHL(String m) {
System.out.println();
System.out.println("#####################################################");
System.out.println("### " + m);
System.out.println("###");
System.out.println();
}
}
/**
* Starts target thread T and then queries monitor information for T using JVMTI's GetOwnedMonitorStackDepthInfo().
* With escape analysis enabled the jit compiled method {@link #dontinline_testMethod()} has
* scalar replaced objects with eliminated (nested) locking in scope when the monitor
* information is retrieved. Effectively the objects escape through the JVMTI call. This works
* only with RFE 8227745. Without it escape analysis needs to be disabled.
*/
public static class TestCase_1 extends TestCaseBase {
public void runTest() throws Exception {
loopCount = 1L << 62; // endless loop
Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread");
try {
t1.start();
waitUntilTargetThreadHasEnteredEndlessLoop();
int expectedMonitorCount = 1;
int resultSize = expectedMonitorCount + 3;
Object[] ownedMonitors = new Object[resultSize];
int[] depths = new int[resultSize];
msg("Get monitor info");
int monitorCount = getOwnedMonitorStackDepthInfo(t1, ownedMonitors, depths);
Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed");
msg("Monitor info:");
for (int i = 0; i < monitorCount; i++) {
System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null) + " depth=" + depths[i]);
}
Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()");
Asserts.assertNotNull(ownedMonitors[0]);
Asserts.assertSame(ownedMonitors[0].getClass(), LockCls.class);
Asserts.assertEQ(depths[0], 1, "unexpected depth for owned monitor at index 0");
} finally {
terminateEndlessLoop();
t1.join();
}
}
public void dontinline_testMethod() {
LockCls l1 = new LockCls(); // to be scalar replaced
synchronized (l1) {
inlinedTestMethodWithNestedLocking(l1);
}
}
public void inlinedTestMethodWithNestedLocking(LockCls l1) {
synchronized (l1) { // nested
dontinline_endlessLoop();
}
}
}
/**
* Similar to {@link TestCase_1}. Additionally the target thread T has got eliminated locking
* for a synchronized method of a different type {@linkplain LockCls2}.
*/
public static class TestCase_2 extends TestCaseBase {
public void runTest() throws Exception {
loopCount = 1L << 62; // endless loop
Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread");
t1.start();
try {
waitUntilTargetThreadHasEnteredEndlessLoop();
int expectedMonitorCount = 2;
int resultSize = expectedMonitorCount + 3;
Object[] ownedMonitors = new Object[resultSize];
int[] depths = new int[resultSize];
msg("Get monitor info");
int monitorCount = getOwnedMonitorStackDepthInfo(t1, ownedMonitors, depths);
terminateEndlessLoop();
t1.join();
Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed");
msg("Monitor info:");
for (int i = 0; i < monitorCount; i++) {
System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null) + " depth=" + depths[i]);
}
Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()");
Asserts.assertNotNull(ownedMonitors[0]);
Asserts.assertSame(ownedMonitors[0].getClass(), LockCls2.class);
Asserts.assertEQ(depths[0], 1, "unexpected depth for owned monitor at index 0");
Asserts.assertNotNull(ownedMonitors[1]);
Asserts.assertSame(ownedMonitors[1].getClass(), LockCls.class);
Asserts.assertEQ(depths[1], 3, "unexpected depth for owned monitor at index 1");
} finally {
terminateEndlessLoop();
t1.join();
}
}
public void dontinline_testMethod() {
LockCls l1 = new LockCls();
synchronized (l1) {
inlinedTestMethodWithNestedLocking(l1);
}
}
public void inlinedTestMethodWithNestedLocking(LockCls l1) {
synchronized (l1) {
dontinline_testMethod2();
}
}
public void dontinline_testMethod2() {
// Call synchronized method. Receiver of the call will be scalar replaced,
// and locking will be eliminated. Here we use a different type.
new LockCls2().inline_synchronized_testMethod(this);
}
}
public static class LockCls {
}
public static class LockCls2 {
public synchronized void inline_synchronized_testMethod(TestCaseBase testCase) {
testCase.dontinline_endlessLoop();
}
}
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2019 SAP SE. 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 <string.h>
#include "jvmti.h"
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef JNI_ENV_ARG
#ifdef __cplusplus
#define JNI_ENV_ARG(x, y) y
#define JNI_ENV_PTR(x) x
#else
#define JNI_ENV_ARG(x,y) x, y
#define JNI_ENV_PTR(x) (*x)
#endif
#endif
#define FAILED -1
static jvmtiEnv *jvmti;
static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) {
char *errMsg;
jvmtiError result;
result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg);
if (result == JVMTI_ERROR_NONE) {
fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode);
(*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg);
} else {
fprintf(stderr, "%s (%d)\n", message, errCode);
}
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved) {
jint res;
JNIEnv *env;
res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env),
JNI_VERSION_9);
if (res != JNI_OK || env == NULL) {
fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res);
return JNI_ERR;
}
return JNI_VERSION_9;
}
static
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
jint res;
jvmtiError err;
jvmtiCapabilities caps;
printf("Agent_OnLoad started\n");
memset(&caps, 0, sizeof(caps));
res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
JVMTI_VERSION_9);
if (res != JNI_OK || jvmti == NULL) {
fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
return JNI_ERR;
}
caps.can_get_owned_monitor_stack_depth_info = 1;
err = (*jvmti)->AddCapabilities(jvmti, &caps);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"Agent_OnLoad: error in JVMTI AddCapabilities");
return JNI_ERR;
}
err = (*jvmti)->GetCapabilities(jvmti, &caps);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"Agent_OnLoad: error in JVMTI GetCapabilities");
return JNI_ERR;
}
if (!caps.can_get_owned_monitor_stack_depth_info) {
fprintf(stderr, "Warning: GetOwnedMonitorStackDepthInfo is not implemented\n");
return JNI_ERR;
}
printf("Agent_OnLoad finished\n");
return JNI_OK;
}
JNIEXPORT jint JNICALL
Java_GetOwnedMonitorStackDepthInfoWithEATest_getOwnedMonitorStackDepthInfo(JNIEnv *env, jclass cls, jobject targetThread, jobjectArray ownedMonitors, jintArray depths) {
jvmtiError err;
jvmtiThreadInfo threadInfo;
jint monitorCount;
jvmtiMonitorStackDepthInfo* stackDepthInfo;
jint* depthsPtr;
jint idx = 0;
err = (*jvmti)->GetThreadInfo(jvmti, targetThread, &threadInfo);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"getOwnedMonitorsFor: error in JVMTI GetThreadInfo");
return FAILED;
}
err = (*jvmti)->GetOwnedMonitorStackDepthInfo(jvmti, targetThread, &monitorCount, &stackDepthInfo);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err,
"getOwnedMonitorsFor: error in JVMTI GetOwnedMonitorStackDepthInfo");
return FAILED;
}
printf("getOwnedMonitorsFor: %s owns %d monitor(s)\n", threadInfo.name, monitorCount);
depthsPtr = (*env)->GetIntArrayElements(env, depths, NULL);
for (idx = 0; idx < monitorCount; idx++) {
(*env)->SetObjectArrayElement(env, ownedMonitors, idx, stackDepthInfo[idx].monitor);
depthsPtr[idx] = stackDepthInfo[idx].stack_depth;
}
(*env)->ReleaseIntArrayElements(env, depths, depthsPtr, 0);
(*jvmti)->Deallocate(jvmti, (unsigned char *) stackDepthInfo);
(*jvmti)->Deallocate(jvmti, (unsigned char *) threadInfo.name);
return monitorCount;
}
#ifdef __cplusplus
}
#endif