From 0a8332f5af1b61d23750d438daeeb82c5a9b5956 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 28 Aug 2019 11:36:22 +0800 Subject: [PATCH] [JWarmup] Invoke JWarmup via jcmd Summary: Invoke JWarmup APIs via jcmd Test Plan: test/jwarmup/jcmd/TestInvokeJWarmupViaJcmd.java Reviewers: kuaiwei, luchsh Issue: https://aone.alibaba-inc.com/task/22023553 CR: https://aone.alibaba-inc.com/task/22023553 --- src/share/vm/classfile/vmSymbols.hpp | 4 + src/share/vm/services/diagnosticCommand.cpp | 106 ++++++++ src/share/vm/services/diagnosticCommand.hpp | 19 ++ .../jcmd/TestInvokeJWarmupViaJcmd.java | 244 ++++++++++++++++++ 4 files changed, 373 insertions(+) create mode 100644 test/jwarmup/jcmd/TestInvokeJWarmupViaJcmd.java diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp index c9fe32512..84ea4876b 100644 --- a/src/share/vm/classfile/vmSymbols.hpp +++ b/src/share/vm/classfile/vmSymbols.hpp @@ -410,6 +410,10 @@ template(signers_name, "signers_name") \ template(loader_data_name, "loader_data") \ template(dependencies_name, "dependencies") \ + template(com_alibaba_jwarmup_JWarmUp, "com/alibaba/jwarmup/JWarmUp") \ + template(jwarmup_notify_application_startup_is_done_name, "notifyApplicationStartUpIsDone") \ + template(jwarmup_check_if_compilation_is_complete_name, "checkIfCompilationIsComplete") \ + template(jwarmup_notify_jvm_deopt_warmup_methods_name, "notifyJVMDeoptWarmUpMethods") \ template(jwarmup_dummy_name, "dummy") \ template(input_stream_void_signature, "(Ljava/io/InputStream;)V") \ template(getFileURL_name, "getFileURL") \ diff --git a/src/share/vm/services/diagnosticCommand.cpp b/src/share/vm/services/diagnosticCommand.cpp index 80afddaba..a6a8324bd 100644 --- a/src/share/vm/services/diagnosticCommand.cpp +++ b/src/share/vm/services/diagnosticCommand.cpp @@ -63,6 +63,7 @@ void DCmdRegistrant::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); // Enhanced JMX Agent Support // These commands won't be exported via the DiagnosticCommandMBean until an @@ -743,3 +744,108 @@ void RotateGCLogDCmd::execute(DCmdSource source, TRAPS) { output()->print_cr("Target VM does not support GC log file rotation."); } } + +JWarmupDCmd::JWarmupDCmd(outputStream* output, bool heap_allocated) : DCmdWithParser(output, heap_allocated), + _notify_startup("-notify", "Notify JVM that application startup is done", "BOOLEAN", false, "false"), + _check_compile_finished("-check", "Check if the last compilation submitted by JWarmup is complete", "BOOLEAN", false, "false"), + _deopt("-deopt", "Notify JVM to de-optimize warmup methods", "BOOLEAN", false, "false"), + _help("-help", "Print this help information", "BOOLEAN", false, "false") +{ + _dcmdparser.add_dcmd_option(&_notify_startup); + _dcmdparser.add_dcmd_option(&_check_compile_finished); + _dcmdparser.add_dcmd_option(&_deopt); + _dcmdparser.add_dcmd_option(&_help); +} + +int JWarmupDCmd::num_arguments() { + ResourceMark rm; + JWarmupDCmd* dcmd = new JWarmupDCmd(NULL, false); + if (dcmd != NULL) { + DCmdMark mark(dcmd); + return dcmd->_dcmdparser.num_arguments(); + } else { + return 0; + } +} + +void JWarmupDCmd::execute(DCmdSource source, TRAPS) { + assert(is_init_completed(), "JVM is not fully initialized. Please try it later."); + + Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::com_alibaba_jwarmup_JWarmUp(), true, CHECK); + instanceKlassHandle ik (THREAD, k); + if (ik->should_be_initialized()) { + ik->initialize(THREAD); + } + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, output()); + output()->cr(); + CLEAR_PENDING_EXCEPTION; + return; + } + + if (_notify_startup.value()) { + if (!CompilationWarmUp) { + output()->print_cr("CompilationWarmUp is off, " + "notifyApplicationStartUpIsDone is invalid"); + return; + } + + JavaValue result(T_VOID); + JavaCalls::call_static(&result, ik, vmSymbols::jwarmup_notify_application_startup_is_done_name(), vmSymbols::void_method_signature(), THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, output()); + output()->cr(); + CLEAR_PENDING_EXCEPTION; + return; + } + } else if (_check_compile_finished.value()) { + if (!CompilationWarmUp) { + output()->print_cr("CompilationWarmUp is off, " + "checkIfCompilationIsComplete is invalid"); + return; + } + + JavaValue result(T_BOOLEAN); + JavaCalls::call_static(&result, ik, vmSymbols::jwarmup_check_if_compilation_is_complete_name(), vmSymbols::void_boolean_signature(), THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, output()); + output()->cr(); + CLEAR_PENDING_EXCEPTION; + return; + } + + if (result.get_jboolean()) { + output()->print_cr("Last compilation task is completed."); + } else { + output()->print_cr("Last compilation task is not completed."); + } + } else if (_deopt.value()) { + if (!(CompilationWarmUp && CompilationWarmUpExplicitDeopt)) { + output()->print_cr("CompilationWarmUp or CompilationWarmUpExplicitDeopt is off, " + "notifyJVMDeoptWarmUpMethods is invalid"); + return; + } + + JavaValue result(T_VOID); + JavaCalls::call_static(&result, ik, vmSymbols::jwarmup_notify_jvm_deopt_warmup_methods_name(), vmSymbols::void_method_signature(), THREAD); + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, output()); + output()->cr(); + CLEAR_PENDING_EXCEPTION; + return; + } + } else if (_help.value()) { + print_info(); + } else { + print_info(); + } +} + +void JWarmupDCmd::print_info() { + output()->print_cr("The following commands are available:\n" + "-notify: %s\n" + "-check: %s\n" + "-deopt: %s\n" + "-help: %s\n", + _notify_startup.description(), _check_compile_finished.description(), _deopt.description(), _help.description()); +} \ No newline at end of file diff --git a/src/share/vm/services/diagnosticCommand.hpp b/src/share/vm/services/diagnosticCommand.hpp index 0d047589d..40337f7d5 100644 --- a/src/share/vm/services/diagnosticCommand.hpp +++ b/src/share/vm/services/diagnosticCommand.hpp @@ -442,4 +442,23 @@ public: } }; +class JWarmupDCmd : public DCmdWithParser { +protected: + DCmdArgument _notify_startup; + DCmdArgument _check_compile_finished; + DCmdArgument _deopt; + DCmdArgument _help; + void print_info(); +public: + JWarmupDCmd(outputStream* output, bool heap_allocated); + static const char* name() { + return "JWarmup"; + } + static const char* description() { + return "JWarmup command. "; + } + static int num_arguments(); + virtual void execute(DCmdSource source, TRAPS); +}; + #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP diff --git a/test/jwarmup/jcmd/TestInvokeJWarmupViaJcmd.java b/test/jwarmup/jcmd/TestInvokeJWarmupViaJcmd.java new file mode 100644 index 000000000..ed340ba08 --- /dev/null +++ b/test/jwarmup/jcmd/TestInvokeJWarmupViaJcmd.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. 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. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + * + */ + +import com.oracle.java.testlibrary.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.lang.reflect.Field; +import static com.oracle.java.testlibrary.Asserts.assertTrue; + +/* + * @test TestInvokeJWarmupViaJcmd + * @library /testlibrary + * @build TestInvokeJWarmupViaJcmd + * @run main TestInvokeJWarmupViaJcmd + * @run main/othervm TestInvokeJWarmupViaJcmd + * @summary test invoking JWarmup APIs via jcmd + */ +public class TestInvokeJWarmupViaJcmd { + public static void main(String[] args) throws Exception { + Recording.run(); + CheckNotifyStartup.run(); + CheckCompilationSuccess.run(); + CheckCompilationFail.run(); + CheckDeopt.run(); + } +} + +class Main { + public static void main(String[] args) throws Exception { + Main main = new Main(); + for (int i = 0; i < 20000; i++) { + main.foo(); + } + // Waiting for recording or jcmd. + Thread.sleep(12000); + System.out.println("done!"); + } + + public void foo() {} +} + +/** + * JWarmup command + */ +class JCMDArg { + public static final String notify = "-notify"; + public static final String check = "-check"; + public static final String deopt = "-deopt"; + +} + +class ProgramArg { + public static final String Recording = "Recording"; + public static final String NotifyStartup = "NotifyStartup"; + public static final String CheckCompilationSuccess = "CheckCompilationSuccess"; + public static final String CheckCompilationFail = "CheckCompilationFail"; + public static final String Deopt = "Deopt"; +} + +/** + * JVM options + */ +class JVMArg { + public static final List Recording = new ArrayList(Arrays.asList( + "-XX:-ClassUnloading", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-ClassUnloadingWithConcurrentMark", + "-XX:CompilationWarmUpLogfile=jwarmup.log", + "-XX:+CompilationWarmUpRecording", + "-XX:CompilationWarmUpRecordTime=10" + )); + + public static final List Running = new ArrayList(Arrays.asList( + "-XX:+CompilationWarmUp", + "-XX:+CompilationWarmUpExplicitDeopt", + "-XX:-TieredCompilation", + "-XX:CompilationWarmUpLogfile=jwarmup.log", + "-XX:CompilationWarmUpDeoptTime=0" + )); +} + +class Recording { + public static void run() throws Exception { + System.out.println("Test Jwarmup recording."); + ProcessBuilderFactory.create(ProgramArg.Recording, JVMArg.Recording).start(); + Thread.sleep(15000); + File logFile = new File("./jwarmup.log"); + assertTrue(logFile.exists() && logFile.isFile()); + } +} + +class CheckNotifyStartup { + public static void run() throws Exception { + System.out.println("Test JWarmup notifyStartup."); + ProcessBuilder processBuilder = ProcessBuilderFactory.create(ProgramArg.NotifyStartup, JVMArg.Running); + Process p = processBuilder.start(); + Thread.sleep(5000); + String pid = PID.get(p); + assert pid != null; + JcmdCaller.callJcmd(ProgramArg.NotifyStartup, pid); + } +} + +class CheckCompilationSuccess { + public static void run() throws Exception { + System.out.println("Test JWarmup checkCompilation."); + ProcessBuilder processBuilder = ProcessBuilderFactory.create(ProgramArg.CheckCompilationSuccess, JVMArg.Running); + Process p = processBuilder.start(); + Thread.sleep(5000); + String pid = PID.get(p); + assert pid != null; + JcmdCaller.callJcmd(ProgramArg.CheckCompilationSuccess, pid); + } +} + +/** + * Excluding compiling com.alibaba.jwarmup.JWarmUp.dummy() to test a unfinished compilation task. + */ +class CheckCompilationFail { + public static void run() throws Exception { + System.out.println("Test JWarmup checkCompilation."); + List jvmArgs = new ArrayList<>(); + jvmArgs.addAll(JVMArg.Running); + jvmArgs.add("-XX:CompileCommand=exclude,com/alibaba/jwarmup/JWarmUp,dummy"); + ProcessBuilder processBuilder = ProcessBuilderFactory.create(ProgramArg.CheckCompilationFail, jvmArgs); + Process p = processBuilder.start(); + String pid = PID.get(p); + assert pid != null; + Thread.sleep(5000); + JcmdCaller.callJcmd(ProgramArg.CheckCompilationFail, pid); + } +} + +class CheckDeopt { + public static void run() throws Exception { + System.out.println("Test JWarmup de-optimization."); + ProcessBuilder processBuilder = ProcessBuilderFactory.create(ProgramArg.Deopt, JVMArg.Running); + Process p = processBuilder.start(); + Thread.sleep(5000); + JcmdCaller.callJcmd(ProgramArg.Deopt, PID.get(p)); + } +} + +class ProcessBuilderFactory { + public static ProcessBuilder create(String programArg, final List jvmArgs) throws Exception { + assert jvmArgs != null && jvmArgs.size() != 0; + List _jvmArgs = new ArrayList<>(jvmArgs); + _jvmArgs.addAll(Arrays.asList( + Main.class.getName(), + programArg + )); + return ProcessTools.createJavaProcessBuilder(_jvmArgs.stream().toArray(String[]::new)); + } +} + +class JcmdCaller { + public static void callJcmd(String arg, String pid) throws Exception { + if (ProgramArg.Recording.equals(arg)) { + return; + } else if (ProgramArg.NotifyStartup.equals(arg)) { + ProcessBuilder processBuilder = notifyStartup(pid); + OutputAnalyzer output = new OutputAnalyzer(processBuilder.start()); + output.shouldContain("Command executed successfully"); + } else if (ProgramArg.CheckCompilationSuccess.equals(arg)) { + ProcessBuilder processBuilder = checkCompilation(pid); + OutputAnalyzer output = new OutputAnalyzer(processBuilder.start()); + output.shouldContain("Last compilation task is completed."); + } else if (ProgramArg.CheckCompilationFail.equals(arg)) { + ProcessBuilder processBuilder = checkCompilation(pid); + OutputAnalyzer output = new OutputAnalyzer(processBuilder.start()); + output.shouldContain("Last compilation task is not completed."); + } else if (ProgramArg.Deopt.equals(arg)) { + ProcessBuilder processBuilder = deopt(pid); + OutputAnalyzer output = new OutputAnalyzer(processBuilder.start()); + output.shouldContain("Command executed successfully"); + } else { + throw new RuntimeException(String.format("Unrecognized argument: %s", arg)); + } + } + + private static ProcessBuilder notifyStartup(String pid) { + JDKToolLauncher notify = JDKToolLauncher.create("jcmd") + .addToolArg(pid) + .addToolArg("JWarmup") + .addToolArg(JCMDArg.notify); + return new ProcessBuilder(notify.getCommand()); + } + + private static ProcessBuilder checkCompilation(String pid) throws Exception { + // Invoke notifyStartup() before checkCompilation(). + ProcessBuilder processBuilder = notifyStartup(pid); + Process p = processBuilder.start(); + p.waitFor(); + JDKToolLauncher check = JDKToolLauncher.create("jcmd") + .addToolArg(pid) + .addToolArg("JWarmup") + .addToolArg(JCMDArg.check); + return new ProcessBuilder(check.getCommand()); + } + + private static ProcessBuilder deopt(String pid) { + JDKToolLauncher deopt = JDKToolLauncher.create("jcmd") + .addToolArg(pid) + .addToolArg("JWarmup") + .addToolArg(JCMDArg.deopt); + return new ProcessBuilder(deopt.getCommand()); + } +} + +class PID { + public static String get(Process p) throws Exception { + if ("java.lang.UNIXProcess".equals(p.getClass().getName())) { + Field f = p.getClass().getDeclaredField("pid"); + f.setAccessible(true); + long pid = f.getLong(p); + f.setAccessible(false); + return Long.toString(pid); + } else { + throw new RuntimeException("Unable to obtain pid."); + } + } +} \ No newline at end of file -- GitLab