CompilerWhiteBoxTest.java 20.0 KB
Newer Older
1
/*
2
 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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.
 */

24 25
import com.sun.management.HotSpotDiagnosticMXBean;
import com.sun.management.VMOption;
26
import sun.hotspot.WhiteBox;
I
iignatyev 已提交
27
import sun.hotspot.code.NMethod;
28 29
import sun.management.ManagementFactoryHelper;

30 31
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
32
import java.lang.reflect.Method;
33 34
import java.util.Objects;
import java.util.concurrent.Callable;
35
import java.util.function.Function;
36

37 38 39
/**
 * Abstract class for WhiteBox testing of JIT.
 *
40 41 42
 * @author igor.ignatyev@oracle.com
 */
public abstract class CompilerWhiteBoxTest {
43 44 45 46
    /** {@code CompLevel::CompLevel_none} -- Interpreter */
    protected static int COMP_LEVEL_NONE = 0;
    /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */
    protected static int COMP_LEVEL_ANY = -1;
47 48
    /** {@code CompLevel::CompLevel_simple} -- C1 */
    protected static int COMP_LEVEL_SIMPLE = 1;
49 50 51 52
    /** {@code CompLevel::CompLevel_limited_profile} -- C1, invocation & backedge counters */
    protected static int COMP_LEVEL_LIMITED_PROFILE = 2;
    /** {@code CompLevel::CompLevel_full_profile} -- C1, invocation & backedge counters + mdo */
    protected static int COMP_LEVEL_FULL_PROFILE = 3;
53 54
    /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */
    protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4;
55
    /** Maximal value for CompLevel */
56
    protected static int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION;
57

58
    /** Instance of WhiteBox */
59
    protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
60
    /** Value of {@code -XX:CompileThreshold} */
61 62
    protected static final int COMPILE_THRESHOLD
            = Integer.parseInt(getVMOption("CompileThreshold", "10000"));
63
    /** Value of {@code -XX:BackgroundCompilation} */
64 65
    protected static final boolean BACKGROUND_COMPILATION
            = Boolean.valueOf(getVMOption("BackgroundCompilation", "true"));
66
    /** Value of {@code -XX:TieredCompilation} */
67 68
    protected static final boolean TIERED_COMPILATION
            = Boolean.valueOf(getVMOption("TieredCompilation", "false"));
69 70 71
    /** Value of {@code -XX:TieredStopAtLevel} */
    protected static final int TIERED_STOP_AT_LEVEL
            = Integer.parseInt(getVMOption("TieredStopAtLevel", "0"));
72 73 74
    /** Flag for verbose output, true if {@code -Dverbose} specified */
    protected static final boolean IS_VERBOSE
            = System.getProperty("verbose") != null;
75 76 77 78
    /** count of invocation to triger compilation */
    protected static final int THRESHOLD;
    /** count of invocation to triger OSR compilation */
    protected static final long BACKEDGE_THRESHOLD;
79
    /** Value of {@code java.vm.info} (interpreted|mixed|comp mode) */
80
    protected static final String MODE = System.getProperty("java.vm.info");
81 82 83

    static {
        if (TIERED_COMPILATION) {
84
            BACKEDGE_THRESHOLD = THRESHOLD = 150000;
85 86 87 88 89 90
        } else {
            THRESHOLD = COMPILE_THRESHOLD;
            BACKEDGE_THRESHOLD = COMPILE_THRESHOLD * Long.parseLong(getVMOption(
                    "OnStackReplacePercentage"));
        }
    }
91

92 93 94 95 96 97 98
    /**
     * Returns value of VM option.
     *
     * @param name option's name
     * @return value of option or {@code null}, if option doesn't exist
     * @throws NullPointerException if name is null
     */
99
    protected static String getVMOption(String name) {
100
        Objects.requireNonNull(name);
101 102
        HotSpotDiagnosticMXBean diagnostic
                = ManagementFactoryHelper.getDiagnosticMXBean();
103 104 105 106 107 108 109
        VMOption tmp;
        try {
            tmp = diagnostic.getVMOption(name);
        } catch (IllegalArgumentException e) {
            tmp = null;
        }
        return (tmp == null ? null : tmp.getValue());
110 111
    }

112 113 114 115 116 117 118 119 120
    /**
     * Returns value of VM option or default value.
     *
     * @param name         option's name
     * @param defaultValue default value
     * @return value of option or {@code defaultValue}, if option doesn't exist
     * @throws NullPointerException if name is null
     * @see #getVMOption(String)
     */
121 122
    protected static String getVMOption(String name, String defaultValue) {
        String result = getVMOption(name);
123 124 125
        return result == null ? defaultValue : result;
    }

126 127 128 129 130 131 132 133 134 135 136
    /** copy of is_c1_compile(int) from utilities/globalDefinitions.hpp */
    protected static boolean isC1Compile(int compLevel) {
        return (compLevel > COMP_LEVEL_NONE)
                && (compLevel < COMP_LEVEL_FULL_OPTIMIZATION);
    }

    /** copy of is_c2_compile(int) from utilities/globalDefinitions.hpp */
    protected static boolean isC2Compile(int compLevel) {
        return compLevel == COMP_LEVEL_FULL_OPTIMIZATION;
    }

137 138 139 140 141 142 143 144 145 146 147 148 149 150
    protected static void main(
            Function<TestCase, CompilerWhiteBoxTest> constructor,
            String[] args) {
        if (args.length == 0) {
            for (TestCase test : SimpleTestCase.values()) {
                constructor.apply(test).runTest();
            }
        } else {
            for (String name : args) {
                constructor.apply(SimpleTestCase.valueOf(name)).runTest();
            }
        }
    }

151 152
    /** tested method */
    protected final Executable method;
153
    protected final TestCase testCase;
154 155 156 157 158 159 160 161 162

    /**
     * Constructor.
     *
     * @param testCase object, that contains tested method and way to invoke it.
     */
    protected CompilerWhiteBoxTest(TestCase testCase) {
        Objects.requireNonNull(testCase);
        System.out.println("TEST CASE:" + testCase.name());
163
        method = testCase.getExecutable();
164
        this.testCase = testCase;
165 166 167 168 169 170 171 172 173 174 175 176
    }

    /**
     * Template method for testing. Prints tested method's info before
     * {@linkplain #test()} and after {@linkplain #test()} or on thrown
     * exception.
     *
     * @throws RuntimeException if method {@linkplain #test()} throws any
     *                          exception
     * @see #test()
     */
    protected final void runTest() {
177 178 179 180 181 182
        if (ManagementFactoryHelper.getCompilationMXBean() == null) {
            System.err.println(
                    "Warning: test is not applicable in interpreted mode");
            return;
        }
        System.out.println("at test's start:");
183
        printInfo();
184 185 186 187
        try {
            test();
        } catch (Exception e) {
            System.out.printf("on exception '%s':", e.getMessage());
188
            printInfo();
189
            e.printStackTrace();
190 191 192
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
193 194 195
            throw new RuntimeException(e);
        }
        System.out.println("at test's end:");
196
        printInfo();
197 198
    }

199 200 201 202 203 204 205 206
    /**
     * Checks, that {@linkplain #method} is not compiled.
     *
     * @throws RuntimeException if {@linkplain #method} is in compiler queue or
     *                          is compiled, or if {@linkplain #method} has zero
     *                          compilation level.
     */
    protected final void checkNotCompiled() {
207 208 209
        if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
            throw new RuntimeException(method + " must not be in queue");
        }
210
        if (WHITE_BOX.isMethodCompiled(method, false)) {
211 212
            throw new RuntimeException(method + " must be not compiled");
        }
213
        if (WHITE_BOX.getMethodCompilationLevel(method, false) != 0) {
214 215
            throw new RuntimeException(method + " comp_level must be == 0");
        }
216 217 218 219 220 221
        if (WHITE_BOX.isMethodCompiled(method, true)) {
            throw new RuntimeException(method + " must be not osr_compiled");
        }
        if (WHITE_BOX.getMethodCompilationLevel(method, true) != 0) {
            throw new RuntimeException(method + " osr_comp_level must be == 0");
        }
222
    }
223

224 225 226 227 228 229 230 231
    /**
     * Checks, that {@linkplain #method} is compiled.
     *
     * @throws RuntimeException if {@linkplain #method} isn't in compiler queue
     *                          and isn't compiled, or if {@linkplain #method}
     *                          has nonzero compilation level
     */
    protected final void checkCompiled() {
232
        final long start = System.currentTimeMillis();
233
        waitBackgroundCompilation();
234 235 236 237 238
        if (WHITE_BOX.isMethodQueuedForCompilation(method)) {
            System.err.printf("Warning: %s is still in queue after %dms%n",
                    method, System.currentTimeMillis() - start);
            return;
        }
239
        if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
240
            throw new RuntimeException(method + " must be "
241
                    + (testCase.isOsr() ? "osr_" : "") + "compiled");
242
        }
243 244
        if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr())
                == 0) {
245
            throw new RuntimeException(method
246
                    + (testCase.isOsr() ? " osr_" : " ")
247 248 249 250 251
                    + "comp_level must be != 0");
        }
    }

    protected final void deoptimize() {
252 253
        WHITE_BOX.deoptimizeMethod(method, testCase.isOsr());
        if (testCase.isOsr()) {
254
            WHITE_BOX.deoptimizeMethod(method, false);
255 256 257
        }
    }

258
    protected final int getCompLevel() {
I
iignatyev 已提交
259 260
        NMethod nm = NMethod.get(method, testCase.isOsr());
        return nm == null ? COMP_LEVEL_NONE : nm.comp_level;
261 262 263 264
    }

    protected final boolean isCompilable() {
        return WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY,
265
                testCase.isOsr());
266 267 268
    }

    protected final boolean isCompilable(int compLevel) {
269 270
        return WHITE_BOX
                .isMethodCompilable(method, compLevel, testCase.isOsr());
271 272 273 274
    }

    protected final void makeNotCompilable() {
        WHITE_BOX.makeMethodNotCompilable(method, COMP_LEVEL_ANY,
275
                testCase.isOsr());
276 277 278
    }

    protected final void makeNotCompilable(int compLevel) {
279
        WHITE_BOX.makeMethodNotCompilable(method, compLevel, testCase.isOsr());
280 281
    }

282 283 284 285
    /**
     * Waits for completion of background compilation of {@linkplain #method}.
     */
    protected final void waitBackgroundCompilation() {
286 287 288
        if (!BACKGROUND_COMPILATION) {
            return;
        }
289
        final Object obj = new Object();
290 291 292 293 294 295 296
        for (int i = 0; i < 10
                && WHITE_BOX.isMethodQueuedForCompilation(method); ++i) {
            synchronized (obj) {
                try {
                    obj.wait(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
297 298 299 300 301
                }
            }
        }
    }

302 303 304 305
    /**
     * Prints information about {@linkplain #method}.
     */
    protected final void printInfo() {
306 307
        System.out.printf("%n%s:%n", method);
        System.out.printf("\tcompilable:\t%b%n",
308
                WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, false));
309
        System.out.printf("\tcompiled:\t%b%n",
310
                WHITE_BOX.isMethodCompiled(method, false));
311
        System.out.printf("\tcomp_level:\t%d%n",
312 313 314 315 316 317 318
                WHITE_BOX.getMethodCompilationLevel(method, false));
        System.out.printf("\tosr_compilable:\t%b%n",
                WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, true));
        System.out.printf("\tosr_compiled:\t%b%n",
                WHITE_BOX.isMethodCompiled(method, true));
        System.out.printf("\tosr_comp_level:\t%d%n",
                WHITE_BOX.getMethodCompilationLevel(method, true));
319
        System.out.printf("\tin_queue:\t%b%n",
320 321 322 323 324
                WHITE_BOX.isMethodQueuedForCompilation(method));
        System.out.printf("compile_queues_size:\t%d%n%n",
                WHITE_BOX.getCompileQueuesSize());
    }

325 326 327
    /**
     * Executes testing.
     */
328 329
    protected abstract void test() throws Exception;

330 331
    /**
     * Tries to trigger compilation of {@linkplain #method} by call
332
     * {@linkplain TestCase#getCallable()} enough times.
333 334 335 336
     *
     * @return accumulated result
     * @see #compile(int)
     */
337
    protected final int compile() {
338
        if (testCase.isOsr()) {
339 340 341 342
            return compile(1);
        } else {
            return compile(THRESHOLD);
        }
343 344
    }

345 346
    /**
     * Tries to trigger compilation of {@linkplain #method} by call
347
     * {@linkplain TestCase#getCallable()} specified times.
348 349 350 351
     *
     * @param count invocation count
     * @return accumulated result
     */
352
    protected final int compile(int count) {
353
        int result = 0;
354
        Integer tmp;
355
        for (int i = 0; i < count; ++i) {
356
            try {
357
                tmp = testCase.getCallable().call();
358 359 360 361
            } catch (Exception e) {
                tmp = null;
            }
            result += tmp == null ? 0 : tmp;
362
        }
363 364 365
        if (IS_VERBOSE) {
            System.out.println("method was invoked " + count + " times");
        }
366 367
        return result;
    }
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

    /**
     * Utility interface provides tested method and object to invoke it.
     */
    public interface TestCase {
        /** the name of test case */
        String name();

        /** tested method */
        Executable getExecutable();

        /** object to invoke {@linkplain #getExecutable()} */
        Callable<Integer> getCallable();

        /** flag for OSR test case */
        boolean isOsr();
    }
385 386 387 388 389 390 391 392 393 394 395 396 397 398

    /**
     * @return {@code true} if the current test case is OSR and the mode is
     *          Xcomp, otherwise {@code false}
     */
    protected boolean skipXcompOSR() {
        boolean result =  testCase.isOsr()
                && CompilerWhiteBoxTest.MODE.startsWith("compiled ");
        if (result && IS_VERBOSE) {
            System.err.printf("Warning: %s is not applicable in %s%n",
                    testCase.name(), CompilerWhiteBoxTest.MODE);
        }
        return result;
    }
399
}
400

401
enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase {
402
    /** constructor test case */
403
    CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE, false),
404
    /** method test case */
405
    METOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE, false),
406
    /** static method test case */
407 408 409 410
    STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE, false),
    /** OSR constructor test case */
    OSR_CONSTRUCTOR_TEST(Helper.OSR_CONSTRUCTOR,
            Helper.OSR_CONSTRUCTOR_CALLABLE, true),
411
    /** OSR method test case */
412 413 414
    OSR_METOD_TEST(Helper.OSR_METHOD, Helper.OSR_METHOD_CALLABLE, true),
    /** OSR static method test case */
    OSR_STATIC_TEST(Helper.OSR_STATIC, Helper.OSR_STATIC_CALLABLE, true);
415

416 417 418
    private final Executable executable;
    private final Callable<Integer> callable;
    private final boolean isOsr;
419

420
    private SimpleTestCase(Executable executable, Callable<Integer> callable,
421
            boolean isOsr) {
422 423
        this.executable = executable;
        this.callable = callable;
424
        this.isOsr = isOsr;
425 426
    }

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
    @Override
    public Executable getExecutable() {
        return executable;
    }

    @Override
    public Callable<Integer> getCallable() {
        return callable;
    }

    @Override
    public boolean isOsr() {
        return isOsr;
    }

442
    private static class Helper {
443

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
        private static final Callable<Integer> CONSTRUCTOR_CALLABLE
                = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return new Helper(1337).hashCode();
            }
        };

        private static final Callable<Integer> METHOD_CALLABLE
                = new Callable<Integer>() {
            private final Helper helper = new Helper();

            @Override
            public Integer call() throws Exception {
                return helper.method();
            }
        };

        private static final Callable<Integer> STATIC_CALLABLE
                = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return staticMethod();
            }
        };

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
        private static final Callable<Integer> OSR_CONSTRUCTOR_CALLABLE
                = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return new Helper(null).hashCode();
            }
        };

        private static final Callable<Integer> OSR_METHOD_CALLABLE
                = new Callable<Integer>() {
            private final Helper helper = new Helper();

            @Override
            public Integer call() throws Exception {
                return helper.osrMethod();
            }
        };

        private static final Callable<Integer> OSR_STATIC_CALLABLE
                = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return osrStaticMethod();
            }
        };

496
        private static final Constructor CONSTRUCTOR;
497
        private static final Constructor OSR_CONSTRUCTOR;
498 499
        private static final Method METHOD;
        private static final Method STATIC;
500 501
        private static final Method OSR_METHOD;
        private static final Method OSR_STATIC;
502 503 504 505 506 507 508 509 510

        static {
            try {
                CONSTRUCTOR = Helper.class.getDeclaredConstructor(int.class);
            } catch (NoSuchMethodException | SecurityException e) {
                throw new RuntimeException(
                        "exception on getting method Helper.<init>(int)", e);
            }
            try {
511 512
                OSR_CONSTRUCTOR = Helper.class.getDeclaredConstructor(
                        Object.class);
513 514
            } catch (NoSuchMethodException | SecurityException e) {
                throw new RuntimeException(
515
                        "exception on getting method Helper.<init>(Object)", e);
516
            }
517 518 519 520 521 522 523
            METHOD = getMethod("method");
            STATIC = getMethod("staticMethod");
            OSR_METHOD = getMethod("osrMethod");
            OSR_STATIC = getMethod("osrStaticMethod");
        }

        private static Method getMethod(String name) {
524
            try {
525
                return Helper.class.getDeclaredMethod(name);
526 527
            } catch (NoSuchMethodException | SecurityException e) {
                throw new RuntimeException(
528
                        "exception on getting method Helper." + name, e);
529
            }
530

531 532 533 534 535 536 537 538 539 540
        }

        private static int staticMethod() {
            return 1138;
        }

        private int method() {
            return 42;
        }

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
        private static int osrStaticMethod() {
            int result = 0;
            for (long i = 0; i < CompilerWhiteBoxTest.BACKEDGE_THRESHOLD; ++i) {
                result += staticMethod();
            }
            return result;
        }

        private int osrMethod() {
            int result = 0;
            for (long i = 0; i < CompilerWhiteBoxTest.BACKEDGE_THRESHOLD; ++i) {
                result += method();
            }
            return result;
        }

557 558
        private final int x;

559
        // for method and OSR method test case
560 561 562 563
        public Helper() {
            x = 0;
        }

564 565 566 567 568 569 570 571 572 573
        // for OSR constructor test case
        private Helper(Object o) {
            int result = 0;
            for (long i = 0; i < CompilerWhiteBoxTest.BACKEDGE_THRESHOLD; ++i) {
                result += method();
            }
            x = result;
        }

        // for constructor test case
574 575 576 577 578 579 580 581
        private Helper(int x) {
            this.x = x;
        }

        @Override
        public int hashCode() {
            return x;
        }
582 583
    }
}