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         Thread.sleep(100);
 78 
 79         // this is too fragile, implementation can change at any time.
 80         checkFrames(vThread1, false, 14);
 81         LockSupport.unpark(vThread1);
 82         vThread1.join();
 83 
 84         // Test GetFrameCount on live platform thread
 85         Thread pThread = Thread.ofPlatform().name("PlatformThread-Live").start(() -> {
 86             checkFrames(Thread.currentThread(), false, 5);
 87         });
 88         pThread.join();
 89 
 90         // Test GetFrameCount on parked platform thread
 91         Thread pThread1 = Thread.ofPlatform().name("PlatformThread-Parked").start(() -> {
 92                 pThread1Started = true;
 93                 LockSupport.park();
 94         });
 95         while (!pThread1Started) {
 96             Thread.sleep(1);
 97         }
 98         Thread.sleep(10);
 99         checkFrames(pThread1, false, 5);
100         LockSupport.unpark(pThread1);
101         pThread1.join();
102 
103 
104         // Test GetFrameCount on some depth stack fixed by sync
105         FixedDepthThread.checkFrameCount(0);
106         FixedDepthThread.checkFrameCount(500);
107     }
108 }
109 
110 class FixedDepthThread implements Runnable {
111     int depth;
112     Object startedFlag;
113     Object checkFlag;
114     Thread thread;
115 
116     // Each stack has 2 frames additional to expected depth
117     // 0: FixedDepthThread: run()V
118     // 1: java/lang/Thread: run()V
119     static final int ADDITIONAL_STACK_COUNT = 2;
120 
121     private FixedDepthThread(String name, int depth, Object checkFlag) {
122         this.thread = Thread.ofPlatform().name(name).unstarted(this);
123         this.depth = depth;
124         this.startedFlag = new Object();
125         this.checkFlag = checkFlag;
126     }
127 
128     private void startAndWait() {
129         synchronized(startedFlag) {
130             thread.start();
131             try {
132                 startedFlag.wait();
133 
134             } catch(InterruptedException e) {}
135 
136         }
137     }
138 
139     public void run() {
140         if (depth > 0) {
141             depth--;
142             run();
143         }
144         synchronized(startedFlag) {
145             startedFlag.notify();  // let main thread know that all frames are in place
146         }
147         synchronized(checkFlag) {  // wait for the check done
148         }
149     }
150 
151     static void checkFrameCount(int depth) {
152         final Object checkFlag = new Object();
153         FixedDepthThread fixedDepthThread = new FixedDepthThread("FixedDepthThread-" + depth, depth, checkFlag);
154         synchronized(checkFlag) {
155             fixedDepthThread.startAndWait();
156             framecnt01.checkFrames(fixedDepthThread.thread, false, depth + ADDITIONAL_STACK_COUNT);
157             framecnt01.checkFrames(fixedDepthThread.thread, true, depth + ADDITIONAL_STACK_COUNT);
158         }
159     }
160 }