1 /*
   2  * Copyright (c) 2015, 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  * @bug 8136421
  27  * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9" | os.simpleArch == "aarch64")
  28  * @library / /testlibrary /test/lib
  29  * @library ../common/patches
  30  * @modules java.base/jdk.internal.misc
  31  * @modules java.base/jdk.internal.org.objectweb.asm
  32  *          java.base/jdk.internal.org.objectweb.asm.tree
  33  *          jdk.vm.ci/jdk.vm.ci.hotspot
  34  *          jdk.vm.ci/jdk.vm.ci.code
  35  *          jdk.vm.ci/jdk.vm.ci.meta
  36  * @build jdk.vm.ci/jdk.vm.ci.hotspot.CompilerToVMHelper
  37  * @build compiler.jvmci.compilerToVM.GetNextStackFrameTest
  38  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI
  39  *                   compiler.jvmci.compilerToVM.GetNextStackFrameTest
  40  */
  41 
  42 package compiler.jvmci.compilerToVM;
  43 
  44 import compiler.jvmci.common.CTVMUtilities;
  45 import java.lang.reflect.Method;
  46 import jdk.vm.ci.hotspot.CompilerToVMHelper;
  47 import jdk.vm.ci.hotspot.HotSpotStackFrameReference;
  48 import jdk.vm.ci.meta.ResolvedJavaMethod;
  49 import jdk.test.lib.Asserts;
  50 
  51 public class GetNextStackFrameTest {
  52     private static final int RECURSION_AMOUNT = 3;
  53     private static final ResolvedJavaMethod REC_FRAME_METHOD;
  54     private static final ResolvedJavaMethod FRAME1_METHOD;
  55     private static final ResolvedJavaMethod FRAME2_METHOD;
  56     private static final ResolvedJavaMethod FRAME3_METHOD;
  57     private static final ResolvedJavaMethod FRAME4_METHOD;
  58     private static final ResolvedJavaMethod RUN_METHOD;
  59 
  60     static {
  61         Method method;
  62         try {
  63             Class<?> aClass = GetNextStackFrameTest.class;
  64             method = aClass.getDeclaredMethod("recursiveFrame", int.class);
  65             REC_FRAME_METHOD = CTVMUtilities.getResolvedMethod(method);
  66             method = aClass.getDeclaredMethod("frame1");
  67             FRAME1_METHOD = CTVMUtilities.getResolvedMethod(method);
  68             method = aClass.getDeclaredMethod("frame2");
  69             FRAME2_METHOD = CTVMUtilities.getResolvedMethod(method);
  70             method = aClass.getDeclaredMethod("frame3");
  71             FRAME3_METHOD = CTVMUtilities.getResolvedMethod(method);
  72             method = aClass.getDeclaredMethod("frame4");
  73             FRAME4_METHOD = CTVMUtilities.getResolvedMethod(method);
  74             method = Thread.class.getDeclaredMethod("run");
  75             RUN_METHOD = CTVMUtilities.getResolvedMethod(Thread.class, method);
  76         } catch (NoSuchMethodException e) {
  77             throw new Error("TEST BUG: can't find a test method : " + e, e);
  78         }
  79     }
  80 
  81     public static void main(String[] args) {
  82         new GetNextStackFrameTest().test();
  83     }
  84 
  85     private void test() {
  86         // Create new thread to get new clean stack
  87         Thread thread = new Thread(() -> recursiveFrame(RECURSION_AMOUNT));
  88         thread.start();
  89         try {
  90             thread.join();
  91         } catch (InterruptedException e) {
  92             throw new Error("Interrupted while waiting to join", e);
  93         }
  94     }
  95 
  96     // Helper methods for a longer stack
  97     private void recursiveFrame(int recursionAmount) {
  98         if (--recursionAmount != 0) {
  99             recursiveFrame(recursionAmount);
 100         } else {
 101             frame1();
 102         }
 103     }
 104 
 105     private void frame1() {
 106         frame2();
 107     }
 108 
 109     private void frame2() {
 110         frame3();
 111     }
 112 
 113     private void frame3() {
 114         frame4();
 115     }
 116 
 117     private void frame4() {
 118         check();
 119     }
 120 
 121     private void check() {
 122         findFirst();
 123         walkThrough();
 124         skipAll();
 125         findNextSkipped();
 126         findYourself();
 127     }
 128 
 129     /**
 130      * Finds the first topmost frame from the list of methods to search
 131      */
 132     private void findFirst() {
 133         checkNextFrameFor(null /* topmost frame */,
 134                 new ResolvedJavaMethod[]
 135                         {FRAME2_METHOD, FRAME3_METHOD, FRAME4_METHOD},
 136                 FRAME4_METHOD, 0);
 137     }
 138 
 139     /**
 140      * Walks through whole stack and checks that every frame could be found
 141      * while going down the stack till the end
 142      */
 143     private void walkThrough() {
 144         // Check that we would get a frame 4 starting from the topmost frame
 145         HotSpotStackFrameReference nextStackFrame = checkNextFrameFor(
 146                 null /* topmost frame */,
 147                 new ResolvedJavaMethod[] {FRAME4_METHOD},
 148                 FRAME4_METHOD, 0);
 149         // Check that we would get a frame 3 starting from frame 4 when we try
 150         // to search one of the next two frames
 151         nextStackFrame = checkNextFrameFor(nextStackFrame,
 152                 new ResolvedJavaMethod[] {FRAME3_METHOD,
 153                         FRAME2_METHOD},
 154                 FRAME3_METHOD, 0);
 155         // Check that we would get a frame 1
 156         nextStackFrame = checkNextFrameFor(nextStackFrame,
 157                 new ResolvedJavaMethod[] {FRAME1_METHOD},
 158                 FRAME1_METHOD, 0);
 159         // Check that we would skip (RECURSION_AMOUNT - 1) methods and find a
 160         // recursionFrame starting from frame 1
 161         nextStackFrame = checkNextFrameFor(nextStackFrame,
 162                 new ResolvedJavaMethod[] {REC_FRAME_METHOD},
 163                 REC_FRAME_METHOD, RECURSION_AMOUNT - 1);
 164         // Check that we would get a Thread::run method frame;
 165         nextStackFrame = checkNextFrameFor(nextStackFrame,
 166                 new ResolvedJavaMethod[] {RUN_METHOD},
 167                 RUN_METHOD, 0);
 168         // Check that there are no more frames after thread's run method
 169         nextStackFrame = CompilerToVMHelper.getNextStackFrame(nextStackFrame,
 170                 null /* any */, 0);
 171         Asserts.assertNull(nextStackFrame,
 172                 "Found stack frame after Thread::run");
 173     }
 174 
 175     /**
 176      * Skips all frames to get null at the end of the stack
 177      */
 178     private void skipAll() {
 179         // Skip all frames (stack size) + 2 (getNextStackFrame() itself
 180         // and from CompilerToVMHelper)
 181         int initialSkip = Thread.currentThread().getStackTrace().length + 2;
 182         HotSpotStackFrameReference nextStackFrame = CompilerToVMHelper
 183                 .getNextStackFrame(null /* topmost frame */, null /* any */,
 184                         initialSkip);
 185         Asserts.assertNull(nextStackFrame, "Unexpected frame");
 186     }
 187 
 188     /**
 189      * Search for any frame skipping one frame
 190      */
 191     private void findNextSkipped() {
 192         // Get frame 4
 193         HotSpotStackFrameReference nextStackFrame = CompilerToVMHelper
 194                 .getNextStackFrame(null /* topmost frame */,
 195                         new ResolvedJavaMethod[] {FRAME4_METHOD}, 0);
 196         // Get frame 2 by skipping one method starting from frame 4
 197         checkNextFrameFor(nextStackFrame, null /* any */,
 198                 FRAME2_METHOD , 1 /* skip one */);
 199     }
 200 
 201     /**
 202      * Finds test method in the stack
 203      */
 204     private void findYourself() {
 205         Method method;
 206         Class<?> aClass = CompilerToVMHelper.CompilerToVMClass();
 207         try {
 208             method = aClass.getDeclaredMethod(
 209                     "getNextStackFrame",
 210                     HotSpotStackFrameReference.class,
 211                     ResolvedJavaMethod[].class,
 212                     int.class);
 213         } catch (NoSuchMethodException e) {
 214             throw new Error("TEST BUG: can't find getNextStackFrame : " + e, e);
 215         }
 216         ResolvedJavaMethod self
 217                 = CTVMUtilities.getResolvedMethod(aClass, method);
 218         checkNextFrameFor(null /* topmost frame */, null /* any */, self, 0);
 219     }
 220 
 221     /**
 222      * Searches next frame and checks that it equals to expected
 223      *
 224      * @param currentFrame  start frame to search from
 225      * @param searchMethods a list of methods to search
 226      * @param expected      expected frame
 227      * @param skip          amount of frames to be skipped
 228      * @return frame reference
 229      */
 230     private HotSpotStackFrameReference checkNextFrameFor(
 231             HotSpotStackFrameReference currentFrame,
 232             ResolvedJavaMethod[] searchMethods,
 233             ResolvedJavaMethod expected,
 234             int skip) {
 235         HotSpotStackFrameReference nextStackFrame = CompilerToVMHelper
 236                 .getNextStackFrame(currentFrame, searchMethods, skip);
 237         Asserts.assertNotNull(nextStackFrame);
 238         Asserts.assertTrue(nextStackFrame.isMethod(expected),
 239                 "Unexpected next frame: " + nextStackFrame
 240                         + " from current frame: " + currentFrame);
 241         return nextStackFrame;
 242     }
 243 }