1 /*
  2  * Copyright (c) 2003, 2022, Oracle and/or its affiliates. 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  *
 27  * @summary converted from VM Testbase nsk/jvmti/GetFrameCount/framecnt001.
 28  * VM Testbase keywords: [quick, jpda, jvmti, noras]
 29  * VM Testbase readme:
 30  * DESCRIPTION
 31  *     The test exercise JVMTI function GetFrameCount.
 32  *     The function is tested for current thread, other thread. For platform and virtual threads.
 33  * COMMENTS
 34  *     Ported from JVMDI.
 35  *
 36  * @requires vm.continuations
 37  * @library /test/lib
 38  * @compile --enable-preview -source ${jdk.version} framecnt01.java
 39  * @run main/othervm/native --enable-preview -agentlib:framecnt01 framecnt01
 40  */
 41 
 42 import java.util.concurrent.locks.LockSupport;
 43 
 44 public class framecnt01 {
 45 
 46     native static boolean checkFrames0(Thread thread, boolean shouldSuspend, int expected);
 47 
 48     static void checkFrames(Thread thread, boolean shouldSuspend, int expected) {
 49         if(!checkFrames0(thread, shouldSuspend, expected)) {
 50             throw new RuntimeException("Check failed for " + thread + " " + shouldSuspend + " " + expected);
 51         }
 52     }
 53 
 54     static {
 55         System.loadLibrary("framecnt01");
 56     }
 57     static volatile boolean vThread1Started = false;
 58     static volatile boolean pThread1Started = false;
 59 
 60     public static void main(String args[]) throws Exception {
 61 
 62         // Test GetFrameCount on virtual live thread
 63         Thread vThread = Thread.ofVirtual().name("VirtualThread-Live").start(() -> {
 64            checkFrames(Thread.currentThread(), false, 9);
 65         });
 66         vThread.join();
 67 
 68         // Test GetFrameCount on virtual frozen thread
 69         Thread vThread1 = Thread.ofVirtual().name("VirtualThread-Frozen").start(() -> {
 70             vThread1Started = true;
 71             LockSupport.park();
 72         });
 73         while (!vThread1Started) {
 74             Thread.sleep(1);
 75         }
 76         // Let vthread1 to park
 77         while(vThread1.getState() != Thread.State.WAITING) {
 78             Thread.sleep(1);
 79         }
 80 
 81         // this is too fragile, implementation can change at any time.
 82         checkFrames(vThread1, false, 14);
 83         LockSupport.unpark(vThread1);
 84         vThread1.join();
 85 
 86         // Test GetFrameCount on live platform thread
 87         Thread pThread = Thread.ofPlatform().name("PlatformThread-Live").start(() -> {
 88             checkFrames(Thread.currentThread(), false, 5);
 89         });
 90         pThread.join();
 91 
 92         // Test GetFrameCount on parked platform thread
 93         Thread pThread1 = Thread.ofPlatform().name("PlatformThread-Parked").start(() -> {
 94                 pThread1Started = true;
 95                 LockSupport.park();
 96         });
 97         while (!pThread1Started) {
 98             Thread.sleep(1);
 99         }
100 
101         while(pThread1.getState() != Thread.State.WAITING) {
102             Thread.sleep(1);
103         }
104         checkFrames(pThread1, false, 5);
105         LockSupport.unpark(pThread1);
106         pThread1.join();
107 
108 
109         // Test GetFrameCount on some depth stack fixed by sync
110         FixedDepthThread.checkFrameCount(0);
111         FixedDepthThread.checkFrameCount(500);
112     }
113 }
114 
115 class FixedDepthThread implements Runnable {
116     int depth;
117     Object startedFlag;
118     Object checkFlag;
119     Thread thread;
120 
121     // Each stack has 2 frames additional to expected depth
122     // 0: FixedDepthThread: run()V
123     // 1: java/lang/Thread: run()V
124     static final int ADDITIONAL_STACK_COUNT = 2;
125 
126     private FixedDepthThread(String name, int depth, Object checkFlag) {
127         this.thread = Thread.ofPlatform().name(name).unstarted(this);
128         this.depth = depth;
129         this.startedFlag = new Object();
130         this.checkFlag = checkFlag;
131     }
132 
133     private void startAndWait() {
134         synchronized(startedFlag) {
135             thread.start();
136             try {
137                 startedFlag.wait();
138 
139             } catch(InterruptedException e) {}
140 
141         }
142     }
143 
144     public void run() {
145         if (depth > 0) {
146             depth--;
147             run();
148         }
149         synchronized(startedFlag) {
150             startedFlag.notify();  // let main thread know that all frames are in place
151         }
152         synchronized(checkFlag) {  // wait for the check done
153         }
154     }
155 
156     static void checkFrameCount(int depth) {
157         final Object checkFlag = new Object();
158         FixedDepthThread fixedDepthThread = new FixedDepthThread("FixedDepthThread-" + depth, depth, checkFlag);
159         synchronized(checkFlag) {
160             fixedDepthThread.startAndWait();
161             framecnt01.checkFrames(fixedDepthThread.thread, false, depth + ADDITIONAL_STACK_COUNT);
162             framecnt01.checkFrames(fixedDepthThread.thread, true, depth + ADDITIONAL_STACK_COUNT);
163         }
164     }
165 }