hotspot/test/compiler/jvmci/compilerToVM/GetNextStackFrameTest.java
author twisti
Thu, 08 Oct 2015 12:49:30 -1000
changeset 33160 c59f1676d27e
child 33730 30e064828045
child 33632 038347770a9e
permissions -rw-r--r--
8136421: JEP 243: Java-Level JVM Compiler Interface Reviewed-by: ihse, alanb, roland, coleenp, iveresov, kvn, kbarrett

/*
 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 8136421
 * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9") & os.arch != "aarch64"
 * @library / /testlibrary /../../test/lib
 * @compile ../common/CompilerToVMHelper.java
 * @run main ClassFileInstaller
 *      jdk.vm.ci.hotspot.CompilerToVMHelper
 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockExperimentalVMOptions
 *      -XX:+EnableJVMCI compiler.jvmci.compilerToVM.GetNextStackFrameTest
 */

package compiler.jvmci.compilerToVM;

import compiler.jvmci.common.CTVMUtilities;
import java.lang.reflect.Method;
import jdk.vm.ci.hotspot.CompilerToVM;
import jdk.vm.ci.hotspot.CompilerToVMHelper;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethodImpl;
import jdk.vm.ci.hotspot.HotSpotStackFrameReference;
import jdk.test.lib.Asserts;

public class GetNextStackFrameTest {
    private static final int RECURSION_AMOUNT = 3;
    private static final HotSpotResolvedJavaMethodImpl REC_FRAME_METHOD;
    private static final HotSpotResolvedJavaMethodImpl FRAME1_METHOD;
    private static final HotSpotResolvedJavaMethodImpl FRAME2_METHOD;
    private static final HotSpotResolvedJavaMethodImpl FRAME3_METHOD;
    private static final HotSpotResolvedJavaMethodImpl FRAME4_METHOD;
    private static final HotSpotResolvedJavaMethodImpl RUN_METHOD;

    static {
        Method method;
        try {
            Class<?> aClass = GetNextStackFrameTest.class;
            method = aClass.getDeclaredMethod("recursiveFrame", int.class);
            REC_FRAME_METHOD = CTVMUtilities.getResolvedMethod(method);
            method = aClass.getDeclaredMethod("frame1");
            FRAME1_METHOD = CTVMUtilities.getResolvedMethod(method);
            method = aClass.getDeclaredMethod("frame2");
            FRAME2_METHOD = CTVMUtilities.getResolvedMethod(method);
            method = aClass.getDeclaredMethod("frame3");
            FRAME3_METHOD = CTVMUtilities.getResolvedMethod(method);
            method = aClass.getDeclaredMethod("frame4");
            FRAME4_METHOD = CTVMUtilities.getResolvedMethod(method);
            method = Thread.class.getDeclaredMethod("run");
            RUN_METHOD = CTVMUtilities.getResolvedMethod(Thread.class, method);
        } catch (NoSuchMethodException e) {
            throw new Error("TEST BUG: can't find a test method", e);
        }
    }

    public static void main(String[] args) {
        new GetNextStackFrameTest().test();
    }

    private void test() {
        // Create new thread to get new clean stack
        Thread thread = new Thread(() -> recursiveFrame(RECURSION_AMOUNT));
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new Error("Interrupted while waiting to join", e);
        }
    }

    // Helper methods for a longer stack
    private void recursiveFrame(int recursionAmount) {
        if (--recursionAmount != 0) {
            recursiveFrame(recursionAmount);
        } else {
            frame1();
        }
    }

    private void frame1() {
        frame2();
    }

    private void frame2() {
        frame3();
    }

    private void frame3() {
        frame4();
    }

    private void frame4() {
        check();
    }

    private void check() {
        findFirst();
        walkThrough();
        skipAll();
        findNextSkipped();
        findYourself();
    }

    /**
     * Finds the first topmost frame from the list of methods to search
     */
    private void findFirst() {
        checkNextFrameFor(null /* topmost frame */,
                new HotSpotResolvedJavaMethodImpl[]
                        {FRAME2_METHOD, FRAME3_METHOD, FRAME4_METHOD},
                FRAME4_METHOD, 0);
    }

    /**
     * Walks through whole stack and checks that every frame could be found
     * while going down the stack till the end
     */
    private void walkThrough() {
        // Check that we would get a frame 4 starting from the topmost frame
        HotSpotStackFrameReference nextStackFrame = checkNextFrameFor(
                null /* topmost frame */,
                new HotSpotResolvedJavaMethodImpl[] {FRAME4_METHOD},
                FRAME4_METHOD, 0);
        // Check that we would get a frame 3 starting from frame 4 when we try
        // to search one of the next two frames
        nextStackFrame = checkNextFrameFor(nextStackFrame,
                new HotSpotResolvedJavaMethodImpl[] {FRAME3_METHOD,
                        FRAME2_METHOD},
                FRAME3_METHOD, 0);
        // Check that we would get a frame 1
        nextStackFrame = checkNextFrameFor(nextStackFrame,
                new HotSpotResolvedJavaMethodImpl[] {FRAME1_METHOD},
                FRAME1_METHOD, 0);
        // Check that we would skip (RECURSION_AMOUNT - 1) methods and find a
        // recursionFrame starting from frame 1
        nextStackFrame = checkNextFrameFor(nextStackFrame,
                new HotSpotResolvedJavaMethodImpl[] {REC_FRAME_METHOD},
                REC_FRAME_METHOD, RECURSION_AMOUNT - 1);
        // Check that we would get a Thread::run method frame;
        nextStackFrame = checkNextFrameFor(nextStackFrame,
                new HotSpotResolvedJavaMethodImpl[] {RUN_METHOD},
                RUN_METHOD, 0);
        // Check that there are no more frames after thread's run method
        nextStackFrame = CompilerToVMHelper.getNextStackFrame(nextStackFrame,
                null /* any */, 0);
        Asserts.assertNull(nextStackFrame,
                "Found stack frame after Thread::run");
    }

    /**
     * Skips all frames to get null at the end of the stack
     */
    private void skipAll() {
        // Skip all frames (stack size) + 2 (getNextStackFrame() itself
        // and from CompilerToVMHelper)
        int initialSkip = Thread.currentThread().getStackTrace().length + 2;
        HotSpotStackFrameReference nextStackFrame = CompilerToVMHelper
                .getNextStackFrame(null /* topmost frame */, null /* any */,
                        initialSkip);
        Asserts.assertNull(nextStackFrame, "Unexpected frame");
    }

    /**
     * Search for any frame skipping one frame
     */
    private void findNextSkipped() {
        // Get frame 4
        HotSpotStackFrameReference nextStackFrame = CompilerToVMHelper
                .getNextStackFrame(null /* topmost frame */,
                        new HotSpotResolvedJavaMethodImpl[] {FRAME4_METHOD}, 0);
        // Get frame 2 by skipping one method starting from frame 4
        checkNextFrameFor(nextStackFrame, null /* any */,
                FRAME2_METHOD , 1 /* skip one */);
    }

    /**
     * Finds test method in the stack
     */
    private void findYourself() {
        Method method;
        try {
            method = CompilerToVM.class.getDeclaredMethod("getNextStackFrame",
                        HotSpotStackFrameReference.class,
                        HotSpotResolvedJavaMethodImpl[].class, int.class);
        } catch (NoSuchMethodException e) {
            throw new Error("TEST BUG: can't find getNextStackFrame method");
        }
        HotSpotResolvedJavaMethodImpl self
                = CTVMUtilities.getResolvedMethod(CompilerToVM.class, method);
        checkNextFrameFor(null /* topmost frame */, null /* any */, self, 0);
    }

    /**
     * Searches next frame and checks that it equals to expected
     *
     * @param currentFrame  start frame to search from
     * @param searchMethods a list of methods to search
     * @param expected      expected frame
     * @param skip          amount of frames to be skipped
     * @return frame reference
     */
    private HotSpotStackFrameReference checkNextFrameFor(
            HotSpotStackFrameReference currentFrame,
            HotSpotResolvedJavaMethodImpl[] searchMethods,
            HotSpotResolvedJavaMethodImpl expected,
            int skip) {
        HotSpotStackFrameReference nextStackFrame = CompilerToVMHelper
                .getNextStackFrame(currentFrame, searchMethods, skip);
        Asserts.assertNotNull(nextStackFrame);
        Asserts.assertTrue(nextStackFrame.isMethod(expected),
                "Unexpected next frame: " + nextStackFrame
                        + " from current frame: " + currentFrame);
        return nextStackFrame;
    }
}