Path: blob/master/test/hotspot/jtreg/serviceability/jvmti/Heap/IterateHeapWithEscapeAnalysisEnabled.java
41153 views
/*1* Copyright (c) 2020 SAP SE. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @bug 823095626* @summary JVMTI agents can obtain references to not escaping objects using JVMTI Heap functions.27* Therefore optimizations based on escape analysis have to be reverted,28* i.e. scalar replaced objects need to be reallocated on the heap and objects with eliminated locking29* need to be relocked.30* @requires ((vm.compMode == "Xmixed") & vm.compiler2.enabled & vm.jvmti)31* @library /test/lib /test/hotspot/jtreg32* @build sun.hotspot.WhiteBox33* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox34* @compile IterateHeapWithEscapeAnalysisEnabled.java35*36* @comment BLOCK BEGIN EXCLUSIVE TESTCASES {37*38* The following test cases are executed in fresh VMs because they require that the39* capability can_tag_objects is not taken until dontinline_testMethod is jit compiled and40* an activation of the compiled version is on stack of the target thread.41*42* Without JDK-8227745 these test cases require that escape analysis is disabled at43* start-up because can_tag_objects can be taken lazily, potentially after loading an44* agent dynamically by means of the attach API. Disabling escape analysis and invalidating45* compiled methods does not help then because there may be compiled frames with ea-based46* optimizations on stack. Just like in this collection of test cases.47*48* @run main/othervm/native49* -agentlib:IterateHeapWithEscapeAnalysisEnabled50* -XX:+UnlockDiagnosticVMOptions51* -Xms256m -Xmx256m52* -XX:+PrintCompilation -XX:+PrintInlining53* -XX:+WhiteBoxAPI -Xbootclasspath/a:.54* -Xbatch55* -XX:CompileCommand=dontinline,*::dontinline_*56* -XX:+DoEscapeAnalysis57* IterateHeapWithEscapeAnalysisEnabled IterateOverReachableObjects58* @run main/othervm/native59* -agentlib:IterateHeapWithEscapeAnalysisEnabled60* -XX:+UnlockDiagnosticVMOptions61* -Xms256m -Xmx256m62* -XX:+PrintCompilation -XX:+PrintInlining63* -XX:+WhiteBoxAPI -Xbootclasspath/a:.64* -Xbatch65* -XX:CompileCommand=dontinline,*::dontinline_*66* -XX:+DoEscapeAnalysis67* IterateHeapWithEscapeAnalysisEnabled IterateOverHeap68* @run main/othervm/native69* -agentlib:IterateHeapWithEscapeAnalysisEnabled70* -XX:+UnlockDiagnosticVMOptions71* -Xms256m -Xmx256m72* -XX:+PrintCompilation -XX:+PrintInlining73* -XX:+WhiteBoxAPI -Xbootclasspath/a:.74* -Xbatch75* -XX:CompileCommand=dontinline,*::dontinline_*76* -XX:+DoEscapeAnalysis77* IterateHeapWithEscapeAnalysisEnabled IterateOverInstancesOfClass78* @run main/othervm/native79* -agentlib:IterateHeapWithEscapeAnalysisEnabled80* -XX:+UnlockDiagnosticVMOptions81* -Xms256m -Xmx256m82* -XX:+PrintCompilation -XX:+PrintInlining83* -XX:+WhiteBoxAPI -Xbootclasspath/a:.84* -Xbatch85* -XX:CompileCommand=dontinline,*::dontinline_*86* -XX:+DoEscapeAnalysis87* IterateHeapWithEscapeAnalysisEnabled FollowReferences88* @run main/othervm/native89* -agentlib:IterateHeapWithEscapeAnalysisEnabled90* -XX:+UnlockDiagnosticVMOptions91* -Xms256m -Xmx256m92* -XX:+PrintCompilation -XX:+PrintInlining93* -XX:+WhiteBoxAPI -Xbootclasspath/a:.94* -Xbatch95* -XX:CompileCommand=dontinline,*::dontinline_*96* -XX:+DoEscapeAnalysis97* IterateHeapWithEscapeAnalysisEnabled IterateThroughHeap98*99* @comment } BLOCK END EXCLUSIVE TESTCASES100*101* @comment BLOCK BEGIN NON EXCLUSIVE TESTCASES {102*103* @run main/othervm/native104* -agentlib:IterateHeapWithEscapeAnalysisEnabled105* -XX:+UnlockDiagnosticVMOptions106* -Xms256m -Xmx256m107* -XX:CompileCommand=dontinline,*::dontinline_*108* -XX:+PrintCompilation109* -XX:+PrintInlining110* -XX:+WhiteBoxAPI -Xbootclasspath/a:.111* -Xbatch112* -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking113* IterateHeapWithEscapeAnalysisEnabled114* @run main/othervm/native115* -agentlib:IterateHeapWithEscapeAnalysisEnabled116* -XX:+UnlockDiagnosticVMOptions117* -Xms256m -Xmx256m118* -XX:CompileCommand=dontinline,*::dontinline_*119* -XX:+PrintCompilation120* -XX:+PrintInlining121* -XX:+WhiteBoxAPI -Xbootclasspath/a:.122* -Xbatch123* -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking124* IterateHeapWithEscapeAnalysisEnabled125* @run main/othervm/native126* -agentlib:IterateHeapWithEscapeAnalysisEnabled127* -XX:+UnlockDiagnosticVMOptions128* -Xms256m -Xmx256m129* -XX:CompileCommand=dontinline,*::dontinline_*130* -XX:+PrintCompilation131* -XX:+PrintInlining132* -XX:+WhiteBoxAPI -Xbootclasspath/a:.133* -Xbatch134* -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking135* IterateHeapWithEscapeAnalysisEnabled136*137* @comment } BLOCK END NON EXCLUSIVE TESTCASES138*/139140import compiler.whitebox.CompilerWhiteBoxTest;141import jdk.test.lib.Asserts;142import sun.hotspot.WhiteBox;143144public class IterateHeapWithEscapeAnalysisEnabled {145146public static final WhiteBox WB = WhiteBox.getWhiteBox();147148public static final int COMPILE_THRESHOLD = CompilerWhiteBoxTest.THRESHOLD;149150public static native int jvmtiTagClass(Class<?> cls, long tag);151152// Methods to tag or count instances of a given class available in JVMTI153public static enum TaggingAndCountingMethods {154IterateOverReachableObjects,155IterateOverHeap,156IterateOverInstancesOfClass,157FollowReferences,158IterateThroughHeap159}160161public static native int acquireCanTagObjectsCapability();162public static native int registerMethod(TaggingAndCountingMethods m, String name);163public static native void agentTearDown();164165/**166* Count and tag instances of a given class.167* @param cls Used by the method {@link TaggingAndCountingMethods#IterateOverInstancesOfClass} as class to count and tag instances of.168* Ignored by other counting methods.169* @param clsTag Tag of the class to count and tag instances of. Used by all methods except170* {@link TaggingAndCountingMethods#IterateOverInstancesOfClass}171* @param instanceTag The tag to be set for selected instances.172* @param method JVMTI counting and tagging method to be used.173* @return The number of instances or -1 if the call fails.174*/175public static native int countAndTagInstancesOfClass(Class<?> cls, long clsTag, long instanceTag, TaggingAndCountingMethods method);176177/**178* Get all objects tagged with the given tag.179* @param tag The tag used to select objects.180* @param result Selected objects are copied into this array.181* @return -1 to indicated failure and 0 for success.182*/183public static native int getObjectsWithTag(long tag, Object[] result);184185public static void main(String[] args) throws Exception {186try {187new IterateHeapWithEscapeAnalysisEnabled().runTestCases(args);188} finally {189agentTearDown();190}191}192193public void runTestCases(String[] args) throws Exception {194// register various instance tagging and counting methods with agent195for (TaggingAndCountingMethods m : TaggingAndCountingMethods.values()) {196msg("register instance count method " + m.name());197int rc = registerMethod(m, m.name());198Asserts.assertGreaterThanOrEqual(rc, 0, "method " + m.name() + " is unknown to agent");199}200201if (args.length > 0) {202// EXCLUSIVE TEST CASES203// cant_tag_objects is acquired after warmup. Use given tagging/counting method.204new TestCase01(true, 100, TaggingAndCountingMethods.valueOf(args[0])).run();205} else {206// NON-EXCLUSIVE TEST CASES207// cant_tag_objects is acquired before test cases are run but still during live phase.208msgHL("Acquire capability can_tag_objects before first test case.");209int err = acquireCanTagObjectsCapability();210Asserts.assertEQ(0, err, "acquireCanTagObjectsCapability FAILED");211212// run test cases213for (TaggingAndCountingMethods m : TaggingAndCountingMethods.values()) {214new TestCase01(false, 200, m).run();215}216new TestCase02a(200).run();217new TestCase02b(300).run();218}219}220221static class ABBox {222public int aVal;223public int bVal;224public TestCaseBase testCase;225226public ABBox() { /* empty */ }227228public ABBox(TestCaseBase testCase) {229this.testCase = testCase;230}231232/**233* Increment {@link #aVal} and {@link #bVal} under lock. The method is supposed to234* be inlined into the test method and locking is supposed to be eliminated. After235* this object escaped to the JVMTI agent, the code with eliminated locking must236* not be used anymore.237*/238public synchronized void synchronizedSlowInc() {239aVal++;240testCase.waitingForCheck = true;241dontinline_waitForCheck(testCase);242testCase.waitingForCheck = false;243bVal++;244}245246public static void dontinline_waitForCheck(TestCaseBase testCase) {247if (testCase.warmUpDone) {248while(!testCase.checkingNow) {249try {250Thread.sleep(50);251} catch (InterruptedException e) { /*ign*/ }252}253}254}255256/**257* This method and incrementing {@link #aVal} and {@link #bVal} are synchronized.258* So {@link #aVal} and {@link #bVal} should always be equal. Unless the optimized version259* of {@link #synchronizedSlowInc()} without locking is still used after this object260* escaped to the JVMTI agent.261* @return262*/263public synchronized boolean check() {264return aVal == bVal;265}266}267268public static abstract class TestCaseBase implements Runnable {269public final long classTag;270public long instanceTag;271272public final Class<?> taggedClass;273274public long checkSum;275public long loopCount;276public volatile boolean doLoop;277public volatile boolean targetIsInLoop;278279public volatile boolean waitingForCheck;280public volatile boolean checkingNow;281282public boolean warmUpDone;283284public TestCaseBase(long classTag, Class<?> taggedClass) {285this.classTag = classTag;286this.taggedClass = taggedClass;287}288289public void setUp() {290// Tag the class of instances to be scalar replaced291msg("tagging " + taggedClass.getName() + " with tag " + classTag);292int err = jvmtiTagClass(taggedClass, classTag);293Asserts.assertEQ(0, err, "jvmtiTagClass FAILED");294}295296// to be overridden by test cases297abstract public void dontinline_testMethod();298299public void warmUp() {300msg("WarmUp: START");301int callCount = COMPILE_THRESHOLD + 1000;302doLoop = true;303while (callCount-- > 0) {304dontinline_testMethod();305}306warmUpDone = true;307msg("WarmUp: DONE");308}309310public Object dontinline_endlessLoop(Object argEscape) {311long cs = checkSum;312while (loopCount-- > 0 && doLoop) {313targetIsInLoop = true;314checkSum += checkSum % ++cs;315}316loopCount = 3;317targetIsInLoop = false;318return argEscape;319}320321public void waitUntilTargetThreadHasEnteredEndlessLoop() {322while(!targetIsInLoop) {323msg("Target has not yet entered the loop. Sleep 100ms.");324try { Thread.sleep(100); } catch (InterruptedException e) { /*ignore */ }325}326msg("Target has entered the loop.");327}328329public void terminateEndlessLoop() throws Exception {330msg("Terminate endless loop");331doLoop = false;332}333}334335/**336* Use JVMTI heap functions associated with the elements of {@link TaggingAndCountingMethods} to337* get a reference to an object allocated in {@link TestCase01#dontinline_testMethod()}. The338* allocation can be eliminated / scalar replaced. The test case can be run in two modes: (1)339* the capability can_tag_objects which is required to use the JVMTI heap functions is taken340* before the test case (2) the capability is taken after {@link TestCase01#dontinline_testMethod()}341* is compiled and the target thread has an activation of it on stack.342*/343public static class TestCase01 extends TestCaseBase {344345public volatile int testMethod_result;346public boolean acquireCanTagObjectsCapabilityAfterWarmup;347public TaggingAndCountingMethods taggingMethod;348349public TestCase01(boolean acquireCanTagObjectsCapabilityAfterWarmup, long classTag, TaggingAndCountingMethods taggingMethod) {350super(classTag, ABBox.class);351instanceTag = classTag + 1;352this.acquireCanTagObjectsCapabilityAfterWarmup = acquireCanTagObjectsCapabilityAfterWarmup;353this.taggingMethod = taggingMethod;354}355356@Override357public void setUp() {358if (!acquireCanTagObjectsCapabilityAfterWarmup) {359super.setUp();360}361}362363public void setUpAfterWarmUp() {364if (acquireCanTagObjectsCapabilityAfterWarmup) {365msg("Acquire capability can_tag_objects " + (warmUpDone ? "after" : "before") + " warmup.");366int err = acquireCanTagObjectsCapability();367Asserts.assertEQ(0, err, "acquireCanTagObjectsCapability FAILED");368super.setUp();369}370}371372public void run() {373try {374msgHL(getClass().getName() + ": test if object that may be scalar replaced is found using " + taggingMethod);375msg("The capability can_tag_object is acquired " + (acquireCanTagObjectsCapabilityAfterWarmup ? "AFTER" : "BEFORE")376+ " warmup.");377setUp();378warmUp();379WB.deflateIdleMonitors();380WB.fullGC(); // get rid of dead instances from previous test cases381runTest(taggingMethod);382} catch (Exception e) {383Asserts.fail("Unexpected Exception", e);384}385}386387public void runTest(TaggingAndCountingMethods m) throws Exception {388loopCount = 1L << 62; // endless loop389doLoop = true;390testMethod_result = 0;391Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread (" + getClass().getName() + ")");392try {393t1.start();394try {395waitUntilTargetThreadHasEnteredEndlessLoop();396setUpAfterWarmUp();397msg("count and tag instances of " + taggedClass.getName() + " with tag " + instanceTag + " using JVMTI " + m.name());398int count = countAndTagInstancesOfClass(taggedClass, classTag, instanceTag, m);399msg("Done. Count is " + count);400Asserts.assertGreaterThanOrEqual(count, 0, "countAndTagInstancesOfClass FAILED");401Asserts.assertEQ(count, 1, "unexpected number of instances");402403ABBox[] result = new ABBox[1];404msg("get instances tagged with " + instanceTag + ". The instances escape thereby.");405int err = getObjectsWithTag(instanceTag, result);406msg("Done.");407Asserts.assertEQ(0, err, "getObjectsWithTag FAILED");408409msg("change the now escaped instance' bVal");410ABBox abBox = result[0];411abBox.bVal = 3;412terminateEndlessLoop();413414msg("wait until target thread has set testMethod_result");415while (testMethod_result == 0) {416Thread.sleep(50);417}418msg("check if the modification of bVal is reflected in testMethod_result.");419Asserts.assertEQ(7, testMethod_result, " testMethod_result has wrong value");420msg("ok.");421} finally {422terminateEndlessLoop();423}424} finally {425t1.join();426}427}428429@Override430public void dontinline_testMethod() {431ABBox ab = new ABBox(); // can be scalar replaced432ab.aVal = 4;433ab.bVal = 2;434dontinline_endlessLoop(null); // JVMTI agent acquires reference to ab and changes bVal435testMethod_result = ab.aVal + ab.bVal;436}437}438439/**440* {@link #dontinline_testMethod()} creates an ArgEscape instance of {@link TestCaseBase#taggedClass} on stack.441* The jvmti agent tags all instances of this class using one of the {@link TaggingAndCountingMethods}. Then it gets the tagged442* instances using <code>GetObjectsWithTags()</code>. This is where the ArgEscape globally escapes.443* It happens at a location without eliminated locking but there is444* eliminated locking following, so the compiled frame must be deoptimized. This is checked by letting the agent call the445* synchronized method {@link ABBox#check()} on the escaped instance.446*/447public static class TestCase02a extends TestCaseBase {448449public long instanceTag;450451public TestCase02a(long classTag) {452super(classTag, ABBox.class);453instanceTag = classTag + 1;454}455456public void run() {457try {458msgHL(getClass().getName() + ": test if owning frame is deoptimized if ArgEscape escapes globally");459setUp();460warmUp();461for (TaggingAndCountingMethods m : TaggingAndCountingMethods.values()) {462msgHL(getClass().getName() + ": Tag and Get of ArgEscapes using " + m.name());463waitingForCheck = false;464checkingNow = false;465WB.deflateIdleMonitors();466WB.fullGC(); // get rid of dead instances from previous test cases467runTest(m);468}469} catch (Exception e) {470Asserts.fail("Unexpected Exception", e);471}472}473474public void runTest(TaggingAndCountingMethods m) throws Exception {475loopCount = 1L << 62; // endless loop476doLoop = true;477Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread (" + getClass().getName() + ")");478try {479t1.start();480try {481waitUntilTargetThreadHasEnteredEndlessLoop();482msg("count and tag instances of " + taggedClass.getName() + " with tag " + instanceTag + " using JVMTI " + m.name());483int count = countAndTagInstancesOfClass(taggedClass, classTag, instanceTag, m);484msg("Done. Count is " + count);485Asserts.assertGreaterThanOrEqual(count, 0, "countAndTagInstancesOfClass FAILED");486Asserts.assertEQ(count, 1, "unexpected number of instances");487} finally {488terminateEndlessLoop();489}490491ABBox[] result = new ABBox[1];492msg("get instances tagged with " + instanceTag);493int err = getObjectsWithTag(instanceTag, result);494msg("Done.");495Asserts.assertEQ(0, err, "getObjectsWithTag FAILED");496497ABBox abBoxArgEscape = result[0];498while (!waitingForCheck) {499Thread.yield();500}501msg("Check abBoxArgEscape's state is consistent");502checkingNow = true;503Asserts.assertTrue(abBoxArgEscape.check(), "Detected inconsistent state. abBoxArgEscape.aVal != abBoxArgEscape.bVal");504msg("Ok.");505} finally {506checkingNow = true;507t1.join();508}509}510511@Override512public void dontinline_testMethod() {513ABBox ab = new ABBox(this);514dontinline_endlessLoop(ab);515ab.synchronizedSlowInc();516}517}518519/**520* Like {@link TestCase02a}, with the exception that at the location in {@link #dontinline_testMethod()} where the521* ArgEscape escapes it is not referenced by a local variable.522*/523public static class TestCase02b extends TestCaseBase {524525public long instanceTag;526527public TestCase02b(long classTag) {528super(classTag, ABBox.class);529instanceTag = classTag + 1;530}531532public void run() {533try {534msgHL(getClass().getName() + ": test if owning frame is deoptimized if ArgEscape escapes globally");535setUp();536warmUp();537for (TaggingAndCountingMethods m : TaggingAndCountingMethods.values()) {538msgHL(getClass().getName() + ": Tag and Get of ArgEscapes using " + m.name());539waitingForCheck = false;540checkingNow = false;541WB.deflateIdleMonitors();542WB.fullGC(); // get rid of dead instances from previous test cases543runTest(m);544}545} catch (Exception e) {546Asserts.fail("Unexpected Exception", e);547}548}549550public void runTest(TaggingAndCountingMethods m) throws Exception {551loopCount = 1L << 62; // endless loop552doLoop = true;553Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread (" + getClass().getName() + ")");554try {555t1.start();556try {557waitUntilTargetThreadHasEnteredEndlessLoop();558msg("count and tag instances of " + taggedClass.getName() + " with tag " + instanceTag + " using JVMTI " + m.name());559int count = countAndTagInstancesOfClass(taggedClass, classTag, instanceTag, m);560msg("Done. Count is " + count);561Asserts.assertGreaterThanOrEqual(count, 0, "countAndTagInstancesOfClass FAILED");562Asserts.assertEQ(count, 1, "unexpected number of instances");563} finally {564terminateEndlessLoop();565}566567ABBox[] result = new ABBox[1];568msg("get instances tagged with " + instanceTag);569int err = getObjectsWithTag(instanceTag, result);570msg("Done.");571Asserts.assertEQ(0, err, "getObjectsWithTag FAILED");572573ABBox abBoxArgEscape = result[0];574while (!waitingForCheck) {575Thread.yield();576}577msg("Check abBoxArgEscape's state is consistent");578checkingNow = true;579Asserts.assertTrue(abBoxArgEscape.check(), "Detected inconsistent state. abBoxArgEscape.aVal != abBoxArgEscape.bVal");580msg("Ok.");581} finally {582checkingNow = true;583t1.join();584}585}586587@Override588public void dontinline_testMethod() {589// The new instance is an ArgEscape instance and escapes to the JVMTI agent590// while the target thread is in the call to dontinline_endlessLoop(). At this591// location there is no local variable that references the ArgEscape.592((ABBox) dontinline_endlessLoop(new ABBox(this))).synchronizedSlowInc();;593}594}595596public static void msg(String m) {597System.out.println();598System.out.println("### " + m);599System.out.println();600}601602public static void msgHL(String m) {603System.out.println(); System.out.println(); System.out.println();604System.out.println("#####################################################");605System.out.println("### " + m);606System.out.println("###");607System.out.println();608}609}610611612