From 5b8ecf7c24ca91a37b2073d4ee1a462f7aab5760 Mon Sep 17 00:00:00 2001 From: dowzyx Date: Thu, 9 Mar 2023 20:30:06 +0800 Subject: [PATCH 16/30] refactor: modify jvmprobe to support pod --- src/probes/extends/java.probe/build.sh | 100 +++--- src/probes/extends/java.probe/install.sh | 5 +- .../jvm.probe/config/META-INF/MANIFEST.MF | 1 - .../java.probe/jvm.probe/jvm_probe.meta | 231 ++++++++++--- .../java.probe/jvm.probe/src/JvmProbe.java | 305 +++++++++++++----- .../extends/java.probe/jvm.probe/src/Vm.java | 173 ---------- .../jvm.probe/src/agent/JvmProbeAgent.java | 188 +++++++++++ .../src/agent/config/META-INF/MANIFEST.MF | 6 + 8 files changed, 657 insertions(+), 352 deletions(-) delete mode 100644 src/probes/extends/java.probe/jvm.probe/src/Vm.java create mode 100644 src/probes/extends/java.probe/jvm.probe/src/agent/JvmProbeAgent.java create mode 100644 src/probes/extends/java.probe/jvm.probe/src/agent/config/META-INF/MANIFEST.MF diff --git a/src/probes/extends/java.probe/build.sh b/src/probes/extends/java.probe/build.sh index 0ea7782..3c6f4e0 100755 --- a/src/probes/extends/java.probe/build.sh +++ b/src/probes/extends/java.probe/build.sh @@ -4,95 +4,79 @@ PRJ_DIR=$(dirname $(readlink -f "$0")) BUILD_FILES=${PRJ_DIR}/jvm.probe cd ${BUILD_FILES} -function find_jars() +function find_cmd_jar() { - if [ -z $JAVA_HOME ]; - then - # find jdk - clink_path=$(echo $(ls -lrt $javac_link) | awk -F " " '{print $NF}' ) - link_path=$(echo $(ls -lrt $clink_path) | awk -F " " '{print $NF}' ) - jdk_path=$(dirname $(dirname $link_path)) - dir=$jdk_path - else - dir=$JAVA_HOME - fi - - #tools.jar - if [ -e $dir/lib/tools.jar ]; - then - mkdir -p lib - cp $dir/lib/tools.jar lib/ - else - echo "Error: tools.jar not found" - return 1 - fi - - #management.jar - if [ -e $dir/jre/lib/management-agent.jar ]; + if [ -z $(which jar 2>/dev/null) ]; then - cp $dir/jre/lib/management-agent.jar lib/ - else - echo "Error: management-agent.jar not found" + echo "Error: jar command not found" return 1 - fi - - return 0 + else + return 0 + fi } -function make_probe_jar() +function make_probe_agent_jar() { - mkdir -p tmp - cd tmp - javac -cp ../lib/tools.jar ../src/*.java -d ./ + mkdir -p tmp + cd tmp + javac ../src/agent/JvmProbeAgent.java -d ./ + cd .. + jar cfm JvmProbeAgent.jar src/agent/config/META-INF/MANIFEST.MF -C tmp/ . - if [ -z $(which jar 2>/dev/null) ]; - then - echo "Error: jar command not found" - return 1 - else - jar xf ../lib/tools.jar #>/dev/null 2>&1 - cp ../lib/management-agent.jar ./ - cd .. - jar cfm JvmProbe.jar config/META-INF/MANIFEST.MF -C tmp/ . #2>/dev/null - fi + rm -rf tmp 2>/dev/null + return 0 +} + +function make_probe_jar() +{ + mkdir -p tmp + cd tmp/ + javac ../src/JvmProbe.java -d . + cd .. + jar cfm JvmProbe.jar config/META-INF/MANIFEST.MF -C tmp/ . - rm -rf tmp 2>/dev/null - - return 0 + rm -rf tmp 2>/dev/null + return 0 } function compile_clean() { - rm -rf lib 2>/dev/null - rm -rf tmp 2>/dev/null + rm -rf tmp 2>/dev/null } if [ "$1" == "-c" -o "$1" == "--clean" ]; then compile_clean + rm -f *.jar 2>/dev/null exit fi java_link=$(which java 2>/dev/null) javac_link=$(which javac 2>/dev/null) - -if [ -z $java_link ] || [ -z $javac_link ]; + +if [ -z $java_link ] || [ -z $javac_link ]; then echo "Error: java and javac : command not found" exit 1 -else - find_jars +else + find_cmd_jar if [ $? -eq 1 ]; then - exit 1 - fi - + exit 1 + fi + + make_probe_agent_jar + if [ $? -eq 1 ]; + then + exit 1 + fi + make_probe_jar if [ $? -eq 1 ]; then - exit 1 + exit 1 fi - + compile_clean exit fi diff --git a/src/probes/extends/java.probe/install.sh b/src/probes/extends/java.probe/install.sh index ee653b5..4111fb3 100755 --- a/src/probes/extends/java.probe/install.sh +++ b/src/probes/extends/java.probe/install.sh @@ -2,6 +2,7 @@ PROGRAM=$0 PRJ_DIR=$(dirname $(readlink -f "$0")) INSTALL_FILES="jvm.probe/JvmProbe.jar" +INSTALL_FILES+=" jvm.probe/JvmProbeAgent.jar" while getopts ":b:c:" opt do @@ -15,6 +16,8 @@ done cd ${PRJ_DIR} if [ ${INSTALL_PATH} ]; then mkdir -p ${INSTALL_PATH} - \cp ${INSTALL_FILES} ${INSTALL_PATH} + for file in ${INSTALL_FILES}; do + cp ${file} ${INSTALL_PATH} + done fi diff --git a/src/probes/extends/java.probe/jvm.probe/config/META-INF/MANIFEST.MF b/src/probes/extends/java.probe/jvm.probe/config/META-INF/MANIFEST.MF index 9f2a9af..fd73d72 100644 --- a/src/probes/extends/java.probe/jvm.probe/config/META-INF/MANIFEST.MF +++ b/src/probes/extends/java.probe/jvm.probe/config/META-INF/MANIFEST.MF @@ -1,5 +1,4 @@ Manifest-Version: 1.0 -Class-Path: management-agent.jar Created-By: 1.8.0_333 (Oracle Corporation) Main-Class: JvmProbe diff --git a/src/probes/extends/java.probe/jvm.probe/jvm_probe.meta b/src/probes/extends/java.probe/jvm.probe/jvm_probe.meta index bcce601..bb9d01a 100755 --- a/src/probes/extends/java.probe/jvm.probe/jvm_probe.meta +++ b/src/probes/extends/java.probe/jvm.probe/jvm_probe.meta @@ -3,100 +3,259 @@ version = "1.0.0" measurements: ( { - table_name: "jvm", + table_name: "jvm_info", entity_name: "jvm", fields: ( { - description: "ID of current java process ", + description: "ID of current java process.", type: "key", name: "tgid", }, { - description: "pkg_name-java_main_class", + description: "JVM runtime Environment.", type: "label", - name: "pkg_name_main_class", + name: "runtime", }, { - description: "JVM specification version ", + description: "....", type: "label", - name: "jvm_version", + name: "vendor", }, { - description: "JVM implementation name ", + description: "....", type: "label", - name: "jvm_type", + name: "version", }, { - description: "Garbage collectors` average usage", + description: "a fixed num 1 as metric", type: "gauge", - name: "gc_activity_util", + name: "info", + } + ) + }, + { + table_name: "jvm_process", + entity_name: "jvm", + fields: + ( + { + description: "ID of current java process.", + type: "key", + name: "tgid", }, { - description: "Garbage collector collection counts", + description: "Start time of the process since unix epoch in seconds.", type: "gauge", - name: "gc_total_count", + name: "process_start_time_seconds", }, { - description: "Total garbage collector collection time", + description: "Total user and system CPU time spent in seconds.", type: "gauge", - name: "gc_total_seconds", + name: "process_cpu_seconds_total", }, + ) + }, + { + table_name: "jvm_class", + entity_name: "jvm", + fields: + ( { - description: "Process start time in seconds", + description: "ID of current java process.", + type: "key", + name: "tgid", + }, + { + description: "Start time of the process since unix epoch in seconds.", type: "gauge", - name: "process_start_time_seconds", + name: "classes_currently_loaded", }, { - description: "Process run time in seconds", + description: "Total user and system CPU time spent in seconds.", type: "gauge", - name: "process_run_time_seconds", + name: "classes_loaded_total", + }, + ) + }, + { + table_name: "jvm_thread", + entity_name: "jvm", + fields: + ( + { + description: "ID of current java process.", + type: "key", + name: "tgid", }, { - description: "The number of threads in the current process", + description: "Current thread count of a JVM.", type: "gauge", name: "threads_current", }, { - description: "The peak number of threads of the java process", + description: "Daemon thread count of a JVM.", + type: "gauge", + name: "threads_daemon", + }, + { + description: "Peak thread count of a JVM.", type: "gauge", name: "threads_peak", }, { - description: "Deadlocked JVM thread number", + description: "Started thread count of a JVM.", + type: "gauge", + name: "threads_started_total", + }, + { + description: "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors or ownable synchronizers.", type: "gauge", name: "threads_deadlocked", }, + ) + }, + { + table_name: "jvm_mem", + entity_name: "jvm", + fields: + ( + { + description: "ID of current java process.", + type: "key", + name: "tgid", + }, + { + description: "type of JVM memory type: heap or noheap.", + type: "key", + name: "area", + }, { - description: "Used bytes of JVM _Heap memory area", + description: "Used bytes of a given JVM memory area.", type: "gauge", - name: "heap_memory_bytes_used", + name: "memory_bytes_used", }, { - description: "JVM _Heap occupation", + description: "Committed (bytes) of a given JVM memory area.", type: "gauge", - name: "heap_occuped_percent", + name: "memory_bytes_committed", }, { - description: "Used bytes of JVM _NonHeap memory area", + description: "Max (bytes) of a given JVM memory area.", type: "gauge", - name: "nonheap_memory_bytes_used", + name: "memory_bytes_max", }, { - description: "JVM _NonHeap occupation", + description: "Initial bytes of a given JVM memory area.", type: "gauge", - name: "nonheap_occuped_percent", + name: "memory_bytes_init", }, + ) + }, + { + table_name: "jvm_mem_pool", + entity_name: "jvm", + fields: + ( { - description: "The total capacity of the JVM buffer pools", - type: "gauge", - name: "buffer_pool_total_size", + description: "ID of current java process.", + type: "key", + name: "tgid", }, { - description: "the memory that the JVM uses for buffer pools", - type: "gauge", - name: "buffer_pool_used_bytes", - } + description: "....", + type: "key", + name: "pool", + }, + { + description: "Used bytes of a given JVM memory pool.", + type: "gauge", + name: "memory_pool_bytes_used", + }, + { + description: "Committed bytes of a given JVM memory pool.", + type: "gauge", + name: "memory_pool_bytes_committed", + }, + { + description: "Max bytes of a given JVM memory pool.", + type: "gauge", + name: "memory_pool_bytes_max", + }, + { + description: "Used bytes after last collection of a given JVM memory pool.", + type: "gauge", + name: "memory_pool_collection_used_bytes", + }, + { + description: "Committed after last collection bytes of a given JVM memory pool.", + type: "gauge", + name: "memory_pool_collection_committed_bytes", + }, + { + description: "Max bytes after last collection of a given JVM memory pool.", + type: "gauge", + name: "memory_pool_collection_max_bytes", + }, + ) + }, + { + table_name: "jvm_buf_pool", + entity_name: "jvm", + fields: + ( + { + description: "ID of current java process.", + type: "key", + name: "tgid", + }, + { + description: "....", + type: "label", + name: "pool", + }, + { + description: "Used bytes of a given JVM buffer pool.", + type: "gauge", + name: "buffer_pool_used_bytes", + }, + { + description: "Used buffers of a given JVM buffer pool.", + type: "gauge", + name: "buffer_pool_used_buffers", + }, + { + description: "Bytes capacity of a given JVM buffer pool.", + type: "gauge", + name: "buffer_pool_capacity_bytes", + }, + ) + }, + { + table_name: "jvm_gc", + entity_name: "jvm", + fields: + ( + { + description: "ID of current java process.", + type: "key", + name: "tgid", + }, + { + description: "....", + type: "label", + name: "gc", + }, + { + description: "Collection count of a given JVM garbage collector.", + type: "gauge", + name: "gc_collection_seconds_count", + }, + { + description: "Time spent in a given JVM garbage collector in seconds.", + type: "gauge", + name: "gc_collection_seconds_sum", + }, ) } ) diff --git a/src/probes/extends/java.probe/jvm.probe/src/JvmProbe.java b/src/probes/extends/java.probe/jvm.probe/src/JvmProbe.java index 9c67f17..81d64ba 100644 --- a/src/probes/extends/java.probe/jvm.probe/src/JvmProbe.java +++ b/src/probes/extends/java.probe/jvm.probe/src/JvmProbe.java @@ -1,83 +1,222 @@ -import com.sun.tools.attach.*; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.List; -import java.util.HashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -public class JvmProbe { - - public static List virtualMachineDescriptors; - public static ExecutorService executorService; - public static int time = 5; - public static String agentPath; - final static String agent = "management-agent.jar"; - - public static void main(String[] args) { - ArgsParse(args); - try { - agentPath = getTemporaryRes(agent); - } catch (IOException e) { - System.out.println(e.getMessage()); - System.exit(1); - } - - while (true) { - try { - probe(); - TimeUnit.SECONDS.sleep(time); - } catch (InterruptedException e) { - System.out.println(e.getMessage()); - } - } - } - - public static void ArgsParse(String[] args) { - int argsLen = args.length; - HashMap argsMap = new HashMap<>(); - for (int i = 0; i < argsLen / 2; i++) { - argsMap.put(args[i], args[i + 1]); - } - //set - if (argsMap.containsKey("-t")) { - time = Integer.parseInt(argsMap.get("-t")); - } - } - - public static void probe() { - virtualMachineDescriptors = VirtualMachine.list(); - if (executorService == null) { - executorService = Executors.newFixedThreadPool(5); - } - - for (VirtualMachineDescriptor virtualMachineDescriptor : virtualMachineDescriptors) { - executorService.submit(() -> { - try { - Vm vm = new Vm(virtualMachineDescriptor, agentPath); - vm.getData(); - } catch (IOException | AgentLoadException | AttachNotSupportedException | AgentInitializationException e) { - System.out.println(e.getMessage()); - } - }); - } - } - - public static String getTemporaryRes(String resource) throws IOException { - //read embedded resource from this JAR - InputStream inputStream = JvmProbe.class.getResourceAsStream(resource); - if(inputStream == null){ - throw new IOException("Resource not found in the JAR"); - } - // create a temporary file - File temporaryFile = File.createTempFile("resource", null); - temporaryFile.deleteOnExit(); - // Copy the resource data into the temporary file - Files.copy(inputStream, temporaryFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - return temporaryFile.getAbsolutePath(); - } -} +import java.io.IOException; +import java.io.File; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.concurrent.TimeUnit; +import java.lang.Process; +import java.lang.ProcessBuilder; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +public class JvmProbe { + + private static String PROC_COMM_CMD = "/usr/bin/cat /proc/%d/comm 2> /dev/null"; + private static String PROC_STATUS_CMD = + "/usr/bin/cat /proc/%s/status 2> /dev/null | grep -w NStgid | awk -F ' ' '{print $NF}'"; + private static String BPFTOOL_DUMP_PROCMAP_CMD = + "bpftool map dump pinned /sys/fs/bpf/probe/proc_map 2> /dev/null | grep key | awk -F ' ' '{print $5$4$3$2}'"; + // jvm_attach load instrument false "/tmp/JvmProbeAgent.jar=," + private static String ATTACH_CMD = "%s %s %s load instrument false \"%s=%s,%s\""; + private final static String ATTACH_BIN_PATH = "/opt/gala-gopher/extend_probes/jvm_attach"; + private final static String HOST_JAR_DIR = "/opt/gala-gopher/extend_probes"; + private final static String NS_TMP_DIR = "/tmp"; + private final static String AGENT_JAR_NAME = "JvmProbeAgent.jar"; + private static final String METRIC_FILE_NAME = "jvm-metrics.txt"; + private static int period = 5; + + private static class ProcessInfo { + String pid; + String nspid; + } + + private static void argsParse(String[] args) { + int argsLen = args.length; + HashMap argsMap = new HashMap<>(); + for (int i = 0; i < argsLen / 2; i++) { + argsMap.put(args[i], args[i + 1]); + } + //set + if (argsMap.containsKey("-t")) { + period = Integer.parseInt(argsMap.get("-t")); + } + } + + private static Boolean detactProcIsJava(int procId) throws IOException { + List commandList = new ArrayList<>(); + commandList.add("sh"); + commandList.add("-c"); + commandList.add(String.format(PROC_COMM_CMD, procId)); + + ProcessBuilder pb = new ProcessBuilder(commandList); + Process process = pb.start(); + + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line = null; + while ((line = br.readLine()) != null) { + if (line.equals("java")) { + return true; + } + } + return false; + } + + private static List checkProcessToAttach() throws IOException, InterruptedException { + List pidList = new ArrayList<>(); + List commandList = new ArrayList<>(); + commandList.add("sh"); + commandList.add("-c"); + commandList.add(BPFTOOL_DUMP_PROCMAP_CMD); + + ProcessBuilder pb = new ProcessBuilder(commandList); + Process process = pb.start(); + int ret = process.waitFor(); + if (ret != 0) { + System.out.println("Process exited with code: " + ret + " cmd: " + commandList); + return pidList; // empty list + } + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line = null; + while ((line = br.readLine()) != null) { + int decimal = Integer.decode(String.format("0x%s", line)); + if (detactProcIsJava(decimal)) { + pidList.add(decimal); + } + } + + return pidList; + } + + private static ProcessInfo setEffectiveId(String pid) throws IOException { + ProcessInfo attachInfo = new ProcessInfo(); + List commandList = new ArrayList<>(); + commandList.add("sh"); + commandList.add("-c"); + commandList.add(String.format(PROC_STATUS_CMD, pid)); + + ProcessBuilder pb = new ProcessBuilder(commandList); + Process process = pb.start(); + + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line = null; + while ((line = br.readLine()) != null) { + attachInfo.nspid = line; + } + + attachInfo.pid = pid; + return attachInfo; + } + + private static void setJarFileToTmp(String Pid) throws IOException { + File nsAgentPath = new File(String.format("/proc/%s/root%s/%s", Pid, NS_TMP_DIR, AGENT_JAR_NAME)); + File hostAgentJarPath = new File(String.format("%s/%s", HOST_JAR_DIR, AGENT_JAR_NAME)); + + Files.copy(hostAgentJarPath.toPath(), nsAgentPath.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + private static Boolean getNsJarPath(ProcessInfo attachInfo) { + if (attachInfo.nspid.equals(attachInfo.pid)) { + return true; + } + return false; + } + + private static void delTmpFile(File file) { + if (file.delete() != true) { + System.out.println("delete file failed.\n"); + } + } + + private static int doAttach(ProcessInfo attachInfo) throws IOException, InterruptedException { + int ret = -1; + List commandList = new ArrayList<>(); + if (attachInfo.pid == null || attachInfo.nspid == null) { + System.out.println("attach failed becase null pid or nspid"); + return -1; + } + String nsJarPath; + if (getNsJarPath(attachInfo)) { + nsJarPath = String.format("/proc/%s/root%s/%s", attachInfo.pid, NS_TMP_DIR, AGENT_JAR_NAME); + } else { + nsJarPath = String.format("%s/%s", NS_TMP_DIR, AGENT_JAR_NAME); + } + + commandList.add("sh"); + commandList.add("-c"); + commandList.add(String.format(ATTACH_CMD, + ATTACH_BIN_PATH, attachInfo.pid, attachInfo.nspid, nsJarPath, attachInfo.pid, attachInfo.nspid)); + + ProcessBuilder pb = new ProcessBuilder(commandList); + Process process = pb.start(); + ret = process.waitFor(); + if (ret != 0) { + System.out.println("Program attach exited with code: " + ret + " command: " + commandList); + } + + return ret; + } + + private static int readMetricFile(ProcessInfo attachInfo) throws IOException { + File jvmMetricFile = new File(String.format("/proc/%s/root/tmp/%s", attachInfo.pid, METRIC_FILE_NAME)); + if (!jvmMetricFile.exists()) { + System.out.printf("Proc[%s] has no jvm metric file in /tmp path.\n", attachInfo.pid); + return 0; + } + + InputStreamReader streamReader = new InputStreamReader(new FileInputStream(jvmMetricFile)); + BufferedReader br = new BufferedReader(streamReader); + String line = null; + while ((line = br.readLine()) != null) { + System.out.println(line); + } + + // del metric file after read + if (jvmMetricFile.delete() != true) { + System.out.println("delete jvm metric file failed.\n"); + return -1; + } + + return 0; + } + + private static void probe() throws IOException, InterruptedException { + List jvmProcList = checkProcessToAttach(); + System.out.println(jvmProcList); + + for (int i = 0; i < jvmProcList.size(); i++) { + ProcessInfo attachInfo = setEffectiveId(jvmProcList.get(i).toString()); + try { + setJarFileToTmp(attachInfo.pid); + } catch (IOException e) { + System.out.println("copy host_jar to ns_tmp failed, err: %s" + e.getMessage()); + continue; + } + doAttach(attachInfo); + readMetricFile(attachInfo); + } + } + + public static void main(String[] args) { + argsParse(args); + while (true) { + try { + probe(); + } catch (IOException e) { + System.out.println(e.getMessage()); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + } + // sleep + try { + TimeUnit.SECONDS.sleep(period); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + } + } + } + +} diff --git a/src/probes/extends/java.probe/jvm.probe/src/Vm.java b/src/probes/extends/java.probe/jvm.probe/src/Vm.java deleted file mode 100644 index 61d111d..0000000 --- a/src/probes/extends/java.probe/jvm.probe/src/Vm.java +++ /dev/null @@ -1,173 +0,0 @@ -import com.sun.tools.attach.*; -import javax.management.MBeanServerConnection; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; -import java.io.IOException; -import java.lang.management.*; -import java.util.List; -import java.util.concurrent.TimeUnit; - - -public class Vm { - public VirtualMachineDescriptor virtualMachineDescriptor; - public MBeanServerConnection mBeanServerConnection; - public JMXConnector connector; - public final String canonicalPath; - public StringBuilder res; - - final String localConnectorAddress = "com.sun.management.jmxremote.localConnectorAddress"; - - public Vm(VirtualMachineDescriptor virtualMachineDescriptor,String canonicalPath) throws AgentLoadException, IOException, AttachNotSupportedException, AgentInitializationException { - this.virtualMachineDescriptor = virtualMachineDescriptor; - this.canonicalPath = canonicalPath; - getTargetVmConnection(virtualMachineDescriptor.id()); - res = new StringBuilder(); - } - - private void getTargetVmConnection(String vmId) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException { - - VirtualMachine virtualMachine = VirtualMachine.attach(vmId); - virtualMachine.loadAgent(canonicalPath); - - String connectorAddress = (String) virtualMachine.getAgentProperties().get(localConnectorAddress); - virtualMachine.detach(); - this.connector = JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress)); - this.mBeanServerConnection = connector.getMBeanServerConnection(); - } - - public void getData() { - try { - getInfo(this.mBeanServerConnection); - } catch (IOException e) { - System.out.println(e.getMessage()); - } finally { - try { - connector.close(); - } catch (IOException e) { - System.out.println(e.getMessage()); - } - } - } - - private void getInfo(MBeanServerConnection connection) throws IOException { - RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy( - connection, ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class); - com.sun.management.OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.newPlatformMXBeanProxy(connection, - ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, com.sun.management.OperatingSystemMXBean.class); - List garbageCollectorMXBeans = ManagementFactory.getPlatformMXBeans( - connection,GarbageCollectorMXBean.class); - ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy( - connection, ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class); - MemoryMXBean memoryMXBean = ManagementFactory.newPlatformMXBeanProxy( - connection, ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class); - List pools = ManagementFactory.getPlatformMXBeans( - connection,BufferPoolMXBean.class); - // tid % mainclass - String jvmPid = virtualMachineDescriptor.id(); - String[] split = virtualMachineDescriptor.displayName().split(" "); - String pkgNameMainClass = split[0]; - res.append(String.format("|jvm|%s|%s|", jvmPid, pkgNameMainClass)); - - // vesion % type - String jvmVersion = runtimeMXBean.getSpecVersion(); - String jvmType = runtimeMXBean.getVmName(); - res.append(String.format("%s|%s|", jvmVersion, jvmType)); - - // gc - double gcDetail = getGarbageCollectionUsage(runtimeMXBean, operatingSystemMXBean, garbageCollectorMXBeans); - long gc_counts = getTotalGarbageCollectionCount(garbageCollectorMXBeans); - long gc_time_ms = getTotalGarbageCollectionTime(garbageCollectorMXBeans); - res.append(String.format("%.2f|%d|%d|", gcDetail, gc_counts, gc_time_ms)); - // process & thread - Long processStartTimeSeconds = runtimeMXBean.getStartTime(); - Long processCpuSecondsTotal = runtimeMXBean.getUptime(); - - int threadsCurrent = threadMXBean.getThreadCount(); - int threadsPeak = threadMXBean.getPeakThreadCount(); - int threadsDeadlocked = 0; - if(threadMXBean.findDeadlockedThreads() != null) { - threadsDeadlocked = threadMXBean.findDeadlockedThreads().length; - } - - res.append(String.format("%d|%d|%d|%d|%d|", processStartTimeSeconds, processCpuSecondsTotal, threadsCurrent, threadsPeak, threadsDeadlocked)); - - // heap - double heap_occupied = 100.0 * Double.valueOf(memoryMXBean.getHeapMemoryUsage().getCommitted()) / memoryMXBean.getHeapMemoryUsage().getMax(); - long heap_used = memoryMXBean.getHeapMemoryUsage().getUsed(); - res.append(String.format("%d|%.2f|", heap_used, heap_occupied)); - - // noheap - long noheap_used = memoryMXBean.getNonHeapMemoryUsage().getUsed(); - double noheap_occupied = memoryMXBean.getNonHeapMemoryUsage().getMax(); - if(noheap_occupied > 0) { - noheap_occupied = 100.0 * Double.valueOf(memoryMXBean.getNonHeapMemoryUsage().getCommitted()) / noheap_occupied; - res.append(String.format("%d|%.2f|", noheap_used, noheap_occupied)); - } - else { - res.append(String.format("%d|%.0f|", noheap_used,noheap_occupied)); - } - // bufferpool - long bf_capacity = getTotalBufferPoolsCapacity(pools); - long bf_used = getTotalBufferPoolsUsed(pools); - res.append(String.format("%d|%d|\n", bf_capacity, bf_used)); - System.out.printf(res.toString()); - } - - private long getTotalGarbageCollectionCount(List garbageCollectorMXBeans) { - long gc_count=0; - for(GarbageCollectorMXBean gc :garbageCollectorMXBeans) { - gc_count +=gc.getCollectionCount(); - } - return gc_count; - } - - private long getTotalGarbageCollectionTime(List gcmList) { - long total_ms=0; - for(GarbageCollectorMXBean gc :gcmList) { - total_ms += gc.getCollectionTime(); - } - return total_ms; - } - - private double getGarbageCollectionUsage(RuntimeMXBean runtime, com.sun.management.OperatingSystemMXBean os, List gcmList) { - // the uptime of the Java virtual machine - long prevUpTime = runtime.getUptime(); - // the approximate accumulated collection elapsed time - long prevProcessGcTime = getTotalGarbageCollectionTime(gcmList); - // the number of processors available to the Java virtual machine - int processorCount = os.getAvailableProcessors(); - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - System.out.println(e.getMessage()); - System.exit(1); - } - - long upTime = runtime.getUptime(); - long processGcTime = getTotalGarbageCollectionTime(gcmList); - long upTimeDiff = upTime - prevUpTime; - long processGcTimeDiff = processGcTime - prevProcessGcTime; - - double gcDetail = (processGcTimeDiff * 100.0 / processorCount / upTimeDiff); - return gcDetail; - } - - private long getTotalBufferPoolsCapacity(List bufferpools) { - long total_bytes = 0; - for (BufferPoolMXBean bpool : bufferpools) { - total_bytes += bpool.getTotalCapacity(); - } - return total_bytes; - } - - private long getTotalBufferPoolsUsed(List bufferpools) { - long total_bytes = 0; - for (BufferPoolMXBean bpool : bufferpools) { - total_bytes += bpool.getMemoryUsed(); - } - return total_bytes; - } - -} - diff --git a/src/probes/extends/java.probe/jvm.probe/src/agent/JvmProbeAgent.java b/src/probes/extends/java.probe/jvm.probe/src/agent/JvmProbeAgent.java new file mode 100644 index 0000000..488c73c --- /dev/null +++ b/src/probes/extends/java.probe/jvm.probe/src/agent/JvmProbeAgent.java @@ -0,0 +1,188 @@ +package agent; + +import java.util.List; +import java.io.IOException; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.lang.management.ManagementFactory; +import com.sun.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.BufferPoolMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + + +public class JvmProbeAgent { + + private static final int MSEC_PER_SEC = 1000; + private static final int NSEC_PER_SEC = 1000000; + private static final String METRIC_FILE_NAME = "jvm-metrics.txt"; + private static String pid; + private static String nspid; + + private static String getMetricFilePath() { + String filePath = String.format("/proc/%s/root/tmp/%s", nspid, METRIC_FILE_NAME); + return filePath; + } + + private static void writeMetricRecords(String record) { + try { + File file = new File(getMetricFilePath()); + if (!file.exists()) { + file.createNewFile(); + } + FileWriter fw = new FileWriter(file.getAbsoluteFile(), true); + BufferedWriter bw = new BufferedWriter(fw); + bw.write(record); + bw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void getJmxInfo() throws IOException { + RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); + OperatingSystemMXBean osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + ClassLoadingMXBean clBean = ManagementFactory.getClassLoadingMXBean(); + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); + MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); + List memPoolBeans = ManagementFactory.getMemoryPoolMXBeans(); + List bufPoolBeans = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); + List garbageCollectors = ManagementFactory.getGarbageCollectorMXBeans(); + + infoCollector(runtimeBean); + processCollector(runtimeBean, osBean); + classCollector(clBean); + threadCollector(threadBean); + memoryAreaCollector(heapUsage, "heap"); + memoryAreaCollector(nonHeapUsage, "nonheap"); + memoryPoolCollector(memPoolBeans); + bufferPoolCollector(bufPoolBeans); + gcCollector(garbageCollectors); + } + + private static void infoCollector(RuntimeMXBean runtimeBean) { + String jvmName = runtimeBean.getVmName(); + String jvmVersion = runtimeBean.getVmVersion(); // 原始的为getSpecVersion(); + String jvmVender = runtimeBean.getVmVendor(); + + writeMetricRecords(String.format("|jvm_info|%s|%s|%s|%s|%d|\n", pid, jvmName, jvmVender, jvmVersion, 1)); + } + + private static void processCollector(RuntimeMXBean runtimeBean, OperatingSystemMXBean osBean) { + long processStartTime = runtimeBean.getStartTime(); // ms + try { + Long processCpuTime = callLongGetter(osBean.getClass().getMethod("getProcessCpuTime"), osBean); // ns + writeMetricRecords(String.format("|jvm_process|%s|%d|%d|\n", pid, processStartTime, processCpuTime)); + } catch (Exception e) { + //System.out.println("error"); + } + } + + private static Long callLongGetter(Method method, Object obj) throws InvocationTargetException { + try { + return (Long) method.invoke(obj); + } catch (IllegalAccessException e) { + // Expected, the declaring class or interface might not be public. + } + + for (Class clazz : method.getDeclaringClass().getInterfaces()) { + try { + Method interfaceMethod = clazz.getMethod(method.getName(), method.getParameterTypes()); + Long result = callLongGetter(interfaceMethod, obj); + if (result != null) { + return result; + } + } catch (NoSuchMethodException e) { + // Expected, class might implement multiple, unrelated interfaces. + } + } + return null; + } + + // thread + private static void threadCollector(ThreadMXBean threadBean) { + int currentThreadCnt = threadBean.getThreadCount(); + int daemonThreadCnt = threadBean.getDaemonThreadCount(); + int peakThreadCnt = threadBean.getPeakThreadCount(); + long startThreadCnt = threadBean.getTotalStartedThreadCount(); + long cycleThreadDeadlocked = 0; + long[] deadlocks = threadBean.findDeadlockedThreads(); + if (deadlocks != null && deadlocks.length > 0) { + cycleThreadDeadlocked = deadlocks.length; + } + writeMetricRecords(String.format("|jvm_thread|%s|%d|%d|%d|%d|%d|\n", + pid, currentThreadCnt, daemonThreadCnt, peakThreadCnt, startThreadCnt, cycleThreadDeadlocked)); + } + + private static void classCollector(ClassLoadingMXBean clBean) { + int currentClassCnt = clBean.getLoadedClassCount(); + long totalClassCnt = clBean.getTotalLoadedClassCount(); + writeMetricRecords(String.format("|jvm_class|%s|%d|%d|\n", pid, currentClassCnt, totalClassCnt)); + } + + // memory + private static void memoryAreaCollector(MemoryUsage memUsage, String area) { + long memUsed = memUsage.getUsed(); + long memCommitted = memUsage.getCommitted(); + long memMax = memUsage.getMax(); + long memInit = memUsage.getInit(); + writeMetricRecords(String.format("|jvm_mem|%s|%s|%d|%d|%d|%d|\n", pid, area, memUsed, memCommitted, memMax, memInit)); + } + + // memory_pool + private static void memoryPoolCollector(List poolBeans) { + for (final MemoryPoolMXBean pool : poolBeans) { + MemoryUsage poolUsage = pool.getUsage(); + if (poolUsage != null) { + writeMetricRecords(String.format("|jvm_mem_pool|%s|%s|%d|%d|%d|", + pid, pool.getName(), poolUsage.getUsed(), poolUsage.getCommitted(), poolUsage.getMax())); + } + MemoryUsage colPoolUsage = pool.getCollectionUsage(); + if (colPoolUsage != null) { + writeMetricRecords(String.format("%d|%d|%d|", + colPoolUsage.getUsed(), colPoolUsage.getCommitted(), colPoolUsage.getMax())); + } else { + writeMetricRecords(String.format("-1|-1|-1|")); + } + writeMetricRecords(String.format("\n")); + } + } + + // buffer_pool + private static void bufferPoolCollector(List bufferPools) { + for (BufferPoolMXBean pool : bufferPools) { + writeMetricRecords(String.format("|jvm_buf_pool|%s|%s|%d|%d|%d|\n", + pid, pool.getName(), pool.getMemoryUsed(), pool.getCount(), pool.getTotalCapacity())); + } + } + + // gc + private static void gcCollector(List garbageCollectors) { + for (GarbageCollectorMXBean gc : garbageCollectors) { + writeMetricRecords(String.format("|jvm_gc|%s|%s|%d|%d|\n", + pid, gc.getName(), gc.getCollectionCount(), gc.getCollectionTime())); // ms + } + } + + + public static void agentmain(String agentArgs) { + String[] argStr = agentArgs.split("[,]"); + pid = argStr[0]; + nspid = argStr[1]; + try { + getJmxInfo(); + } catch (IOException e) { + System.out.println(e.getMessage()); + } + } +} diff --git a/src/probes/extends/java.probe/jvm.probe/src/agent/config/META-INF/MANIFEST.MF b/src/probes/extends/java.probe/jvm.probe/src/agent/config/META-INF/MANIFEST.MF new file mode 100644 index 0000000..2d6f14f --- /dev/null +++ b/src/probes/extends/java.probe/jvm.probe/src/agent/config/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Created-By: 1.8.0_272 (Oracle Corporation) +Agent-Class: agent.JvmProbeAgent +Can-Retransform-Classes: true +Can-Redefine-Classes: true + -- 2.33.0