Projects
Mega:23.03
openjdk-1.8.0
_service:tar_scm:8268893-jcmd-to-trim-the-glibc...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:8268893-jcmd-to-trim-the-glibc-heap.patch of Package openjdk-1.8.0
From 1b97a08d822b7e2388ded07c696fffe70b39697a Mon Sep 17 00:00:00 2001 From: eapen <zhangyipeng7@huawei.com> Date: Mon, 12 Dec 2022 17:40:09 +0800 Subject: [PATCH 12/33] I68TO2: 8268893: jcmd to trim the glibc heap --- hotspot/src/os/linux/vm/os_linux.cpp | 57 ++++--- hotspot/src/os/linux/vm/os_linux.hpp | 17 ++ hotspot/src/os/linux/vm/trimCHeapDCmd.cpp | 77 +++++++++ hotspot/src/os/linux/vm/trimCHeapDCmd.hpp | 52 ++++++ .../src/share/vm/services/diagnosticCommand.cpp | 7 + .../test/serviceability/dcmd/TrimLibcHeapTest.java | 53 ++++++ .../oracle/java/testlibrary/CommandExecutor.java | 73 ++++++++ .../java/testlibrary/CommandExecutorException.java | 36 ++++ .../com/oracle/java/testlibrary/JMXExecutor.java | 185 +++++++++++++++++++++ 9 files changed, 532 insertions(+), 25 deletions(-) create mode 100644 hotspot/src/os/linux/vm/trimCHeapDCmd.cpp create mode 100644 hotspot/src/os/linux/vm/trimCHeapDCmd.hpp create mode 100644 hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java create mode 100644 hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java create mode 100644 hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java create mode 100644 hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index 1a3504f..c687b1c 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -2290,44 +2290,51 @@ void os::Linux::print_system_memory_info(outputStream* st) { st->cr(); } -void os::Linux::print_process_memory_info(outputStream* st) { - - st->print_cr("Process Memory:"); - - // Print virtual and resident set size; peak values; swap; and for - // rss its components if the kernel is recent enough. - ssize_t vmsize = -1, vmpeak = -1, vmswap = -1, - vmrss = -1, vmhwm = -1, rssanon = -1, rssfile = -1, rssshmem = -1; - const int num_values = 8; - int num_found = 0; +bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) { FILE* f = ::fopen("/proc/self/status", "r"); + const int num_values = sizeof(os::Linux::meminfo_t) / sizeof(size_t); + int num_found = 0; char buf[256]; + info->vmsize = info->vmpeak = info->vmrss = info->vmhwm = info->vmswap = + info->rssanon = info->rssfile = info->rssshmem = -1; if (f != NULL) { while (::fgets(buf, sizeof(buf), f) != NULL && num_found < num_values) { - if ( (vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &vmsize) == 1) || - (vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &vmpeak) == 1) || - (vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &vmswap) == 1) || - (vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &vmhwm) == 1) || - (vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &vmrss) == 1) || - (rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &rssanon) == 1) || - (rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &rssfile) == 1) || - (rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &rssshmem) == 1) + if ( (info->vmsize == -1 && sscanf(buf, "VmSize: " SSIZE_FORMAT " kB", &info->vmsize) == 1) || + (info->vmpeak == -1 && sscanf(buf, "VmPeak: " SSIZE_FORMAT " kB", &info->vmpeak) == 1) || + (info->vmswap == -1 && sscanf(buf, "VmSwap: " SSIZE_FORMAT " kB", &info->vmswap) == 1) || + (info->vmhwm == -1 && sscanf(buf, "VmHWM: " SSIZE_FORMAT " kB", &info->vmhwm) == 1) || + (info->vmrss == -1 && sscanf(buf, "VmRSS: " SSIZE_FORMAT " kB", &info->vmrss) == 1) || + (info->rssanon == -1 && sscanf(buf, "RssAnon: " SSIZE_FORMAT " kB", &info->rssanon) == 1) || // Needs Linux 4.5 + (info->rssfile == -1 && sscanf(buf, "RssFile: " SSIZE_FORMAT " kB", &info->rssfile) == 1) || // Needs Linux 4.5 + (info->rssshmem == -1 && sscanf(buf, "RssShmem: " SSIZE_FORMAT " kB", &info->rssshmem) == 1) // Needs Linux 4.5 ) { num_found ++; } } fclose(f); + return true; + } + return false; +} + +void os::Linux::print_process_memory_info(outputStream* st) { - st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmsize, vmpeak); - st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", vmrss, vmhwm); - if (rssanon != -1) { // requires kernel >= 4.5 + st->print_cr("Process Memory:"); + + // Print virtual and resident set size; peak values; swap; and for + // rss its components if the kernel is recent enough. + meminfo_t info; + if (query_process_memory_info(&info)) { + st->print_cr("Virtual Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmsize, info.vmpeak); + st->print("Resident Set Size: " SSIZE_FORMAT "K (peak: " SSIZE_FORMAT "K)", info.vmrss, info.vmhwm); + if (info.rssanon != -1) { // requires kernel >= 4.5 st->print(" (anon: " SSIZE_FORMAT "K, file: " SSIZE_FORMAT "K, shmem: " SSIZE_FORMAT "K)", - rssanon, rssfile, rssshmem); + info.rssanon, info.rssfile, info.rssshmem); } st->cr(); - if (vmswap != -1) { // requires kernel >= 2.6.34 - st->print_cr("Swapped out: " SSIZE_FORMAT "K", vmswap); + if (info.vmswap != -1) { // requires kernel >= 2.6.34 + st->print_cr("Swapped out: " SSIZE_FORMAT "K", info.vmswap); } } else { st->print_cr("Could not open /proc/self/status to get process memory related information"); @@ -2344,7 +2351,7 @@ void os::Linux::print_process_memory_info(outputStream* st) { const size_t total_allocated = (size_t)(unsigned)mi.uordblks; st->print("C-Heap outstanding allocations: " SIZE_FORMAT "K", total_allocated / K); // Since mallinfo members are int, glibc values may have wrapped. Warn about this. - if ((vmrss * K) > UINT_MAX && (vmrss * K) > (total_allocated + UINT_MAX)) { + if ((info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX)) { st->print(" (may have wrapped)"); } st->cr(); diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp index 066b03a..2c4efff 100644 --- a/hotspot/src/os/linux/vm/os_linux.hpp +++ b/hotspot/src/os/linux/vm/os_linux.hpp @@ -243,6 +243,23 @@ class Linux { public: static pthread_condattr_t* condAttr() { return _condattr; } + // Output structure for query_process_memory_info() + struct meminfo_t { + ssize_t vmsize; // current virtual size + ssize_t vmpeak; // peak virtual size + ssize_t vmrss; // current resident set size + ssize_t vmhwm; // peak resident set size + ssize_t vmswap; // swapped out + ssize_t rssanon; // resident set size (anonymous mappings, needs 4.5) + ssize_t rssfile; // resident set size (file mappings, needs 4.5) + ssize_t rssshmem; // resident set size (shared mappings, needs 4.5) + }; + + // Attempts to query memory information about the current process and return it in the output structure. + // May fail (returns false) or succeed (returns true) but not all output fields are available; unavailable + // fields will contain -1. + static bool query_process_memory_info(meminfo_t* info); + // Stack repair handling // none present diff --git a/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp new file mode 100644 index 0000000..95d03d9 --- /dev/null +++ b/hotspot/src/os/linux/vm/trimCHeapDCmd.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/os.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" +#include "trimCHeapDCmd.hpp" + +#include <malloc.h> + +void TrimCLibcHeapDCmd::execute(DCmdSource source, TRAPS) { +#ifdef __GLIBC__ + stringStream ss_report(1024); // Note: before calling trim + + os::Linux::meminfo_t info1; + os::Linux::meminfo_t info2; + // Query memory before... + bool have_info1 = os::Linux::query_process_memory_info(&info1); + + _output->print_cr("Attempting trim..."); + ::malloc_trim(0); + _output->print_cr("Done."); + + // ...and after trim. + bool have_info2 = os::Linux::query_process_memory_info(&info2); + + // Print report both to output stream as well to UL + bool wrote_something = false; + if (have_info1 && have_info2) { + if (info1.vmsize != -1 && info2.vmsize != -1) { + ss_report.print_cr("Virtual size before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", + info1.vmsize, info2.vmsize, (info2.vmsize - info1.vmsize)); + wrote_something = true; + } + if (info1.vmrss != -1 && info2.vmrss != -1) { + ss_report.print_cr("RSS before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", + info1.vmrss, info2.vmrss, (info2.vmrss - info1.vmrss)); + wrote_something = true; + } + if (info1.vmswap != -1 && info2.vmswap != -1) { + ss_report.print_cr("Swap before: " SSIZE_FORMAT "k, after: " SSIZE_FORMAT "k, (" SSIZE_FORMAT "k)", + info1.vmswap, info2.vmswap, (info2.vmswap - info1.vmswap)); + wrote_something = true; + } + } + if (!wrote_something) { + ss_report.print_raw("No details available."); + } + + _output->print_raw(ss_report.base()); +#else + _output->print_cr("Not available."); +#endif +} diff --git a/hotspot/src/os/linux/vm/trimCHeapDCmd.hpp b/hotspot/src/os/linux/vm/trimCHeapDCmd.hpp new file mode 100644 index 0000000..4c5b5cc --- /dev/null +++ b/hotspot/src/os/linux/vm/trimCHeapDCmd.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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. + * + */ + +#ifndef OS_LINUX_TRIMCHEAPDCMD_HPP +#define OS_LINUX_TRIMCHEAPDCMD_HPP + +#include "services/diagnosticCommand.hpp" + +class outputStream; + +class TrimCLibcHeapDCmd : public DCmd { +public: + TrimCLibcHeapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} + static const char* name() { + return "System.trim_native_heap"; + } + static const char* description() { + return "Attempts to free up memory by trimming the C-heap."; + } + static const char* impact() { + return "Low"; + } + static const JavaPermission permission() { + JavaPermission p = { "java.lang.management.ManagementPermission", "control", NULL }; + return p; + } + virtual void execute(DCmdSource source, TRAPS); +}; + +#endif // OS_LINUX_TRIMCHEAPDCMD_HPP diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp index 358ec6e..60417b5 100644 --- a/hotspot/src/share/vm/services/diagnosticCommand.cpp +++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp @@ -36,6 +36,10 @@ #include "utilities/macros.hpp" #include "oops/objArrayOop.hpp" +#ifdef LINUX +#include "trimCHeapDCmd.hpp" +#endif + PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC void DCmdRegistrant::register_dcmds(){ @@ -65,6 +69,9 @@ void DCmdRegistrant::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RotateGCLogDCmd>(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassLoaderStatsDCmd>(full_export, true, false)); +#ifdef LINUX + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false)); +#endif // LINUX // Enhanced JMX Agent Support // These commands won't be exported via the DiagnosticCommandMBean until an diff --git a/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java new file mode 100644 index 0000000..0fe8e35 --- /dev/null +++ b/hotspot/test/serviceability/dcmd/TrimLibcHeapTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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 org.testng.annotations.Test; +import com.oracle.java.testlibrary.*; + +/* + * @test + * @summary Test of diagnostic command VM.trim_libc_heap + * @library /testlibrary + * @requires os.family == "linux" + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @run testng TrimLibcHeapTest + */ +public class TrimLibcHeapTest { + public void run(CommandExecutor executor) { + OutputAnalyzer output = executor.execute("System.trim_native_heap"); + output.reportDiagnosticSummary(); + output.shouldMatch("(Done|Not available)"); // Not available could happen on Linux + non-glibc (eg. muslc) + if (output.firstMatch("Done") != null) { + output.shouldMatch("(Virtual size before|RSS before|Swap before|No details available)"); + } + } + + @Test + public void jmx() { + run(new JMXExecutor()); + } +} diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java new file mode 100644 index 0000000..e95a437 --- /dev/null +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutor.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, 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. + */ + +package com.oracle.java.testlibrary; + +/** + * Abstract base class for Diagnostic Command executors + */ +public abstract class CommandExecutor { + + /** + * Execute a diagnostic command + * + * @param cmd The diagnostic command to execute + * @return an {@link jdk.testlibrary.OutputAnalyzer} encapsulating the output of the command + * @throws CommandExecutorException if there is an exception on the "calling side" while trying to execute the + * Diagnostic Command. Exceptions thrown on the remote side are available as textual representations in + * stderr, regardless of the specific executor used. + */ + public final OutputAnalyzer execute(String cmd) throws CommandExecutorException { + return execute(cmd, false); + } + + /** + * Execute a diagnostic command + * + * @param cmd The diagnostic command to execute + * @param silent Do not print the command output + * @return an {@link jdk.testlibrary.OutputAnalyzer} encapsulating the output of the command + * @throws CommandExecutorException if there is an exception on the "calling side" while trying to execute the + * Diagnostic Command. Exceptions thrown on the remote side are available as textual representations in + * stderr, regardless of the specific executor used. + */ + public final OutputAnalyzer execute(String cmd, boolean silent) throws CommandExecutorException { + if (!silent) { + System.out.printf("Running DCMD '%s' through '%s'%n", cmd, this.getClass().getSimpleName()); + } + + OutputAnalyzer oa = executeImpl(cmd); + + if (!silent) { + System.out.println("---------------- stdout ----------------"); + System.out.println(oa.getStdout()); + System.out.println("---------------- stderr ----------------"); + System.out.println(oa.getStderr()); + System.out.println("----------------------------------------"); + System.out.println(); + } + return oa; + } + + protected abstract OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException; +} diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java new file mode 100644 index 0000000..1857a23 --- /dev/null +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/CommandExecutorException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, 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. + */ + +package com.oracle.java.testlibrary; + +/** + * CommandExecutorException encapsulates exceptions thrown (on the "calling side") from the execution of Diagnostic + * Commands + */ +public class CommandExecutorException extends RuntimeException { + private static final long serialVersionUID = -7039597746579144280L; + + public CommandExecutorException(String message, Throwable e) { + super(message, e); + } +} diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java new file mode 100644 index 0000000..317fc5c --- /dev/null +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/JMXExecutor.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2015, 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. + */ + +package com.oracle.java.testlibrary; + +import javax.management.*; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.lang.management.ManagementFactory; + +import java.util.HashMap; + +/** + * Executes Diagnostic Commands on the target VM (specified by a host/port combination or a full JMX Service URL) using + * the JMX interface. If the target is not the current VM, the JMX Remote interface must be enabled beforehand. + */ +public class JMXExecutor extends CommandExecutor { + + private final MBeanServerConnection mbs; + + /** + * Instantiates a new JMXExecutor targeting the current VM + */ + public JMXExecutor() { + super(); + mbs = ManagementFactory.getPlatformMBeanServer(); + } + + /** + * Instantiates a new JMXExecutor targeting the VM indicated by the given host/port combination or a full JMX + * Service URL + * + * @param target a host/port combination on the format "host:port" or a full JMX Service URL of the target VM + */ + public JMXExecutor(String target) { + String urlStr; + + if (target.matches("^\\w[\\w\\-]*(\\.[\\w\\-]+)*:\\d+$")) { + /* Matches "hostname:port" */ + urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s/jmxrmi", target); + } else if (target.startsWith("service:")) { + urlStr = target; + } else { + throw new IllegalArgumentException("Could not recognize target string: " + target); + } + + try { + JMXServiceURL url = new JMXServiceURL(urlStr); + JMXConnector c = JMXConnectorFactory.connect(url, new HashMap<>()); + mbs = c.getMBeanServerConnection(); + } catch (IOException e) { + throw new CommandExecutorException("Could not initiate connection to target: " + target, e); + } + } + + protected OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException { + String stdout = ""; + String stderr = ""; + + String[] cmdParts = cmd.split(" ", 2); + String operation = commandToMethodName(cmdParts[0]); + Object[] dcmdArgs = produceArguments(cmdParts); + String[] signature = {String[].class.getName()}; + + ObjectName beanName = getMBeanName(); + + try { + stdout = (String) mbs.invoke(beanName, operation, dcmdArgs, signature); + } + + /* Failures on the "local" side, the one invoking the command. */ + catch (ReflectionException e) { + Throwable cause = e.getCause(); + if (cause instanceof NoSuchMethodException) { + /* We want JMXExecutor to match the behavior of the other CommandExecutors */ + String message = "Unknown diagnostic command: " + operation; + stderr = exceptionTraceAsString(new IllegalArgumentException(message, e)); + } else { + rethrowExecutorException(operation, dcmdArgs, e); + } + } + + /* Failures on the "local" side, the one invoking the command. */ + catch (InstanceNotFoundException | IOException e) { + rethrowExecutorException(operation, dcmdArgs, e); + } + + /* Failures on the remote side, the one executing the invoked command. */ + catch (MBeanException e) { + stdout = exceptionTraceAsString(e); + } + + return new OutputAnalyzer(stdout, stderr); + } + + private void rethrowExecutorException(String operation, Object[] dcmdArgs, + Exception e) throws CommandExecutorException { + String message = String.format("Could not invoke: %s %s", operation, + String.join(" ", (String[]) dcmdArgs[0])); + throw new CommandExecutorException(message, e); + } + + private ObjectName getMBeanName() throws CommandExecutorException { + String MBeanName = "com.sun.management:type=DiagnosticCommand"; + + try { + return new ObjectName(MBeanName); + } catch (MalformedObjectNameException e) { + String message = "MBean not found: " + MBeanName; + throw new CommandExecutorException(message, e); + } + } + + private Object[] produceArguments(String[] cmdParts) { + Object[] dcmdArgs = {new String[0]}; /* Default: No arguments */ + + if (cmdParts.length == 2) { + dcmdArgs[0] = cmdParts[1].split(" "); + } + return dcmdArgs; + } + + /** + * Convert from diagnostic command to MBean method name + * + * Examples: + * help --> help + * VM.version --> vmVersion + * VM.command_line --> vmCommandLine + */ + private static String commandToMethodName(String cmd) { + String operation = ""; + boolean up = false; /* First letter is to be lower case */ + + /* + * If a '.' or '_' is encountered it is not copied, + * instead the next character will be converted to upper case + */ + for (char c : cmd.toCharArray()) { + if (('.' == c) || ('_' == c)) { + up = true; + } else if (up) { + operation = operation.concat(Character.toString(c).toUpperCase()); + up = false; + } else { + operation = operation.concat(Character.toString(c).toLowerCase()); + } + } + + return operation; + } + + private static String exceptionTraceAsString(Throwable cause) { + StringWriter sw = new StringWriter(); + cause.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + +} -- 1.8.3.1
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2