/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.monitor.jvm; import org.apache.lucene.util.Constants; import org.opensearch.common.Booleans; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtils; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.node.ReportingService; import java.io.IOException; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.ManagementPermission; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.PlatformManagedObject; import java.lang.management.RuntimeMXBean; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Map; /** * Holds information about the JVM * * @opensearch.internal */ public class JvmInfo implements ReportingService.Info { private static JvmInfo INSTANCE; static { RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); long heapInit = memoryMXBean.getHeapMemoryUsage().getInit() < 0 ? 0 : memoryMXBean.getHeapMemoryUsage().getInit(); long heapMax = memoryMXBean.getHeapMemoryUsage().getMax() < 0 ? 0 : memoryMXBean.getHeapMemoryUsage().getMax(); long nonHeapInit = memoryMXBean.getNonHeapMemoryUsage().getInit() < 0 ? 0 : memoryMXBean.getNonHeapMemoryUsage().getInit(); long nonHeapMax = memoryMXBean.getNonHeapMemoryUsage().getMax() < 0 ? 0 : memoryMXBean.getNonHeapMemoryUsage().getMax(); long directMemoryMax = 0; try { Class vmClass = Class.forName("sun.misc.VM"); directMemoryMax = (Long) vmClass.getMethod("maxDirectMemory").invoke(null); } catch (Exception t) { // ignore } String[] inputArguments = runtimeMXBean.getInputArguments().toArray(new String[0]); Mem mem = new Mem(heapInit, heapMax, nonHeapInit, nonHeapMax, directMemoryMax); String bootClassPath; try { bootClassPath = runtimeMXBean.getBootClassPath(); } catch (UnsupportedOperationException e) { // oracle java 9 bootClassPath = System.getProperty("sun.boot.class.path"); if (bootClassPath == null) { // something else bootClassPath = ""; } } String classPath = runtimeMXBean.getClassPath(); Map systemProperties = Collections.unmodifiableMap(runtimeMXBean.getSystemProperties()); List gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans(); String[] gcCollectors = new String[gcMxBeans.size()]; for (int i = 0; i < gcMxBeans.size(); i++) { GarbageCollectorMXBean gcMxBean = gcMxBeans.get(i); gcCollectors[i] = gcMxBean.getName(); } List memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans(); String[] memoryPools = new String[memoryPoolMXBeans.size()]; for (int i = 0; i < memoryPoolMXBeans.size(); i++) { MemoryPoolMXBean memoryPoolMXBean = memoryPoolMXBeans.get(i); memoryPools[i] = memoryPoolMXBean.getName(); } String onError = null; String onOutOfMemoryError = null; String useCompressedOops = "unknown"; String useG1GC = "unknown"; long g1RegisionSize = -1; String useSerialGC = "unknown"; long configuredInitialHeapSize = -1; long configuredMaxHeapSize = -1; try { @SuppressWarnings("unchecked") Class clazz = (Class) Class.forName( "com.sun.management.HotSpotDiagnosticMXBean" ); Class vmOptionClazz = Class.forName("com.sun.management.VMOption"); PlatformManagedObject hotSpotDiagnosticMXBean = ManagementFactory.getPlatformMXBean(clazz); Method vmOptionMethod = clazz.getMethod("getVMOption", String.class); Method valueMethod = vmOptionClazz.getMethod("getValue"); try { Object onErrorObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "OnError"); onError = (String) valueMethod.invoke(onErrorObject); } catch (Exception ignored) {} try { Object onOutOfMemoryErrorObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "OnOutOfMemoryError"); onOutOfMemoryError = (String) valueMethod.invoke(onOutOfMemoryErrorObject); } catch (Exception ignored) {} try { Object useCompressedOopsVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseCompressedOops"); useCompressedOops = (String) valueMethod.invoke(useCompressedOopsVmOptionObject); } catch (Exception ignored) {} try { Object useG1GCVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseG1GC"); useG1GC = (String) valueMethod.invoke(useG1GCVmOptionObject); Object regionSizeVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "G1HeapRegionSize"); g1RegisionSize = Long.parseLong((String) valueMethod.invoke(regionSizeVmOptionObject)); } catch (Exception ignored) {} try { Object initialHeapSizeVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "InitialHeapSize"); configuredInitialHeapSize = Long.parseLong((String) valueMethod.invoke(initialHeapSizeVmOptionObject)); } catch (Exception ignored) {} try { Object maxHeapSizeVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "MaxHeapSize"); configuredMaxHeapSize = Long.parseLong((String) valueMethod.invoke(maxHeapSizeVmOptionObject)); } catch (Exception ignored) {} try { Object useSerialGCVmOptionObject = vmOptionMethod.invoke(hotSpotDiagnosticMXBean, "UseSerialGC"); useSerialGC = (String) valueMethod.invoke(useSerialGCVmOptionObject); } catch (Exception ignored) {} } catch (Exception ignored) { } final boolean bundledJdk = Booleans.parseBoolean(System.getProperty("opensearch.bundled_jdk", Boolean.FALSE.toString())); final Boolean usingBundledJdkOrJre = bundledJdk ? usingBundledJdkOrJre() : null; INSTANCE = new JvmInfo( JvmPid.getPid(), System.getProperty("java.version"), runtimeMXBean.getVmName(), runtimeMXBean.getVmVersion(), runtimeMXBean.getVmVendor(), bundledJdk, usingBundledJdkOrJre, runtimeMXBean.getStartTime(), configuredInitialHeapSize, configuredMaxHeapSize, mem, inputArguments, bootClassPath, classPath, systemProperties, gcCollectors, memoryPools, onError, onOutOfMemoryError, useCompressedOops, useG1GC, useSerialGC, g1RegisionSize ); } @SuppressForbidden(reason = "PathUtils#get") private static boolean usingBundledJdkOrJre() { /* * We are using the bundled JDK if java.home is the jdk sub-directory of our working directory. This is because we always set * the working directory of Elasticsearch to home, and the bundled JDK is in the jdk sub-directory there. */ final String javaHome = System.getProperty("java.home"); final String userDir = System.getProperty("user.dir"); if (Constants.MAC_OS_X) { return PathUtils.get(javaHome).equals(PathUtils.get(userDir).resolve("jdk.app/Contents/Home").toAbsolutePath()); } else { return PathUtils.get(javaHome).equals(PathUtils.get(userDir).resolve("jre").toAbsolutePath()) || PathUtils.get(javaHome).equals(PathUtils.get(userDir).resolve("jdk").toAbsolutePath()); } } public static JvmInfo jvmInfo() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new ManagementPermission("monitor")); sm.checkPropertyAccess("*"); } return INSTANCE; } private final long pid; private final String version; private final String vmName; private final String vmVersion; private final String vmVendor; private final boolean bundledJdk; private final Boolean usingBundledJdk; private final long startTime; private final long configuredInitialHeapSize; private final long configuredMaxHeapSize; private final Mem mem; private final String[] inputArguments; private final String bootClassPath; private final String classPath; private final Map systemProperties; private final String[] gcCollectors; private final String[] memoryPools; private final String onError; private final String onOutOfMemoryError; private final String useCompressedOops; private final String useG1GC; private final String useSerialGC; private final long g1RegionSize; private JvmInfo( long pid, String version, String vmName, String vmVersion, String vmVendor, boolean bundledJdk, Boolean usingBundledJdk, long startTime, long configuredInitialHeapSize, long configuredMaxHeapSize, Mem mem, String[] inputArguments, String bootClassPath, String classPath, Map systemProperties, String[] gcCollectors, String[] memoryPools, String onError, String onOutOfMemoryError, String useCompressedOops, String useG1GC, String useSerialGC, long g1RegionSize ) { this.pid = pid; this.version = version; this.vmName = vmName; this.vmVersion = vmVersion; this.vmVendor = vmVendor; this.bundledJdk = bundledJdk; this.usingBundledJdk = usingBundledJdk; this.startTime = startTime; this.configuredInitialHeapSize = configuredInitialHeapSize; this.configuredMaxHeapSize = configuredMaxHeapSize; this.mem = mem; this.inputArguments = inputArguments; this.bootClassPath = bootClassPath; this.classPath = classPath; this.systemProperties = systemProperties; this.gcCollectors = gcCollectors; this.memoryPools = memoryPools; this.onError = onError; this.onOutOfMemoryError = onOutOfMemoryError; this.useCompressedOops = useCompressedOops; this.useG1GC = useG1GC; this.useSerialGC = useSerialGC; this.g1RegionSize = g1RegionSize; } public JvmInfo(StreamInput in) throws IOException { pid = in.readLong(); version = in.readString(); vmName = in.readString(); vmVersion = in.readString(); vmVendor = in.readString(); bundledJdk = in.readBoolean(); usingBundledJdk = in.readOptionalBoolean(); startTime = in.readLong(); inputArguments = new String[in.readInt()]; for (int i = 0; i < inputArguments.length; i++) { inputArguments[i] = in.readString(); } bootClassPath = in.readString(); classPath = in.readString(); systemProperties = in.readMap(StreamInput::readString, StreamInput::readString); mem = new Mem(in); gcCollectors = in.readStringArray(); memoryPools = in.readStringArray(); useCompressedOops = in.readString(); // the following members are only used locally for bootstrap checks, never serialized nor printed out this.configuredMaxHeapSize = -1; this.configuredInitialHeapSize = -1; this.onError = null; this.onOutOfMemoryError = null; this.useG1GC = "unknown"; this.useSerialGC = "unknown"; this.g1RegionSize = -1; } @Override public void writeTo(StreamOutput out) throws IOException { out.writeLong(pid); out.writeString(version); out.writeString(vmName); out.writeString(vmVersion); out.writeString(vmVendor); out.writeBoolean(bundledJdk); out.writeOptionalBoolean(usingBundledJdk); out.writeLong(startTime); out.writeInt(inputArguments.length); for (String inputArgument : inputArguments) { out.writeString(inputArgument); } out.writeString(bootClassPath); out.writeString(classPath); out.writeVInt(this.systemProperties.size()); for (Map.Entry entry : systemProperties.entrySet()) { out.writeString(entry.getKey()); out.writeString(entry.getValue()); } mem.writeTo(out); out.writeStringArray(gcCollectors); out.writeStringArray(memoryPools); out.writeString(useCompressedOops); } /** * The process id. */ public long pid() { return this.pid; } /** * The process id. */ public long getPid() { return pid; } public String version() { return this.version; } public String getVersion() { return this.version; } public int versionAsInteger() { try { int i = 0; StringBuilder sVersion = new StringBuilder(); for (; i < version.length(); i++) { if (!Character.isDigit(version.charAt(i)) && version.charAt(i) != '.') { break; } if (version.charAt(i) != '.') { sVersion.append(version.charAt(i)); } } if (i == 0) { return -1; } return Integer.parseInt(sVersion.toString()); } catch (Exception e) { return -1; } } public int versionUpdatePack() { try { int i = 0; StringBuilder sVersion = new StringBuilder(); for (; i < version.length(); i++) { if (!Character.isDigit(version.charAt(i)) && version.charAt(i) != '.') { break; } if (version.charAt(i) != '.') { sVersion.append(version.charAt(i)); } } if (i == 0) { return -1; } Integer.parseInt(sVersion.toString()); int from; if (version.charAt(i) == '_') { // 1.7.0_4 from = ++i; } else if (version.charAt(i) == '-' && version.charAt(i + 1) == 'u') { // 1.7.0-u2-b21 i = i + 2; from = i; } else { return -1; } for (; i < version.length(); i++) { if (!Character.isDigit(version.charAt(i)) && version.charAt(i) != '.') { break; } } if (from == i) { return -1; } return Integer.parseInt(version.substring(from, i)); } catch (Exception e) { return -1; } } public String getVmName() { return this.vmName; } public String getVmVersion() { return this.vmVersion; } public String getVmVendor() { return this.vmVendor; } public boolean getBundledJdk() { return bundledJdk; } public Boolean getUsingBundledJdk() { return usingBundledJdk; } public long getStartTime() { return this.startTime; } public Mem getMem() { return this.mem; } public String[] getInputArguments() { return this.inputArguments; } public String getBootClassPath() { return this.bootClassPath; } public String getClassPath() { return this.classPath; } public Map getSystemProperties() { return this.systemProperties; } public long getConfiguredInitialHeapSize() { return configuredInitialHeapSize; } public long getConfiguredMaxHeapSize() { return configuredMaxHeapSize; } public String onError() { return onError; } public String onOutOfMemoryError() { return onOutOfMemoryError; } /** * The value of the JVM flag UseCompressedOops, if available otherwise * "unknown". The value "unknown" indicates that an attempt was * made to obtain the value of the flag on this JVM and the attempt * failed. * * @return the value of the JVM flag UseCompressedOops or "unknown" */ public String useCompressedOops() { return this.useCompressedOops; } public String useG1GC() { return this.useG1GC; } public String useSerialGC() { return this.useSerialGC; } public long getG1RegionSize() { return g1RegionSize; } public String[] getGcCollectors() { return gcCollectors; } public String[] getMemoryPools() { return memoryPools; } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(Fields.JVM); builder.field(Fields.PID, pid); builder.field(Fields.VERSION, version); builder.field(Fields.VM_NAME, vmName); builder.field(Fields.VM_VERSION, vmVersion); builder.field(Fields.VM_VENDOR, vmVendor); builder.field(Fields.BUNDLED_JDK, bundledJdk); builder.field(Fields.USING_BUNDLED_JDK, usingBundledJdk); builder.timeField(Fields.START_TIME_IN_MILLIS, Fields.START_TIME, startTime); builder.startObject(Fields.MEM); builder.humanReadableField(Fields.HEAP_INIT_IN_BYTES, Fields.HEAP_INIT, new ByteSizeValue(mem.heapInit)); builder.humanReadableField(Fields.HEAP_MAX_IN_BYTES, Fields.HEAP_MAX, new ByteSizeValue(mem.heapMax)); builder.humanReadableField(Fields.NON_HEAP_INIT_IN_BYTES, Fields.NON_HEAP_INIT, new ByteSizeValue(mem.nonHeapInit)); builder.humanReadableField(Fields.NON_HEAP_MAX_IN_BYTES, Fields.NON_HEAP_MAX, new ByteSizeValue(mem.nonHeapMax)); builder.humanReadableField(Fields.DIRECT_MAX_IN_BYTES, Fields.DIRECT_MAX, new ByteSizeValue(mem.directMemoryMax)); builder.endObject(); builder.array(Fields.GC_COLLECTORS, gcCollectors); builder.array(Fields.MEMORY_POOLS, memoryPools); builder.field(Fields.USING_COMPRESSED_OOPS, useCompressedOops); builder.field(Fields.INPUT_ARGUMENTS, inputArguments); builder.endObject(); return builder; } static final class Fields { static final String JVM = "jvm"; static final String PID = "pid"; static final String VERSION = "version"; static final String VM_NAME = "vm_name"; static final String VM_VERSION = "vm_version"; static final String VM_VENDOR = "vm_vendor"; static final String BUNDLED_JDK = "bundled_jdk"; static final String USING_BUNDLED_JDK = "using_bundled_jdk"; static final String START_TIME = "start_time"; static final String START_TIME_IN_MILLIS = "start_time_in_millis"; static final String MEM = "mem"; static final String HEAP_INIT = "heap_init"; static final String HEAP_INIT_IN_BYTES = "heap_init_in_bytes"; static final String HEAP_MAX = "heap_max"; static final String HEAP_MAX_IN_BYTES = "heap_max_in_bytes"; static final String NON_HEAP_INIT = "non_heap_init"; static final String NON_HEAP_INIT_IN_BYTES = "non_heap_init_in_bytes"; static final String NON_HEAP_MAX = "non_heap_max"; static final String NON_HEAP_MAX_IN_BYTES = "non_heap_max_in_bytes"; static final String DIRECT_MAX = "direct_max"; static final String DIRECT_MAX_IN_BYTES = "direct_max_in_bytes"; static final String GC_COLLECTORS = "gc_collectors"; static final String MEMORY_POOLS = "memory_pools"; static final String USING_COMPRESSED_OOPS = "using_compressed_ordinary_object_pointers"; static final String INPUT_ARGUMENTS = "input_arguments"; } /** * Memory information. * * @opensearch.internal */ public static class Mem implements Writeable { private final long heapInit; private final long heapMax; private final long nonHeapInit; private final long nonHeapMax; private final long directMemoryMax; public Mem(long heapInit, long heapMax, long nonHeapInit, long nonHeapMax, long directMemoryMax) { this.heapInit = heapInit; this.heapMax = heapMax; this.nonHeapInit = nonHeapInit; this.nonHeapMax = nonHeapMax; this.directMemoryMax = directMemoryMax; } public Mem(StreamInput in) throws IOException { this.heapInit = in.readVLong(); this.heapMax = in.readVLong(); this.nonHeapInit = in.readVLong(); this.nonHeapMax = in.readVLong(); this.directMemoryMax = in.readVLong(); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVLong(heapInit); out.writeVLong(heapMax); out.writeVLong(nonHeapInit); out.writeVLong(nonHeapMax); out.writeVLong(directMemoryMax); } public ByteSizeValue getHeapInit() { return new ByteSizeValue(heapInit); } public ByteSizeValue getHeapMax() { return new ByteSizeValue(heapMax); } public ByteSizeValue getNonHeapInit() { return new ByteSizeValue(nonHeapInit); } public ByteSizeValue getNonHeapMax() { return new ByteSizeValue(nonHeapMax); } public ByteSizeValue getDirectMemoryMax() { return new ByteSizeValue(directMemoryMax); } } }