1 /*
2  * Copyright (c) 2019 SAP SE. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /**
25  * @test
26  * @bug 8230677
27  * @summary Test JVMTI's GetOwnedMonitorStackDepthInfo with scalar replaced objects and eliminated locks on stack (optimizations based on escape analysis).
28  * @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.
29  * @requires (vm.compMode != "Xcomp" & vm.compiler2.enabled)
30  * @library /test/lib
31  * @compile GetOwnedMonitorStackDepthInfoWithEATest.java
32  * @run main/othervm/native
33  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
34  *                  -XX:+UnlockDiagnosticVMOptions
35  *                  -Xms128m -Xmx128m
36  *                  -XX:CompileCommand=dontinline,*::dontinline_*
37  *                  -XX:+PrintCompilation
38  *                  -XX:+PrintInlining
39  *                  -XX:-TieredCompilation
40  *                  -Xbatch
41  *                  -XX:CICompilerCount=1
42  *                  -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
43  *                  GetOwnedMonitorStackDepthInfoWithEATest
44  * @run main/othervm/native
45  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
46  *                  -XX:+UnlockDiagnosticVMOptions
47  *                  -Xms128m -Xmx128m
48  *                  -XX:CompileCommand=dontinline,*::dontinline_*
49  *                  -XX:+PrintCompilation
50  *                  -XX:+PrintInlining
51  *                  -XX:-TieredCompilation
52  *                  -Xbatch
53  *                  -XX:CICompilerCount=1
54  *                  -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking -XX:-UseOptoBiasInlining
55  *                  GetOwnedMonitorStackDepthInfoWithEATest
56  * @run main/othervm/native
57  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
58  *                  -XX:+UnlockDiagnosticVMOptions
59  *                  -Xms128m -Xmx128m
60  *                  -XX:CompileCommand=dontinline,*::dontinline_*
61  *                  -XX:+PrintCompilation
62  *                  -XX:+PrintInlining
63  *                  -XX:-TieredCompilation
64  *                  -Xbatch
65  *                  -XX:CICompilerCount=1
66  *                  -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
67  *                  GetOwnedMonitorStackDepthInfoWithEATest
68  * @run main/othervm/native
69  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
70  *                  -XX:+UnlockDiagnosticVMOptions
71  *                  -Xms128m -Xmx128m
72  *                  -XX:CompileCommand=dontinline,*::dontinline_*
73  *                  -XX:+PrintCompilation
74  *                  -XX:+PrintInlining
75  *                  -XX:-TieredCompilation
76  *                  -Xbatch
77  *                  -XX:CICompilerCount=1
78  *                  -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:+UseBiasedLocking
79  *                  GetOwnedMonitorStackDepthInfoWithEATest
80  * @run main/othervm/native
81  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
82  *                  -XX:+UnlockDiagnosticVMOptions
83  *                  -Xms128m -Xmx128m
84  *                  -XX:CompileCommand=dontinline,*::dontinline_*
85  *                  -XX:+PrintCompilation
86  *                  -XX:+PrintInlining
87  *                  -XX:-TieredCompilation
88  *                  -Xbatch
89  *                  -XX:CICompilerCount=1
90  *                  -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
91  *                  GetOwnedMonitorStackDepthInfoWithEATest
92  * @run main/othervm/native
93  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
94  *                  -XX:+UnlockDiagnosticVMOptions
95  *                  -Xms128m -Xmx128m
96  *                  -XX:CompileCommand=dontinline,*::dontinline_*
97  *                  -XX:+PrintCompilation
98  *                  -XX:+PrintInlining
99  *                  -XX:-TieredCompilation
100  *                  -Xbatch
101  *                  -XX:CICompilerCount=1
102  *                  -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
103  *                  GetOwnedMonitorStackDepthInfoWithEATest
104  * @run main/othervm/native
105  *                  -agentlib:GetOwnedMonitorStackDepthInfoWithEATest
106  *                  -XX:+UnlockDiagnosticVMOptions
107  *                  -Xms128m -Xmx128m
108  *                  -XX:CompileCommand=dontinline,*::dontinline_*
109  *                  -XX:+PrintCompilation
110  *                  -XX:+PrintInlining
111  *                  -XX:-TieredCompilation
112  *                  -Xbatch
113  *                  -XX:CICompilerCount=1
114  *                  -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks -XX:-UseBiasedLocking
115  *                  GetOwnedMonitorStackDepthInfoWithEATest
116  */
117 
118 import jdk.test.lib.Asserts;
119 
120 public class GetOwnedMonitorStackDepthInfoWithEATest {
121 
122     public static final int COMPILE_THRESHOLD = 20000;
123 
124     /**
125      * Native wrapper arround JVMTI's GetOwnedMonitorStackDepthInfo().
126      * @param t The thread for which the owned monitors information should be retrieved.
127      * @param ownedMonitors Array filled in by the call with the objects associated
128      *        with the monitors owned by the given thread.
129      * @param depths Per owned monitor the depth of the frame were it was locked.
130      *        Filled in by the call
131      * @return Number of monitors owned by the given thread.
132      */
getOwnedMonitorStackDepthInfo(Thread t, Object[] ownedMonitors, int[] depths)133     public static native int getOwnedMonitorStackDepthInfo(Thread t, Object[] ownedMonitors, int[] depths);
134 
main(String[] args)135     public static void main(String[] args) throws Exception {
136         new GetOwnedMonitorStackDepthInfoWithEATest().runTest();
137     }
138 
runTest()139     public void runTest() throws Exception {
140         new TestCase_1().run();
141         new TestCase_2().run();
142     }
143 
144     public static abstract class TestCaseBase implements Runnable {
145 
146         public long checkSum;
147         public boolean doLoop;
148         public volatile long loopCount;
149         public volatile boolean targetIsInLoop;
150 
run()151         public void run() {
152             try {
153                 msgHL("Executing test case " + getClass().getName());
154                 warmUp();
155                 runTest();
156             } catch (Exception e) {
157                 Asserts.fail("Unexpected Exception", e);
158             }
159         }
160 
warmUp()161         public void warmUp() {
162             int callCount = COMPILE_THRESHOLD + 1000;
163             doLoop = true;
164             while (callCount-- > 0) {
165                 dontinline_testMethod();
166             }
167         }
168 
runTest()169         public abstract void runTest() throws Exception;
dontinline_testMethod()170         public abstract void dontinline_testMethod();
171 
dontinline_endlessLoop()172         public long dontinline_endlessLoop() {
173             long cs = checkSum;
174             while (doLoop && loopCount-- > 0) {
175                 targetIsInLoop = true;
176                 checkSum += checkSum % ++cs;
177             }
178             loopCount = 3;
179             targetIsInLoop = false;
180             return checkSum;
181         }
182 
waitUntilTargetThreadHasEnteredEndlessLoop()183         public void waitUntilTargetThreadHasEnteredEndlessLoop() throws Exception {
184             while(!targetIsInLoop) {
185                 msg("Target has not yet entered the loop. Sleep 200ms.");
186                 try { Thread.sleep(200); } catch (InterruptedException e) { /*ignore */ }
187             }
188             msg("Target has entered the loop.");
189         }
190 
terminateEndlessLoop()191         public void terminateEndlessLoop() throws Exception {
192             msg("Terminate endless loop");
193             do {
194                 doLoop = false;
195             } while(targetIsInLoop);
196         }
197 
msg(String m)198         public void msg(String m) {
199             System.out.println();
200             System.out.println("### " + m);
201             System.out.println();
202         }
203 
msgHL(String m)204         public void msgHL(String m) {
205             System.out.println();
206             System.out.println("#####################################################");
207             System.out.println("### " + m);
208             System.out.println("###");
209             System.out.println();
210         }
211     }
212 
213     /**
214      * Starts target thread T and then queries monitor information for T using JVMTI's GetOwnedMonitorStackDepthInfo().
215      * With escape analysis enabled the jit compiled method {@link #dontinline_testMethod()} has
216      * scalar replaced objects with eliminated (nested) locking in scope when the monitor
217      * information is retrieved. Effectively the objects escape through the JVMTI call. This works
218      * only with RFE 8227745. Without it escape analysis needs to be disabled.
219      */
220     public static class TestCase_1 extends TestCaseBase {
221 
runTest()222         public void runTest() throws Exception {
223             loopCount = 1L << 62; // endless loop
224             Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread");
225             try {
226                 t1.start();
227                 waitUntilTargetThreadHasEnteredEndlessLoop();
228                 int expectedMonitorCount = 1;
229                 int resultSize = expectedMonitorCount + 3;
230                 Object[] ownedMonitors = new Object[resultSize];
231                 int[]    depths = new int[resultSize];
232                 msg("Get monitor info");
233                 int monitorCount = getOwnedMonitorStackDepthInfo(t1, ownedMonitors, depths);
234                 Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed");
235                 msg("Monitor info:");
236                 for (int i = 0; i < monitorCount; i++) {
237                     System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null) + " depth=" + depths[i]);
238                 }
239                 Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()");
240                 Asserts.assertNotNull(ownedMonitors[0]);
241                 Asserts.assertSame(ownedMonitors[0].getClass(), LockCls.class);
242                 Asserts.assertEQ(depths[0], 1, "unexpected depth for owned monitor at index 0");
243             } finally {
244                 terminateEndlessLoop();
245                 t1.join();
246             }
247         }
248 
dontinline_testMethod()249         public void dontinline_testMethod() {
250             LockCls l1 = new LockCls();        // to be scalar replaced
251             synchronized (l1) {
252                 inlinedTestMethodWithNestedLocking(l1);
253             }
254         }
255 
inlinedTestMethodWithNestedLocking(LockCls l1)256         public void inlinedTestMethodWithNestedLocking(LockCls l1) {
257             synchronized (l1) {              // nested
258                 dontinline_endlessLoop();
259             }
260         }
261     }
262 
263     /**
264      * Similar to {@link TestCase_1}. Additionally the target thread T has got eliminated locking
265      * for a synchronized method of a different type {@linkplain LockCls2}.
266      */
267     public static class TestCase_2 extends TestCaseBase {
268 
runTest()269         public void runTest() throws Exception {
270             loopCount = 1L << 62; // endless loop
271             Thread t1 = new Thread(() -> dontinline_testMethod(), "Target Thread");
272             t1.start();
273             try {
274                 waitUntilTargetThreadHasEnteredEndlessLoop();
275                 int expectedMonitorCount = 2;
276                 int resultSize = expectedMonitorCount + 3;
277                 Object[] ownedMonitors = new Object[resultSize];
278                 int[]    depths = new int[resultSize];
279                 msg("Get monitor info");
280                 int monitorCount = getOwnedMonitorStackDepthInfo(t1, ownedMonitors, depths);
281                 terminateEndlessLoop();
282                 t1.join();
283                 Asserts.assertGreaterThanOrEqual(monitorCount, 0, "getOwnedMonitorsFor() call failed");
284                 msg("Monitor info:");
285                 for (int i = 0; i < monitorCount; i++) {
286                     System.out.println(i + ": cls=" + (ownedMonitors[i] != null ? ownedMonitors[i].getClass() : null) + " depth=" + depths[i]);
287                 }
288                 Asserts.assertEQ(monitorCount, expectedMonitorCount, "unexpected monitor count returned by getOwnedMonitorsFor()");
289                 Asserts.assertNotNull(ownedMonitors[0]);
290                 Asserts.assertSame(ownedMonitors[0].getClass(), LockCls2.class);
291                 Asserts.assertEQ(depths[0], 1, "unexpected depth for owned monitor at index 0");
292 
293                 Asserts.assertNotNull(ownedMonitors[1]);
294                 Asserts.assertSame(ownedMonitors[1].getClass(), LockCls.class);
295                 Asserts.assertEQ(depths[1], 3, "unexpected depth for owned monitor at index 1");
296             } finally {
297                 terminateEndlessLoop();
298                 t1.join();
299             }
300         }
301 
dontinline_testMethod()302         public void dontinline_testMethod() {
303             LockCls l1 = new LockCls();
304             synchronized (l1) {
305                 inlinedTestMethodWithNestedLocking(l1);
306             }
307         }
308 
inlinedTestMethodWithNestedLocking(LockCls l1)309         public void inlinedTestMethodWithNestedLocking(LockCls l1) {
310             synchronized (l1) {
311                 dontinline_testMethod2();
312             }
313         }
314 
dontinline_testMethod2()315         public void dontinline_testMethod2() {
316             // Call synchronized method. Receiver of the call will be scalar replaced,
317             // and locking will be eliminated. Here we use a different type.
318             new LockCls2().inline_synchronized_testMethod(this);
319         }
320     }
321 
322     public static class LockCls {
323     }
324 
325     public static class LockCls2 {
inline_synchronized_testMethod(TestCaseBase testCase)326         public synchronized void inline_synchronized_testMethod(TestCaseBase testCase) {
327             testCase.dontinline_endlessLoop();
328         }
329     }
330 }
331