8147388: Add diagnostic commands to attach JVMTI agent.
authorysuenaga
Thu, 18 Feb 2016 23:26:43 +0900
changeset 36188 99b6836dd3e8
parent 36187 1d61b06a3ae1
child 36190 6f3812212f4f
child 36192 68b97901a2bd
child 36194 d6b05b266581
8147388: Add diagnostic commands to attach JVMTI agent. Reviewed-by: jbachorik, sspitsyn
hotspot/src/share/vm/prims/jvmtiExport.cpp
hotspot/src/share/vm/prims/jvmtiExport.hpp
hotspot/src/share/vm/services/diagnosticCommand.cpp
hotspot/src/share/vm/services/diagnosticCommand.hpp
hotspot/test/serviceability/dcmd/jvmti/LoadAgentDcmdTest.java
hotspot/test/serviceability/dcmd/jvmti/LoadJavaAgentDcmdTest.java
hotspot/test/serviceability/dcmd/jvmti/SimpleJvmtiAgent.java
--- a/hotspot/src/share/vm/prims/jvmtiExport.cpp	Wed Feb 17 18:02:03 2016 -0500
+++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp	Thu Feb 18 23:26:43 2016 +0900
@@ -2200,6 +2200,16 @@
 }
 
 jint JvmtiExport::load_agent_library(AttachOperation* op, outputStream* st) {
+  // get agent name and options
+  const char* agent = op->arg(0);
+  const char* absParam = op->arg(1);
+  const char* options = op->arg(2);
+
+  return load_agent_library(agent, absParam, options, st);
+}
+
+jint JvmtiExport::load_agent_library(const char *agent, const char *absParam,
+                                     const char *options, outputStream* st) {
   char ebuf[1024];
   char buffer[JVM_MAXPATHLEN];
   void* library = NULL;
@@ -2207,11 +2217,6 @@
   const char *on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS;
   size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols);
 
-  // get agent name and options
-  const char* agent = op->arg(0);
-  const char* absParam = op->arg(1);
-  const char* options = op->arg(2);
-
   // The abs paramter should be "true" or "false"
   bool is_absolute_path = (absParam != NULL) && (strcmp(absParam,"true")==0);
 
--- a/hotspot/src/share/vm/prims/jvmtiExport.hpp	Wed Feb 17 18:02:03 2016 -0500
+++ b/hotspot/src/share/vm/prims/jvmtiExport.hpp	Thu Feb 18 23:26:43 2016 +0900
@@ -372,6 +372,7 @@
   static void transition_pending_onload_raw_monitors() NOT_JVMTI_RETURN;
 
   // attach support
+  static jint load_agent_library(const char *agent, const char *absParam, const char *options, outputStream* out) NOT_JVMTI_RETURN_(JNI_ERR);
   static jint load_agent_library(AttachOperation* op, outputStream* out) NOT_JVMTI_RETURN_(JNI_ERR);
 
   // SetNativeMethodPrefix support
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp	Wed Feb 17 18:02:03 2016 -0500
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp	Thu Feb 18 23:26:43 2016 +0900
@@ -71,6 +71,7 @@
 #endif // INCLUDE_SERVICES
 #if INCLUDE_JVMTI
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JVMTIDataDumpDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JVMTIAgentLoadDCmd>(full_export, true, false));
 #endif // INCLUDE_JVMTI
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderStatsDCmd>(full_export, true, false));
@@ -254,6 +255,66 @@
   }
 }
 
+JVMTIAgentLoadDCmd::JVMTIAgentLoadDCmd(outputStream* output, bool heap) :
+                                       DCmdWithParser(output, heap),
+  _libpath("library path", "Absolute path of the JVMTI agent to load.",
+           "STRING", true),
+  _option("agent option", "Option string to pass the agent.", "STRING", false) {
+  _dcmdparser.add_dcmd_argument(&_libpath);
+  _dcmdparser.add_dcmd_argument(&_option);
+}
+
+void JVMTIAgentLoadDCmd::execute(DCmdSource source, TRAPS) {
+
+  if (_libpath.value() == NULL) {
+    output()->print_cr("JVMTI.agent_load dcmd needs library path.");
+    return;
+  }
+
+  char *suffix = strrchr(_libpath.value(), '.');
+  bool is_java_agent = (suffix != NULL) && (strncmp(".jar", suffix, 4) == 0);
+
+  if (is_java_agent) {
+    if (_option.value() == NULL) {
+      JvmtiExport::load_agent_library("instrument", "false",
+                                      _libpath.value(), output());
+    } else {
+      size_t opt_len = strlen(_libpath.value()) + strlen(_option.value()) + 2;
+      if (opt_len > 4096) {
+        output()->print_cr("JVMTI agent attach failed: Options is too long.");
+        return;
+      }
+
+      char *opt = (char *)os::malloc(opt_len, mtInternal);
+      if (opt == NULL) {
+        output()->print_cr("JVMTI agent attach failed: "
+                           "Could not allocate %zu bytes for argument.",
+                           opt_len);
+        return;
+      }
+
+      jio_snprintf(opt, opt_len, "%s=%s", _libpath.value(), _option.value());
+      JvmtiExport::load_agent_library("instrument", "false", opt, output());
+
+      os::free(opt);
+    }
+  } else {
+    JvmtiExport::load_agent_library(_libpath.value(), "true",
+                                    _option.value(), output());
+  }
+}
+
+int JVMTIAgentLoadDCmd::num_arguments() {
+  ResourceMark rm;
+  JVMTIAgentLoadDCmd* dcmd = new JVMTIAgentLoadDCmd(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
 void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) {
   // load sun.misc.VMSupport
   Symbol* klass = vmSymbols::sun_misc_VMSupport();
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp	Wed Feb 17 18:02:03 2016 -0500
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp	Thu Feb 18 23:26:43 2016 +0900
@@ -174,6 +174,26 @@
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+class JVMTIAgentLoadDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<char*> _libpath;
+  DCmdArgument<char*> _option;
+public:
+  JVMTIAgentLoadDCmd(outputStream* output, bool heap);
+  static const char* name() { return "JVMTI.agent_load"; }
+  static const char* description() {
+    return "Load JVMTI native agent.";
+  }
+  static const char* impact() { return "Low"; }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+                        "control", NULL};
+    return p;
+  }
+  static int num_arguments();
+  virtual void execute(DCmdSource source, TRAPS);
+};
+
 class VMDynamicLibrariesDCmd : public DCmd {
 public:
   VMDynamicLibrariesDCmd(outputStream* output, bool heap);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/jvmti/LoadAgentDcmdTest.java	Thu Feb 18 23:26:43 2016 +0900
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+import java.io.*;
+import java.nio.file.*;
+import jdk.test.lib.*;
+import jdk.test.lib.dcmd.*;
+import org.testng.annotations.Test;
+
+/*
+ * Test to attach JVMTI java agent.
+ *
+ * @test
+ * @bug 8147388
+ * @library /testlibrary
+ * @modules java.base/sun.misc
+ *          java.compiler
+ *          java.instrument
+ *          java.management
+ *          jdk.jvmstat/sun.jvmstat.monitor
+ * @build ClassFileInstaller jdk.test.lib.* SimpleJvmtiAgent
+ * @run main ClassFileInstaller SimpleJvmtiAgent
+ * @run testng LoadAgentDcmdTest
+ */
+public class LoadAgentDcmdTest {
+
+    public String getLibInstrumentPath() throws FileNotFoundException {
+        String jdkPath = System.getProperty("test.jdk");
+
+        if (jdkPath == null) {
+            throw new RuntimeException(
+                      "System property 'test.jdk' not set. " +
+                      "This property is normally set by jtreg. " +
+                      "When running test separately, set this property using " +
+                      "'-Dtest.jdk=/path/to/jdk'.");
+        }
+
+        Path libpath;
+        if (Platform.isWindows()) {
+            libpath = Paths.get(jdkPath, "bin", "instrument.dll");
+        } else {
+            libpath = Paths.get(jdkPath, "lib", Platform.getOsArch(), "libinstrument.so");
+        }
+
+        if (!libpath.toFile().exists()) {
+            throw new FileNotFoundException(
+                      "Could not find " + libpath.toAbsolutePath());
+        }
+
+        return libpath.toAbsolutePath().toString();
+    }
+
+    public void run(CommandExecutor executor)  {
+        try{
+            PrintWriter pw = new PrintWriter("MANIFEST.MF");
+            pw.println("Agent-Class: SimpleJvmtiAgent");
+            pw.close();
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(new String[] { JDKToolFinder.getJDKTool("jar"),
+                                      "cmf",
+                                      "MANIFEST.MF",
+                                      "agent.jar",
+                                      "SimpleJvmtiAgent.class"});
+            pb.start().waitFor();
+
+            String libpath = getLibInstrumentPath();
+
+            // Test 1: No argument
+            OutputAnalyzer output = executor.execute("JVMTI.agent_load " +
+                                                     libpath + " agent.jar");
+            output.stderrShouldBeEmpty();
+
+            // Test 2: With argument
+            output = executor.execute("JVMTI.agent_load " +
+                                      libpath + " \"agent.jar=foo=bar\"");
+            output.stderrShouldBeEmpty();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void jmx() throws Throwable {
+        run(new JMXExecutor());
+    }
+
+    @Test
+    public void cli() throws Throwable {
+        run(new PidJcmdExecutor());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/jvmti/LoadJavaAgentDcmdTest.java	Thu Feb 18 23:26:43 2016 +0900
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+import java.io.*;
+import jdk.test.lib.*;
+import jdk.test.lib.dcmd.*;
+import org.testng.annotations.Test;
+
+/*
+ * Test to attach JVMTI java agent.
+ *
+ * @test
+ * @bug 8147388
+ * @library /testlibrary
+ * @modules java.base/sun.misc
+ *          java.compiler
+ *          java.instrument
+ *          java.management
+ *          jdk.jvmstat/sun.jvmstat.monitor
+ * @build ClassFileInstaller jdk.test.lib.* SimpleJvmtiAgent
+ * @run main ClassFileInstaller SimpleJvmtiAgent
+ * @run testng LoadJavaAgentDcmdTest
+ */
+public class LoadJavaAgentDcmdTest {
+    public void run(CommandExecutor executor)  {
+        try{
+            PrintWriter pw = new PrintWriter("MANIFEST.MF");
+            pw.println("Agent-Class: SimpleJvmtiAgent");
+            pw.close();
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(new String[] { JDKToolFinder.getJDKTool("jar"),
+                                      "cmf",
+                                      "MANIFEST.MF",
+                                      "agent.jar",
+                                      "SimpleJvmtiAgent.class"});
+            pb.start().waitFor();
+
+            // Test 1: No argument
+            OutputAnalyzer output = executor.execute("JVMTI.agent_load " +
+                                                     "agent.jar");
+            output.stderrShouldBeEmpty();
+
+            // Test 2: With argument
+            output = executor.execute("JVMTI.agent_load " +
+                                      "\"agent.jar=foo=bar\"");
+            output.stderrShouldBeEmpty();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void jmx() throws Throwable {
+        run(new JMXExecutor());
+    }
+
+    @Test
+    public void cli() throws Throwable {
+        run(new PidJcmdExecutor());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/serviceability/dcmd/jvmti/SimpleJvmtiAgent.java	Thu Feb 18 23:26:43 2016 +0900
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+import java.lang.instrument.*;
+
+public class SimpleJvmtiAgent {
+    public static void agentmain(String agentArgs, Instrumentation instrumentation) {
+        System.out.println("attach succeeded (args: \"" + agentArgs + "\")");
+    }
+}