diff --git a/femas-adaptor/femas-adaptor-opensource-admin/src/main/java/com/tencent/tsf/femas/adaptor/paas/governance/lane/FemasLaneFilter.java b/femas-adaptor/femas-adaptor-opensource-admin/src/main/java/com/tencent/tsf/femas/adaptor/paas/governance/lane/FemasLaneFilter.java index 7cdff12e..af5a4ede 100644 --- a/femas-adaptor/femas-adaptor-opensource-admin/src/main/java/com/tencent/tsf/femas/adaptor/paas/governance/lane/FemasLaneFilter.java +++ b/femas-adaptor/femas-adaptor-opensource-admin/src/main/java/com/tencent/tsf/femas/adaptor/paas/governance/lane/FemasLaneFilter.java @@ -107,17 +107,18 @@ private static List chooseColorfulInstances(List chooseColorlessInstances(Service service, List serviceInstances) { List instances = new ArrayList<>(); String namespaceId = service.getNamespace(); - /** * 该命名空间下没有配置泳道信息 */ @@ -125,7 +126,6 @@ private static List chooseColorlessInstances(Service service, .get(namespaceId).isEmpty()) { return serviceInstances; } - Set colorInstances = new HashSet<>(); for (ServiceInstance instance : serviceInstances) { String groupId = instance.getMetadata(FemasConstant.FEMAS_META_APPLICATION_VERSION_KEY); @@ -138,16 +138,13 @@ private static List chooseColorlessInstances(Service service, instances.add(instance); } - if (!CollectionUtil.isEmpty(colorInstances)) { LOGGER.debug("[FEMAS LANE] Choose Colorless instances. lane take effect, filter color instance list = {}", colorInstances); } - if (CollectionUtil.isEmpty(instances)) { return serviceInstances; } - return instances; } @@ -264,7 +261,6 @@ private static synchronized void resortLaneRule() { public static synchronized void addLaneRule(LaneRule laneRule) { ALL_LANE_RULES.add(laneRule); - refreshEffectiveLaneRule(); } @@ -272,7 +268,6 @@ public static synchronized void removeLaneRule(LaneRule laneRule) { ALL_LANE_RULES.remove(laneRule); EFFECTIVE_LANE_RULES_SET.remove(laneRule); EFFECTIVE_LANE_RULES.remove(laneRule); - LOGGER.info("EFFECTIVE LANE Rule changed. EFFECTIVE_LANE_RULES : " + EFFECTIVE_LANE_RULES); } @@ -376,6 +371,9 @@ private String getLaneIdByPercentage(Map laneMap) { return weightMap.get(tailMap.firstKey()); } + + + @Override public String getName() { return "femasLane"; diff --git a/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/KubernetesFabricRegistryOpenApi.java b/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/KubernetesFabricRegistryOpenApi.java index add748e7..c5b7bf6c 100644 --- a/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/KubernetesFabricRegistryOpenApi.java +++ b/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/KubernetesFabricRegistryOpenApi.java @@ -402,4 +402,4 @@ private int findEndpointPort(EndpointSubset s, String serviceId, String primaryP } } -} +} \ No newline at end of file diff --git a/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/OpenApiFactory.java b/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/OpenApiFactory.java index b2eaf9f5..7646e21e 100644 --- a/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/OpenApiFactory.java +++ b/femas-admin/src/main/java/com/tencent/tsf/femas/service/registry/OpenApiFactory.java @@ -32,23 +32,22 @@ public class OpenApiFactory { private final NacosRegistryOpenApi nacosRegistryOpenApi; private final EurekaRegistryOpenApi eurekaRegistryOpenApi; private final ConsulRegistryOpenApi consulRegistryOpenApi; - private final KubernetesFabricRegistryOpenApi k8sRegistryOpenApi; + // private final KubernetesFabricRegistryOpenApi k8sRegistryOpenApi; private final PolarisRegistryOpenApi polarisRegistryOpenApi; private final Map registryOpenApiInterfaceMapCache = new ConcurrentHashMap<>(); - public OpenApiFactory(NacosRegistryOpenApi nacosRegistryOpenApi, EurekaRegistryOpenApi eurekaRegistryOpenApi, ConsulRegistryOpenApi consulRegistryOpenApi, KubernetesFabricRegistryOpenApi k8sRegistryOpenApi, PolarisRegistryOpenApi polarisRegistryOpenApi) { + public OpenApiFactory(NacosRegistryOpenApi nacosRegistryOpenApi, EurekaRegistryOpenApi eurekaRegistryOpenApi, ConsulRegistryOpenApi consulRegistryOpenApi, PolarisRegistryOpenApi polarisRegistryOpenApi) { this.nacosRegistryOpenApi = nacosRegistryOpenApi; this.eurekaRegistryOpenApi = eurekaRegistryOpenApi; this.consulRegistryOpenApi = consulRegistryOpenApi; - this.k8sRegistryOpenApi = k8sRegistryOpenApi; +// this.k8sRegistryOpenApi = k8sRegistryOpenApi; this.polarisRegistryOpenApi = polarisRegistryOpenApi; registryOpenApiInterfaceMapCache.put(RegistryEnum.CONSUL.getAlias(), consulRegistryOpenApi); registryOpenApiInterfaceMapCache.put(RegistryEnum.NACOS.getAlias(), nacosRegistryOpenApi); registryOpenApiInterfaceMapCache.put(RegistryEnum.EUREKA.getAlias(), eurekaRegistryOpenApi); - registryOpenApiInterfaceMapCache.put(RegistryEnum.KUBERNETES.getAlias(), k8sRegistryOpenApi); +// registryOpenApiInterfaceMapCache.put(RegistryEnum.KUBERNETES.getAlias(), k8sRegistryOpenApi); registryOpenApiInterfaceMapCache.put(RegistryEnum.POLARIS.getAlias(), polarisRegistryOpenApi); - } public RegistryOpenApiInterface select(String type) { diff --git a/femas-agent/femas-agent-core/pom.xml b/femas-agent/femas-agent-core/pom.xml index ccff83a2..29efe359 100644 --- a/femas-agent/femas-agent-core/pom.xml +++ b/femas-agent/femas-agent-core/pom.xml @@ -40,6 +40,18 @@ femas-agent-tools + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + compile + + org.assertj assertj-core diff --git a/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/classloader/JvmMonitorClassloader.java b/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/classloader/JvmMonitorClassloader.java new file mode 100644 index 00000000..c6ff5d72 --- /dev/null +++ b/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/classloader/JvmMonitorClassloader.java @@ -0,0 +1,71 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.tsf.femas.agent.classloader; + + +import com.tencent.tsf.femas.agent.tools.Logger; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; + +public class JvmMonitorClassloader extends URLClassLoader { + private static final Logger LOGGER = Logger.getLogger(JvmMonitorClassloader.class); + // only enable at develop phase. + private static final Boolean DUMP_LOADED_CLASSES = false; + + public JvmMonitorClassloader(URL[] urls) { + super(urls); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + byte[] b = loadClassData(name); + return defineClass(name, b, 0, b.length); + } + + private byte[] loadClassData(String className) { + if (DUMP_LOADED_CLASSES) { + LOGGER.debug("load class: " + className.replace(".", "/") + ".class"); + } + // read class + InputStream is = getClass().getClassLoader().getResourceAsStream( + className.replace(".", "/") + ".class"); + ByteArrayOutputStream byteSt = new ByteArrayOutputStream(); + // write into byte + int len = 0; + try { + while ((len = is.read()) != -1) { + byteSt.write(len); + } + } catch (IOException e) { + e.printStackTrace(); + } + // convert into byte array + return byteSt.toByteArray(); + } +} diff --git a/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/server/MonitorServerBoot.java b/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/server/MonitorServerBoot.java new file mode 100644 index 00000000..6de097cf --- /dev/null +++ b/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/server/MonitorServerBoot.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ +package com.tencent.tsf.femas.agent.server; + +import com.tencent.tsf.femas.agent.classloader.AgentClassLoader; +import com.tencent.tsf.femas.agent.classloader.AgentPackagePathScanner; +import com.tencent.tsf.femas.agent.classloader.JvmMonitorClassloader; +import com.tencent.tsf.femas.agent.tools.AgentLogger; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.File; +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.List; + +/** + * @Author leoziltong@tencent.com + * @Date: 2022/9/22 16:48 + */ +public class MonitorServerBoot implements ServerBoot { + + private static final AgentLogger LOGGER = AgentLogger.getLogger(MonitorServerBoot.class); + + private static final String QOCO_CLASS_NAME = "com.tencent.femas.jvm.monitor.jvmmonitoragent.JvmMonitorAgentEntrance"; + private static final String QOCO_MAIN_METHOD = "main"; + private static final String QOCO_PROFILERAGENTMAIN_METHOD = "profilerAgentMain"; + // must be same as DEFAULT_PORT in JvmMonitorAgentEntrance, but we dont want to load it. + private static final String DEFAULT_PORT = "11099"; + // must be same as JDK_CTX in JvmMonitorAgentEntrance + public static final String JDK_CTX = "/jvm"; + private static boolean alreadyLoaded = false; + private static Instrumentation instrumentation; + private static Class qClass = null; + private static JvmMonitorClassloader monitorClassLoader = null; + private static final String PARENT_DIR_JTS = "jts"; + + @Override + public void init(Object... context) { + for (Object var : context) { + if (var instanceof Instrumentation) { + instrumentation = (Instrumentation) var; + } + } + try { + String classPath = System.getProperty("java.class.path"); + String[] cpArr = classPath.split(":"); + int len = cpArr.length; + URL[] urls = new URL[len]; + for (int i = 0; i < len; i++) { + urls[i] = Paths.get(cpArr[i]).toUri().toURL(); + } + monitorClassLoader = new JvmMonitorClassloader(urls); + qClass = monitorClassLoader.loadClass(QOCO_CLASS_NAME); + } catch (ClassNotFoundException e) { + LOGGER.error("MonitorServerBoot init failed", e); + } catch (MalformedURLException e) { + LOGGER.error("Fail create URL for method " + QOCO_MAIN_METHOD); + } + } + + private static String extractPortNum(String options) { + if (options == null || options.length() == 0) { + return DEFAULT_PORT; + } + String[] opts = options.split(";"); + + for (String kvPair : opts) { + // TODO. warning if fillOption returns false? + if (kvPair.startsWith("portNum=")) { + String[] kv = kvPair.split("="); + if (kv.length != 2) { + return DEFAULT_PORT; + } else { + return kv[1]; + } + } + } + return DEFAULT_PORT; + } + + @Override + public void startup(String options) { + if (alreadyLoaded != true) { + alreadyLoaded = true; + } else { + return; + } + String version = MonitorServerBoot.class.getPackage().getImplementationVersion(); + LOGGER.info("Agent VERSION: " + version); + String portNum = extractPortNum(options); + // For compatibility. + if (maybeInUse(portNum)) { + LOGGER.warn("JvmMonitor port (" + portNum + ") already in use, caused by duplication of JVM monitor agent"); + return; + } + try { + Method mainMethod = qClass.getMethod(QOCO_MAIN_METHOD, String.class, Instrumentation.class); + mainMethod.invoke(null, options, instrumentation); + } catch (NoSuchMethodException e) { + LOGGER.error("Fail find method " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } catch (IllegalAccessException e) { + LOGGER.error("Fail invoke method " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } catch (InvocationTargetException e) { + LOGGER.error("Fail invethod " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } + } + + private static boolean maybeInUse(String portNum) { + // Test whether TencentCloudJvmMonitor exist more than once. + // if yes, testing port... + if (dupInArguments("TencentCloudJvmMonitor")) { + LOGGER.warn("multiple TecnentCloudJvmMonitor agent found, processing... it may take several seconds"); + // Test whether port is in use. + if (portInUse(portNum)) { + LOGGER.warn("jvm monitor port (" + portNum + ") already in Use, duplicated JvmMonitor agent?"); + return true; + } + } + return false; + } + + private static boolean dupInArguments(String agentName) { + RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); + List args = bean.getInputArguments(); + Iterator iter = args.iterator(); + boolean dup = false; + boolean exist = false; + while (iter.hasNext()) { + String arg = (String) iter.next(); + LOGGER.info("Argumnets: " + arg); + if (arg.contains(agentName)) { + if (exist) { + LOGGER.warn("Found dupilicated JvmMonitor agents"); + dup = true; + } else { + exist = true; + } + } + } + return dup; + } + + private static boolean portInUse(String port) { + //testing by send command to self + String url = "http://localhost:" + port + JDK_CTX; + String cmdString = "{\"taskId\":\"Qoco_getPid\",\"type\":\"getpid\",\"action\":\"\",\"metaInfo\":\"\"}"; + CloseableHttpClient client = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + try { + StringEntity entity = new StringEntity(cmdString); + httpPost.setEntity(entity); + CloseableHttpResponse response = client.execute(httpPost); + if (response.getStatusLine().getStatusCode() == 200) { + return true; + } else { + LOGGER.info("test port response no-OK code: " + response.getStatusLine().getStatusCode()); + return false; + } + } catch (Exception e) { + LOGGER.info(url + " port is not used: exception " + e); + return false; + } finally { + try { + client.close(); + } catch (IOException e) { + LOGGER.warn("http client for port testing can not be closed safely"); + } + } + } + + @Override + public void shutdown() { + + } +} diff --git a/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/server/ServerBoot.java b/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/server/ServerBoot.java new file mode 100644 index 00000000..2f46de93 --- /dev/null +++ b/femas-agent/femas-agent-core/src/main/java/com/tencent/tsf/femas/agent/server/ServerBoot.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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. + */ +package com.tencent.tsf.femas.agent.server; + +/** + * @Author leoziltong@tencent.com + * @Date: 2022/9/22 16:56 + */ +public interface ServerBoot { + + void init(Object... vars); + + void startup(String options); + + void shutdown(); +} diff --git a/femas-agent/femas-agent-example/femas-agent-example-springcloud-consumer/pom.xml b/femas-agent/femas-agent-example/femas-agent-example-springcloud-consumer/pom.xml index 15d81eee..281d4656 100644 --- a/femas-agent/femas-agent-example/femas-agent-example-springcloud-consumer/pom.xml +++ b/femas-agent/femas-agent-example/femas-agent-example-springcloud-consumer/pom.xml @@ -87,6 +87,12 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 2.1.4.RELEASE + + + httpclient + org.apache.httpcomponents + + com.alibaba.cloud diff --git a/femas-agent/femas-agent-jts/LICENSE b/femas-agent/femas-agent-jts/LICENSE new file mode 100644 index 00000000..3f8fa0f6 --- /dev/null +++ b/femas-agent/femas-agent-jts/LICENSE @@ -0,0 +1,20 @@ +Copyright 2010-2021 the original author or authors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/femas-agent/femas-agent-jts/README.txt b/femas-agent/femas-agent-jts/README.txt new file mode 100644 index 00000000..68f1fca6 --- /dev/null +++ b/femas-agent/femas-agent-jts/README.txt @@ -0,0 +1,12 @@ +Build Step: +1. add two local dependencies + +cd ${project.basedir}/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT; +mvn install:install-file -Dfile=jmxtrans-agent-1.2.10-SNAPSHOT.jar -DgroupId=org.jmxtrans.agent -DartifactId=jmxtrans-agent -Dversion=1.2.10-SNAPSHOT -Dpackaging=jar + + +cd ${project.basedir}/lib/one/profiler/async-profiler/0.1; +mvn install:install-file -Dfile=async-profiler-0.1.jar -DgroupId=one.profiler -DartifactId=async-profiler -Dversion=0.1 -Dpackaging=jar + +Then +mvn clean package diff --git a/femas-agent/femas-agent-jts/lib/garbagecat_lib/garbagecat.jar b/femas-agent/femas-agent-jts/lib/garbagecat_lib/garbagecat.jar new file mode 100644 index 00000000..2b266fd6 Binary files /dev/null and b/femas-agent/femas-agent-jts/lib/garbagecat_lib/garbagecat.jar differ diff --git a/femas-agent/femas-agent-jts/lib/native_libs/linux_aarch64/libasyncProfiler.so b/femas-agent/femas-agent-jts/lib/native_libs/linux_aarch64/libasyncProfiler.so new file mode 100755 index 00000000..7da21d02 Binary files /dev/null and b/femas-agent/femas-agent-jts/lib/native_libs/linux_aarch64/libasyncProfiler.so differ diff --git a/femas-agent/femas-agent-jts/lib/native_libs/linux_x86_64/libasyncProfiler.so b/femas-agent/femas-agent-jts/lib/native_libs/linux_x86_64/libasyncProfiler.so new file mode 100755 index 00000000..11453cce Binary files /dev/null and b/femas-agent/femas-agent-jts/lib/native_libs/linux_x86_64/libasyncProfiler.so differ diff --git a/femas-agent/femas-agent-jts/lib/native_libs/macos_x86_64/libasyncProfiler.so b/femas-agent/femas-agent-jts/lib/native_libs/macos_x86_64/libasyncProfiler.so new file mode 100755 index 00000000..5fa31818 Binary files /dev/null and b/femas-agent/femas-agent-jts/lib/native_libs/macos_x86_64/libasyncProfiler.so differ diff --git a/femas-agent/femas-agent-jts/lib/one/profiler/async-profiler/0.1/async-profiler-0.1.jar b/femas-agent/femas-agent-jts/lib/one/profiler/async-profiler/0.1/async-profiler-0.1.jar new file mode 100644 index 00000000..c9478cfc Binary files /dev/null and b/femas-agent/femas-agent-jts/lib/one/profiler/async-profiler/0.1/async-profiler-0.1.jar differ diff --git a/femas-agent/femas-agent-jts/lib/one/profiler/async-profiler/0.1/async-profiler-0.1.pom b/femas-agent/femas-agent-jts/lib/one/profiler/async-profiler/0.1/async-profiler-0.1.pom new file mode 100644 index 00000000..3a236f60 --- /dev/null +++ b/femas-agent/femas-agent-jts/lib/one/profiler/async-profiler/0.1/async-profiler-0.1.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + one.profiler + async-profiler + 0.1 + POM was created locally + diff --git a/femas-agent/femas-agent-jts/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT/jmxtrans-agent-1.2.10-SNAPSHOT.jar b/femas-agent/femas-agent-jts/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT/jmxtrans-agent-1.2.10-SNAPSHOT.jar new file mode 100644 index 00000000..0e599713 Binary files /dev/null and b/femas-agent/femas-agent-jts/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT/jmxtrans-agent-1.2.10-SNAPSHOT.jar differ diff --git a/femas-agent/femas-agent-jts/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT/jmxtrans-agent-1.2.10-SNAPSHOT.pom b/femas-agent/femas-agent-jts/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT/jmxtrans-agent-1.2.10-SNAPSHOT.pom new file mode 100644 index 00000000..481d2034 --- /dev/null +++ b/femas-agent/femas-agent-jts/lib/org/jmxtrans/agent/jmxtrans-agent/1.2.10-SNAPSHOT/jmxtrans-agent-1.2.10-SNAPSHOT.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + org.jmxtrans.agent + jmxtrans-agent + 1.2.10-SNAPSHOT + POM was created locally + diff --git a/femas-agent/femas-agent-jts/pom.xml b/femas-agent/femas-agent-jts/pom.xml new file mode 100644 index 00000000..b600088d --- /dev/null +++ b/femas-agent/femas-agent-jts/pom.xml @@ -0,0 +1,259 @@ + + + 4.0.0 + + + femas-agent + com.tencent.tsf + ${revision} + ../pom.xml + + femas-agent-jts + jar + femas-agent-jts + + com.tencent.femas.tencentcloudjvmmonitor.dependencies + UTF-8 + com.fasterxml.jackson + ${shade.package}.${shade.jackson.source} + org.apache.http + ${shade.package}.${shade.org.apache.http.source} + org.apache.commons + ${shade.package}.${shade.org.apache.commons.source} + + one.profiler + ${shade.package}.${shade.one.profiler.source} + org.jmxtrans.agent + ${shade.package}.${shade.org.jmxtrans.agent.source} + + ${basedir} + femas-agent-jts + + + + + femas-local-repo + femas local repo + file://${project.basedir}/lib + + + + + ${project.artifactId}-${project.version}-${maven.build.timestamp} + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + ${project.basedir}/lib + + ${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar${path.separator}${java.home}/../lib/tools.jar + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + true + false + lib/ + true + true + + + + com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent.TencentJvmMonitorAgent + + + com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent.TencentJvmMonitorAgent + + true + true + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + false + + + + ${shade.jackson.source} + ${shade.jackson.target} + + + ${shade.org.apache.http.source} + ${shade.org.apache.http.target} + + + ${shade.org.apache.commons.source} + ${shade.org.apache.commons.target} + + + ${shade.one.profiler.source} + ${shade.one.profiler.target} + + + ${shade.org.jmxtrans.agent.source} + ${shade.org.jmxtrans.agent.target} + + + + + + + + + + com.mycila.maven-license-plugin + maven-license-plugin + 1.10.b1 + +
${license.dir}/LICENSE
+ + + ${project.name} + + ${project.organization.name} + ${project.inceptionYear} + + + src/main/java/** + src/test/java/** + +
+ + + + format + + process-sources + + + + + com.mycila + licenses + 1 + + +
+ + org.apache.maven.plugins + maven-antrun-plugin + + + package + + run + + + + + + overwrite="true"/> + + + + + +
+ + + false + ${project.basedir}/lib/ + + garbagecat_lib/garbagecat.jar + native_libs/linux_x86_64/libasyncProfiler.so + native_libs/macos_x86_64/libasyncProfiler.so + native_libs/linux_aarch64/libasyncProfiler.so + + + + src/main/resources + + +
+ + + + org.jmxtrans.agent + jmxtrans-agent + 1.2.10-SNAPSHOT + + + + one.profiler + async-profiler + 0.1 + + + + commons-validator + commons-validator + 1.6 + provided + + + + com.fasterxml.jackson.core + jackson-databind + 2.13.2.2 + + + com.fasterxml.jackson.core + jackson-core + 2.13.2 + + + com.fasterxml.jackson.core + jackson-annotations + 2.13.2 + + + + + org.jetbrains + annotations-java5 + 18.0.0 + provided + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + + commons-codec + commons-codec + 1.15 + + + org.ow2.asm + asm + 7.3.1 + + + +
diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/DeadLockInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/DeadLockInfo.java new file mode 100644 index 00000000..56f2d65f --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/DeadLockInfo.java @@ -0,0 +1,45 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +class DeadLockInfo { + String lockInfos; + int threadCount; + + public String getLockInfos() { + return lockInfos; + } + + public void setLockInfos(String lockInfos) { + this.lockInfos = lockInfos; + } + + public int getThreadCount() { + return threadCount; + } + + public void setThreadCount(int threadCount) { + this.threadCount = threadCount; + } +} \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/FlameGraphArguments.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/FlameGraphArguments.java new file mode 100644 index 00000000..d1a6f6a1 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/FlameGraphArguments.java @@ -0,0 +1,81 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class FlameGraphArguments { + int duration; + String filename; + String event; + String dataSizeLimit; + String jsonSupport; + boolean compression; + + public boolean isCompression() { + return compression; + } + + public void setCompression(boolean compression) { + this.compression = compression; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getEvent() { + return event; + } + + public void setEvent(String event) { + this.event = event; + } + + public String getDataSizeLimit() { + return dataSizeLimit; + } + + public void setDataSizeLimit(String limit) { + this.dataSizeLimit = limit; + } + + public String getJsonSupport() { + return jsonSupport; + } + + public void setJsonSupport(String jsonSupport) { + this.jsonSupport = jsonSupport; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/FlameGraphQueryMetaInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/FlameGraphQueryMetaInfo.java new file mode 100644 index 00000000..bcb2a1af --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/FlameGraphQueryMetaInfo.java @@ -0,0 +1,32 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class FlameGraphQueryMetaInfo { + public String getRequireData() { + return requireData; + } + + String requireData; +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/GCLogAnalyzerArguments.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/GCLogAnalyzerArguments.java new file mode 100644 index 00000000..7d909c3d --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/GCLogAnalyzerArguments.java @@ -0,0 +1,32 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class GCLogAnalyzerArguments { + String filename; + + public String getFilename() { + return filename; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HeapDumper.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HeapDumper.java new file mode 100644 index 00000000..f8f6fb3c --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HeapDumper.java @@ -0,0 +1,75 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.sun.management.HotSpotDiagnosticMXBean; + +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; + +public class HeapDumper { + private static final String HOTSPOT_BEAN_NAME = + "com.sun.management:type=HotSpotDiagnostic"; + + private static volatile HotSpotDiagnosticMXBean hotspotMBean; + + /* + * Call this method to dump heap into a file + * @param fileName name of the heapdump file + * @param live flag that tells whether to dump only the live objects + */ + static void dumpHeap(String fileName, boolean live) { + initHotspotMBean(); + try { + hotspotMBean.dumpHeap(fileName, live); + } catch (RuntimeException re) { + throw re; + } catch (Exception exp) { + throw new RuntimeException(exp); + } + } + + private static void initHotspotMBean() { + if (hotspotMBean == null) { + synchronized (HeapDumper.class) { + if (hotspotMBean == null) { + hotspotMBean = getHotSpotMBean(); + } + } + } + } + + private static HotSpotDiagnosticMXBean getHotSpotMBean() { + try { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + HotSpotDiagnosticMXBean bean = + ManagementFactory.newPlatformMXBeanProxy(server, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); + return bean; + } catch (RuntimeException re) { + throw re; + } catch (Exception exp) { + throw new RuntimeException(exp); + } + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HeapDumperArguments.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HeapDumperArguments.java new file mode 100644 index 00000000..f747199b --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HeapDumperArguments.java @@ -0,0 +1,36 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class HeapDumperArguments { + String filename; + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } +} \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotCommandProcessor.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotCommandProcessor.java new file mode 100644 index 00000000..fc50ff2e --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotCommandProcessor.java @@ -0,0 +1,1595 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sun.management.ThreadMXBean; +import com.tencent.femas.tencentcloudjvmmonitor.utils.*; +import one.profiler.AsyncProfiler; +import org.apache.commons.codec.binary.Base64; + +import javax.management.MBeanServer; +import java.io.*; +import java.lang.instrument.Instrumentation; +import java.lang.management.*; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.zip.GZIPOutputStream; + + +public class HotspotCommandProcessor { + private static final Logger LOGGER = Logger.getLogger(HotspotCommandProcessor.class); + private static final String INDENT = " "; + private static final int MAX_FRAMES = 8; + private static final long KB = 1024; + private static final long MB = 1024 * KB; + + + private static ThreadMXBean threadBean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); + private static RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); + private static ConcurrentHashMap taskMap = new ConcurrentHashMap(); + private static MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + + private static TaskStatus JFG_STATUS = TaskStatus.AVAILABLE; + private static final Object JFG_MONITOR = new Object(); + private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + private static String JFG_DATA_PATH = "JavaFlameGraph.data"; + private static String HEAPDUMP_DEFAULT_FILE_NAME = "HeapDump"; + private static String JMAPTMP_PATH = "jmap.txt"; + private static String VMFLAGS_PATH = "vmflag.txt"; + private static String GCLOGREPORT_PATH = "report.txt"; + private static int JHAT_SERVER_PORT = 7000; + public static String METHOD_TRACE_DUMP_FILE = "method_trace_dump.bin"; + private static File methodProfileFile; + + private static final int JFG_EXEC_INTERVAL = 5 * 1000; // seconds between two collections for result handling + private static long JFG_LAST_EXEC_TIME = 0; + private static final long JFG_DATASIZE_LIMIT = 2 * MB; + private static final long MININAL_JFG_DATASIZE_LIMIT = 1 * KB; // 1k is too small + private static String pid = null; + private static int defaultTopThreads = 5; + private static final int TOP_INTERVAL = 500; // ms between two collection to calculate jtop info + private static final int THREAD_UTIL_INTERVAL_NS = 50000;//100000; // 1ms + + private static final String EVENT_CPU = "cpu"; + private static final String EVENT_ALLOC = "alloc"; + private static final String EVENT_LOCK = "lock"; + private static final String EVENT_WALL = "wall"; + private static final String EVENT_ITIMER = "itimer"; + private static ObjectMapper mapper = new ObjectMapper(); + private static HotspotGeneralMetrics metrics = new HotspotGeneralMetrics(); + private static AsyncProfiler profiler; + private static String garbagecatLib; + private static boolean flameDataCompression = false; + + private static final boolean DEBUG_SAVE_ORIG_DATA = false; + private static String JFG_ORIG_DATA_PATH = "JavaFlameGraph_Orig.data"; + // private static Map threadCpuMap; + private static volatile boolean NativeLibsLoaded = false; + private static volatile boolean GarbagecatLibLoaded = false; + private static ThreadInfoDrawData lastDrawData; + + static { + // mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + if (threadBean.isThreadAllocatedMemorySupported()) { + if (!threadBean.isThreadAllocatedMemoryEnabled()) { + threadBean.setThreadAllocatedMemoryEnabled(true); + } + } else { + LOGGER.warn("Jvm does not support get thread allocated bytes"); + } + + if (threadBean.isThreadCpuTimeSupported()) { + if (!threadBean.isThreadCpuTimeEnabled()) { + threadBean.setThreadCpuTimeEnabled(true); + } + } else { + LOGGER.warn("Jvm does not support get thread CPU time"); + } + + JFG_DATA_PATH = JvmMonitorUtils.getDataSavePath() + JFG_DATA_PATH; + if (DEBUG_SAVE_ORIG_DATA) { + JFG_ORIG_DATA_PATH = JvmMonitorUtils.getDataSavePath() + JFG_ORIG_DATA_PATH; + } + // threadCpuMap = new HashMap<>(); + } + + public static synchronized void loadNativeLibs() { + if (NativeLibsLoaded) { + LOGGER.warn("Native code libraries already loaded"); + return; + } + + String nPath = getNativeLibPath(); + try { + loadJarDll(nPath); + NativeLibsLoaded = true; + } catch (Exception e) { + LOGGER.error("Native code library failed to load.\n"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void loadJarDll(String name) throws Exception { + long enterTime = System.currentTimeMillis(); + String nativeLibPath = JvmMonitorUtils.getFileFromJar(name); + LOGGER.debug("Start Loading native library: " + name); + profiler = AsyncProfiler.getInstance(nativeLibPath); + long duration = System.currentTimeMillis() - enterTime; + LOGGER.debug("Loaded native library: " + name + " takes: " + duration + " ms."); + } + + public static synchronized void loadGarbagecatLib() { + if (GarbagecatLibLoaded) { + LOGGER.warn("Garabgecat lib already loaded"); + return; + } + + String nPath = getGarbagecatLibPath(); + try { + garbagecatLib = JvmMonitorUtils.getFileFromJar(nPath); + GarbagecatLibLoaded = true; + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + private static String getGarbagecatLibPath() { + final String libName = "garbagecat"; + String suffix = ".jar"; + final String parentFolder = "garbagecat_lib/"; + return parentFolder + libName + suffix; + } + + private static String getNativeLibPath() { + // TODO: support 32bits machine. + final String libName = "libasyncProfiler"; + final String parentFolder = "native_libs/"; + String suffix = ""; + String platformName = ""; + if (OSChecker.isMac()) { + suffix = ".so"; + platformName = "macos_x86_64"; + } else if (OSChecker.isSolaris() || OSChecker.isUnix()) { + suffix = ".so"; + if (OSChecker.isAarch64()) { + platformName = "linux_aarch64"; + } else { + platformName = "linux_x86_64"; + } + } else { + // windows + suffix = ".dll"; + platformName = "windows_x86_64"; + } + + return parentFolder + platformName + "/" + libName + suffix; + } + + private static class FlameGraphCollectionThread extends Thread { + private Command command; + + FlameGraphCollectionThread(Command comm) { + super.setName("FlameGraphCollectionThread"); + command = comm; + } + + @Override + public void run() { + LOGGER.debug("start Thread: " + this.getName()); + // This lock is used for limit collection threads + // as well as status update. + synchronized (JFG_MONITOR) { + + JFG_STATUS = TaskStatus.BUSY; + updateTaskStatus(command.getTaskId(), JFG_STATUS, TaskStatusInfo.BUSY); + TaskStatusInfo resInfo; + String argString = command.getMetaInfo(); + FlameGraphArguments args = null; + try { + args = mapper.readValue(argString, FlameGraphArguments.class); + } catch (IOException e) { + e.printStackTrace(); + LOGGER.warn("Can not parse flame graph arguments, will use default ones."); + args = null; + } + + resInfo = collectJavaFlameGraph(args, command); + if (resInfo != TaskStatusInfo.SUCCESS_NULL) { + LOGGER.error("TaskId: " + command.getTaskId() + " " + "Fail collect flame graph data"); + JFG_STATUS = TaskStatus.ERROR; + } else { + LOGGER.info("success collect flame graph data for task: " + command.getTaskId()); + JFG_STATUS = TaskStatus.COMPLETED; + JFG_LAST_EXEC_TIME = new Date().getTime(); + } + updateTaskStatus(command.getTaskId(), JFG_STATUS, resInfo); + } + } + } + + public static TaskStatusInfo collectJavaFlameGraph(FlameGraphArguments args, Command command) { + int duration = 5; // 5 seconds + String event = EVENT_CPU; + String filename = JFG_DATA_PATH; + String tempFileName = null; + boolean jsonSupport = true; + boolean compressSupport = false; + long dataSizeLimit = JFG_DATASIZE_LIMIT; + TaskStatusInfo ret = TaskStatusInfo.SUCCESS_NULL; + + File tmpDataFile = null; // "./JavaFlameGraph_" + pid + ".data"; + File jsonFile = null; + try { + tmpDataFile = File.createTempFile("JavaFlameGraph_", ".data"); + tmpDataFile.deleteOnExit(); + } catch (IOException e) { + LOGGER.error("TaskId: " + command.getTaskId() + " fail create temp file for JFG collection"); + e.printStackTrace(); + return TaskStatusInfo.NO_FILE; + } + + tempFileName = tmpDataFile.getAbsolutePath(); + LOGGER.info("Temp file for flame graph: " + tempFileName); + + // process args. + // TODO: try catch. + if (args != null) { + int dur = args.getDuration(); + duration = isLegalDuration(duration) ? dur : duration; + String evt = args.getEvent(); + event = isLegalFlameGraphEvent(evt) ? evt : event; + if (event.equalsIgnoreCase("latency")) { + event = EVENT_WALL; + } + + // TODO. fix bug here. and for query + String fn = args.getFilename(); + filename = idLegalFilename(fn) ? fn : filename; + + String dataSizeLimitString = args.getDataSizeLimit(); + if (dataSizeLimitString == null) { + dataSizeLimit = JFG_DATASIZE_LIMIT; + } else { + int length = dataSizeLimitString.length(); + if (length == 0) { + dataSizeLimit = JFG_DATASIZE_LIMIT; + } else { + LOGGER.debug("data size limit string is " + dataSizeLimitString); + try { + char u = dataSizeLimitString.charAt(length - 1); + long unit = 1; + if (u == 'k' || u == 'K') { + unit = KB; + } else if (u == 'm' || u == 'M') { + unit = MB; + } + String sub = dataSizeLimitString.substring(0, length - 1); + LOGGER.debug("data size limit to parse is: " + sub); + LOGGER.debug("data size limit parsed unit is: " + unit); + dataSizeLimit = Long.parseLong(sub); + dataSizeLimit = dataSizeLimit * unit; + } catch (Exception e) { + LOGGER.error("wrong data size limit args: " + dataSizeLimitString); + dataSizeLimit = JFG_DATASIZE_LIMIT; + } + LOGGER.debug("Parsed data size limit is " + dataSizeLimit); + } + if (dataSizeLimit < MININAL_JFG_DATASIZE_LIMIT) { + LOGGER.warn("parsed data limit too small, reset to default value: " + JFG_DATASIZE_LIMIT); + dataSizeLimit = JFG_DATASIZE_LIMIT; + } + } + + String jsonSupportString = args.getJsonSupport(); + if (jsonSupportString == null) { + jsonSupport = true; + } else if (jsonSupportString.equalsIgnoreCase("false")) { + jsonSupport = false; + } + + boolean compress = args.isCompression(); + if (!compress) { + flameDataCompression = false; + } else { + flameDataCompression = true; + } + } else { + LOGGER.warn("conduct java flame graph with default configuration"); + } + + if (event.equalsIgnoreCase(EVENT_CPU) && !OSChecker.isUnix()) { + LOGGER.warn("event " + EVENT_CPU + "is not suppoted because of perf unavailable, use event " + EVENT_WALL); + event = EVENT_WALL; + } + + // String arguments = "collapsed,cstack,dot,sig,ann,file=" + filename; // + threads? + String arguments; + if (!event.equalsIgnoreCase(EVENT_ALLOC)) { + arguments = "collapsed,cstack,dot,sig,file=" + tempFileName; // + threads? + } else { + int topN = 20; + LOGGER.debug("FlameGraph of Allocation only dump top 20 symbols"); + arguments = "collapsed,dot,sig,topN=" + topN + ",file=" + tempFileName; + } + + String startCmd = "event=" + event + "," + "start," + arguments; + String stopCmd = "event=" + event + "," + "stop," + arguments; + // LOGGER.info("ZLIN- Start collecting: " + startCmd + "duration: " + duration); + LOGGER.debug("Start collecting: " + startCmd + " duration: " + duration); + try { + profiler.execute(startCmd); + Thread.sleep((duration + 1) * 1000); + profiler.execute(stopCmd); + LOGGER.debug("Stop collect: " + stopCmd); + } catch (Exception e) { + LOGGER.error("TaskId: " + command.getTaskId() + " Flame Graph Can not collected: " + startCmd); + e.printStackTrace(); + return TaskStatusInfo.NO_FILE; + } + + if (!tmpDataFile.exists() || !tmpDataFile.isFile()) { + LOGGER.error("TaskId: " + command.getTaskId() + " " + "collect Finish with no file generated"); + return TaskStatusInfo.NO_FILE; + } + + String rawData = readFileToString(tempFileName); + // tmp data file can be deleted now + if (tmpDataFile.exists()) { + LOGGER.info("try to delete file: " + tmpDataFile.getName()); + if (tmpDataFile.delete()) { + LOGGER.info("successfully delete flame graph temp file"); + } else { + LOGGER.warn("fail delete flame graph temp file: " + tempFileName); + } + } + + if (rawData.length() == 0) { + LOGGER.error("TaskId: " + command.getTaskId() + " collect Finish with no file content"); + return TaskStatusInfo.NO_DATA; + } + LOGGER.debug("rawData size is :" + rawData.length() + " limit is: " + dataSizeLimit); + // LOGGER.debug("rawdata: " + rawData); + + String dataToWrite = rawData; + if (jsonSupport) { + String jsonData = null; + try { + jsonData = FlameGraphUtil.parseJsonFromString(rawData); + } catch (IOException e) { + LOGGER.error("TaskId: " + command.getTaskId() + " Failed to transfer data to json"); + return TaskStatusInfo.FAIL_TRANSFER; + } + if (jsonData != null) { + LOGGER.debug("jsonData size is :" + jsonData.length()); + } + dataToWrite = jsonData; + + if (flameDataCompression) { + dataToWrite = compressAndEncode(jsonData); + } + // LOGGER.debug("jsonData: " + jsonData); + if (dataToWrite != null && dataToWrite.length() >= dataSizeLimit) { + LOGGER.error("TaskId: " + command.getTaskId() + " " + "Collected Flame graph compressed data size too large: " + dataToWrite.length() + " raw size: " + rawData.length()); + return TaskStatusInfo.EXCEED_SIZE; + } + } else { + if (rawData.length() >= dataSizeLimit) { + LOGGER.error("TaskId: " + command.getTaskId() + " Collected Flame graph raw file size too large: " + rawData.length()); + return TaskStatusInfo.EXCEED_SIZE; + } + LOGGER.info("write Raw flame graph Data, size: " + dataToWrite.length()); + } + + readWriteLock.writeLock().lock(); + File f = new File(filename); + LOGGER.debug("filename: " + filename + " dataToWrite is " + dataToWrite); + if (dataToWrite != null && !writeStringToFile(f, dataToWrite)) { + LOGGER.error("TaskId: " + command.getTaskId() + " Failed to transfer data to json"); + ret = TaskStatusInfo.FAIL_TRANSFER; + } + if (DEBUG_SAVE_ORIG_DATA) { + File of = new File(JFG_ORIG_DATA_PATH); + if (!writeStringToFile(of, rawData)) { + LOGGER.error("TaskId: " + command.getTaskId() + " Failed to write original data to file"); + } + } + readWriteLock.writeLock().unlock(); + // return result. only the file name or the whole data? + // String fileData = readFileToString(filename); + // LOGGER.debug("flameGraph file content: " + fileData + " size: " + fileData.length()); + // lastCollectedFlameGraphData = filename; + return ret; + + } + + private static String compressAndEncode(String originString) { + if (originString == null || originString.length() == 0) { + return originString; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gzip = null; + try { + gzip = new GZIPOutputStream(out); + gzip.write(originString.getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (gzip != null) { + try { + gzip.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + Base64 base64encoder = new Base64(); + return base64encoder.encodeBase64String(out.toByteArray()); + } + + public static ResultPackage processJavaFlameGraphRequest(Command cmd) { + String action = cmd.getAction(); + String metaInfo = cmd.getMetaInfo(); + String data = ""; + ResultInfo rInfo; + LOGGER.debug("Start Process flame graph request: " + cmd); + HotspotCommandProcessor.loadNativeLibs(); + // TODO. move to sub method. + if (action.equalsIgnoreCase("collect")) { + switch (JFG_STATUS) { + case ERROR: { + LOGGER.warn("Collect Flame Graph with ERROR status"); + } // fall through + case COMPLETED: { + // Dup with available, + // TODO: consider report error if time interval is too small. + if (JFG_LAST_EXEC_TIME == 0) { + // fall through, fist time collection + } else if (new Date().getTime() - JFG_LAST_EXEC_TIME > JFG_EXEC_INTERVAL) { + // fall through + } else { + // Warning or Error? + LOGGER.warn("Collect Flame Graph too frequently"); + // continue or interrupt? + } + // fall through + } // fall through + case AVAILABLE: { + taskMap.put(cmd.getTaskId(), new TaskInfo(cmd, TaskStatus.BUSY, TaskStatusInfo.BUSY)); + FlameGraphCollectionThread t = new FlameGraphCollectionThread(cmd); + t.setDaemon(true); + t.setContextClassLoader(HotspotCommandProcessor.class.getClassLoader()); + t.start(); + rInfo = new ResultInfo(TaskStatus.BUSY.name()); + } + break; + case BUSY: + // return BUSY! + data = "taskId: " + cmd.getTaskId() + " BUSY collecting java flame graph data"; + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.BUSY.name(), data); + break; + default: + data = "taskId: " + cmd.getTaskId() + " Unknown Flame graph collection status: " + JFG_STATUS; + LOGGER.error(data); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), data); + } + } else if (action.equalsIgnoreCase("query")) { + LOGGER.debug("process query: " + cmd); + // acquire read lock to block write while reading. or block read while writing. + String targetTaskId = cmd.getTaskId(); + TaskInfo taskInfo = getTaskInfo(targetTaskId); + if (taskInfo == null) { + data = "No task with taskId: " + targetTaskId + " found"; + LOGGER.error(data); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.NO_DATA.name(), data); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + TaskStatus targetTaskStatus = taskInfo.getStatus(); + // TaskStatusInfo targetTaskStatusInfo = taskInfo.getTaskStatusInfo(); + FlameGraphQueryMetaInfo requireData = null; + try { + requireData = mapper.readValue(metaInfo, FlameGraphQueryMetaInfo.class); + } catch (IOException e) { + LOGGER.error("TaskId: " + targetTaskId + " Can not parse metaInfo of requireData"); + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "wrong query metaInfo"); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + if (targetTaskStatus == TaskStatus.COMPLETED) { + LOGGER.debug("acquire lock......"); + readWriteLock.readLock().lock(); + LOGGER.debug("acquire lock ......Pass"); + if (requireData != null && requireData.getRequireData() != null && requireData.getRequireData().equalsIgnoreCase("TRUE")) { + LOGGER.debug("testing file: " + JFG_DATA_PATH); + // TODO move to collection side. + String retMsg1 = readFileToString(JFG_DATA_PATH); + if (flameDataCompression) { + data = retMsg1; + } else { + data = retMsg1; + } + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", data, flameDataCompression); + } else { + LOGGER.debug("No required data!"); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name()); + } + LOGGER.debug("release lock......"); + readWriteLock.readLock().unlock(); + LOGGER.debug("release lock......Pass"); + } else if (targetTaskStatus == TaskStatus.ERROR) { + TaskStatusInfo ts = taskInfo.getTaskStatusInfo(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), ts.name(), ""); + } else if (targetTaskStatus == TaskStatus.BUSY) { + rInfo = new ResultInfo(TaskStatus.BUSY.name()); + } else { + // Should not be here! + data = "TaskId: " + targetTaskId + " Unreachable targetTaskStatus: " + targetTaskStatus.name(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), data); + } + } else { + LOGGER.error("Unreachable action: " + action); + data = "TaskId: " + cmd.getTaskId() + " Unknown Flame graph action: " + action; + LOGGER.error(data); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), data); + } + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + public static String getPid() { + if (pid == null) { + String jvmName = runtimeBean.getName(); + LOGGER.debug("get Pid: " + jvmName); + pid = jvmName.split("@")[0]; + } + return pid; + } + + public static ResultPackage getProcessId(Command cmd) { + ResultInfo rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", getPid()); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + public static ResultPackage getHeapDump(Command cmd) { + HeapDumperArguments args = null; + String fileName = null; + String path; + String metaString = cmd.getMetaInfo(); + String action = cmd.getAction(); + + if (metaString.equalsIgnoreCase("") || metaString == null) { + LOGGER.info("metaInfo is null, use default options"); + } else { + try { + args = mapper.readValue(metaString, HeapDumperArguments.class); + fileName = args.getFilename(); + } catch (IOException e) { + e.printStackTrace(); + args = null; + LOGGER.warn("Can not parse heapdump arguments, will use default ones."); + } + } + + if (fileName == null || fileName == "") { + path = JvmMonitorUtils.getDataSavePath() + HEAPDUMP_DEFAULT_FILE_NAME + ".hprof"; + } else { + path = fileName + ".hprof"; + } + + if (action.equalsIgnoreCase("collect")) { + File file = new File(path); + if (file.exists()) { + file.delete(); + } + HeapDumper.dumpHeap(path, true); + // just start heapdump + ResultInfo rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), TaskStatusInfo.SUCCESS_NULL.name(), "start Heapdump"); + return new ResultPackage(cmd.getTaskId(), rInfo); + } else if (action.equalsIgnoreCase("query")) { + File heapdumpFile = new File(path); + ResultInfo rInfo; + if (heapdumpFile.length() == 0) { + rInfo = new ResultInfo(TaskStatus.BUSY.name(), TaskStatusInfo.BUSY.name(), ""); + return new ResultPackage(cmd.getTaskId(), rInfo); + } else { + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), TaskStatusInfo.SUCCESS_NULL.name(), path); + } + return new ResultPackage(cmd.getTaskId(), rInfo); + } else { + LOGGER.error("Heapdump action is wrong!"); + ResultInfo rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "Heapdump action is wrong"); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + } + + public static ResultPackage getThreadDump(Command cmd) { + + boolean compressThreadInfo = false; + boolean needDrawData = false; + ThreadInfoArguments args = null; + String metaString = cmd.getMetaInfo(); + LOGGER.debug("thread dump : metaString " + metaString); + if (metaString.equalsIgnoreCase("") || metaString == null) { + compressThreadInfo = false; + needDrawData = false; + } else { + try { + args = mapper.readValue(metaString, ThreadInfoArguments.class); + compressThreadInfo = args.isCompression(); + needDrawData = args.isDrawdata(); + } catch (IOException e) { + e.printStackTrace(); + LOGGER.warn("Can not parse jstack arguments, will use default ones."); + compressThreadInfo = false; + args = null; + } + } + + // don't collect thread dump for draw data. only collect when there is no draw data. + if (needDrawData && (lastDrawData != null)) { + // return lastDrawData. + String threadDrawDataSB = null; + try { + threadDrawDataSB = mapper.writeValueAsString(lastDrawData); + if (compressThreadInfo) { + LOGGER.debug("compress draw data"); + threadDrawDataSB = compressAndEncode(threadDrawDataSB); + } + } catch (JsonProcessingException e) { + e.printStackTrace(); + String errMsg = "Error at writing thread Draw data to json"; + LOGGER.error(errMsg); + ResultInfo rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), errMsg); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + ResultInfo rInfo = null; + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", threadDrawDataSB, compressThreadInfo); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + ThreadInfo[] threadInfos; + // long start = System.currentTimeMillis(); + threadInfos = threadBean.dumpAllThreads(true, false); + // long phase1 = System.currentTimeMillis(); + //LOGGER.info("dump All threads took " + (phase1 - start)/1000.0 + "s"); + ThreadInfoList tiList = new ThreadInfoList(); + if (!needDrawData || lastDrawData == null) { + // collect drawdata every time we collect thread dump. + ThreadInfoDrawData drawData = new ThreadInfoDrawData(); + for (ThreadInfo info : threadInfos) { + tiList.add(makeThreadInfoEntry(info, compressThreadInfo, drawData)); + } + lastDrawData = drawData; + } + + if (needDrawData && lastDrawData != null) { + // return lastDrawData. + String threadDrawDataSB = null; + try { + threadDrawDataSB = mapper.writeValueAsString(lastDrawData); + if (compressThreadInfo) { + LOGGER.debug("compress draw data"); + threadDrawDataSB = compressAndEncode(threadDrawDataSB); + } + } catch (JsonProcessingException e) { + e.printStackTrace(); + String errMsg = "Error at writing thread Draw data to json"; + LOGGER.error(errMsg); + ResultInfo rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), errMsg); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + ResultInfo rInfo = null; + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", threadDrawDataSB, compressThreadInfo); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + String threadDumpSB = null; + + try { + threadDumpSB = mapper.writeValueAsString(tiList); + } catch (JsonProcessingException e) { + e.printStackTrace(); + String errMsg = "Error at writting thread Dump to json"; + LOGGER.error(errMsg); + ResultInfo rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), errMsg); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + LOGGER.debug("thread dump info:" + threadDumpSB); + ResultInfo rInfo = null; + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", threadDumpSB, compressThreadInfo); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + private static ThreadInfoEntry makeThreadInfoEntry(ThreadInfo info, boolean compress, ThreadInfoDrawData drawData) { + + ThreadInfoEntry entry = new ThreadInfoEntry(); + PerThreadDrawData perThreadDrawData = new PerThreadDrawData(); + + entry.setThreadName(info.getThreadName()); + entry.setThreadState(info.getThreadState().name()); + + perThreadDrawData.setThreadName(info.getThreadName()); + perThreadDrawData.setThreadState(info.getThreadState()); + perThreadDrawData.setNative(info.isInNative()); + + long allocatedBytes = 0; + allocatedBytes = threadBean.getThreadAllocatedBytes(info.getThreadId()); + entry.setThreadAllocatedBytes(Long.toString(allocatedBytes)); + + long lastThreadTime = threadBean.getThreadCpuTime(info.getThreadId()); + // Todo, use the latest one? + entry.setThreadCpuTime(Long.toString(lastThreadTime)); + long lastNanoTime = System.nanoTime(); + // sleep for cpu util + try { + Thread.sleep(0, THREAD_UTIL_INTERVAL_NS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + long threadCpuTime = threadBean.getThreadCpuTime(info.getThreadId()); + long now = System.nanoTime(); + + LOGGER.debug("thread: " + info.getThreadId() + " thread cpu time: " + threadCpuTime + " lastThreadTime: " + lastThreadTime + " now: " + now + " lasttimestamp: " + lastNanoTime); + + double threadCpuUtil = ((double) (threadCpuTime - lastThreadTime)) / (now - lastNanoTime); + if (threadCpuUtil < 0.001) { + threadCpuUtil = 0.0; + } + + entry.setThreadCpuUtil(Double.toString(threadCpuUtil * 100.0)); + + entry.setThreadBlockCount(Long.toString(info.getBlockedCount())); + + StringBuilder sb = new StringBuilder(" Id=" + info.getThreadId()); + + if (info.getLockName() != null) { + sb.append(" on " + info.getLockName()); + } + if (info.getLockOwnerName() != null) { + sb.append(" owned by \"" + info.getLockOwnerName() + "\" Id=" + info.getLockOwnerId()); + } + if (info.isSuspended()) { + sb.append(" (suspended)"); + } + if (info.isInNative()) { + sb.append(" (in native)"); + } + sb.append('\n'); + + StackTraceElement[] stackTrace = info.getStackTrace(); + if (stackTrace.length > 0) { + perThreadDrawData.setMethodInfo(stackTrace[0].toString()); + } else { + perThreadDrawData.setMethodInfo(""); + } + StringBuilder traceSB = new StringBuilder(""); + int i = 0; + for (; i < stackTrace.length && i < MAX_FRAMES; i++) { + StackTraceElement ste = stackTrace[i]; + sb.append(" at " + ste.toString()); + sb.append('\n'); + + traceSB.append(" at " + ste.toString()); + traceSB.append('\n'); + + if (i == 0 && info.getLockInfo() != null) { + Thread.State ts = info.getThreadState(); + switch (ts) { + case BLOCKED: + sb.append(" - blocked on " + info.getLockInfo()); + sb.append('\n'); + break; + case WAITING: + case TIMED_WAITING: + sb.append(" - waiting on " + info.getLockInfo()); + sb.append('\n'); + break; + default: + } + } + MonitorInfo[] lockedMonitors = info.getLockedMonitors(); + for (MonitorInfo mi : lockedMonitors) { + if (mi.getLockedStackDepth() == i) { + sb.append(" - locked " + mi); + sb.append('\n'); + } + } + } + if (i < stackTrace.length) { + sb.append(" ..."); + sb.append('\n'); + + traceSB.append(" ..."); + traceSB.append('\n'); + } + perThreadDrawData.setTraceInfo(traceSB.toString()); + + LockInfo[] locks = info.getLockedSynchronizers(); + if (locks.length > 0) { + sb.append("\n Number of locked synchronizers = " + locks.length); + sb.append('\n'); + for (LockInfo li : locks) { + sb.append(" - " + li); + sb.append('\n'); + } + } + sb.append("\n"); + String rawInfo = sb.toString(); + if (compress) { + String compressed = compressAndEncode(rawInfo); + entry.setThreadInfos(compressed); + } else { + entry.setThreadInfos(rawInfo); + } + drawData.update(perThreadDrawData); + return entry; + } + + public static ResultPackage getDeadLockInfo(Command cmd) { + long[] tids; + StringBuilder lockMsg = new StringBuilder(); + ResultInfo rInfo; + String retMsg = ""; + DeadLockInfo dlinfo = new DeadLockInfo(); + if (threadBean.isSynchronizerUsageSupported()) { + tids = threadBean.findDeadlockedThreads(); + if (tids == null) { + // lockMsg.append("no dead lock detected."); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name()); + return new ResultPackage(cmd.getTaskId(), rInfo); + } else { + ThreadInfo[] infos = threadBean.getThreadInfo(tids, true, true); + for (ThreadInfo ti : infos) { + lockMsg.append("\"" + getThreadInfo(ti)).append(getMonitorInfo(ti)).append(getLockInfo(ti.getLockedSynchronizers())).append("\"").append('\n'); + } + LOGGER.debug("dead lock info: " + lockMsg.toString()); + dlinfo.setLockInfos(lockMsg.toString()); + dlinfo.setThreadCount(infos.length); + // lockMsg.append(", \" threadCount\" : ").append(infos.length).append(" }"); + } + } else { + tids = threadBean.findMonitorDeadlockedThreads(); + if (tids == null) { + // lockMsg.append("no dead lock detected."); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name()); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + ThreadInfo[] infos = threadBean.getThreadInfo(tids, Integer.MAX_VALUE); + for (ThreadInfo ti : infos) { + lockMsg.append("\"" + getThreadInfo(ti) + "\"").append('\n'); + } + LOGGER.debug("dead lock info: " + lockMsg.toString()); + dlinfo.setLockInfos(lockMsg.toString()); + dlinfo.setThreadCount(infos.length); + + //lockMsg.append(", \" threadCount\" : ").append(infos.length).append(" }"); + } + + try { + retMsg = (dlinfo.getThreadCount() == 0) ? "" : mapper.writeValueAsString(dlinfo); + } catch (JsonProcessingException e) { + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), retMsg); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", retMsg); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + private static String getLockInfo(LockInfo[] locks) { + StringBuilder sb = new StringBuilder(INDENT + "Locked synchronizers: count = " + locks.length + "\n"); + for (LockInfo li : locks) { + sb.append(INDENT + " - " + li + "\n"); + } + return sb.toString(); + } + + private static String getMonitorInfo(ThreadInfo ti) { + StringBuilder sb = new StringBuilder(""); + MonitorInfo[] monitors = ti.getLockedMonitors(); + sb.append(INDENT + "Locked monitors: count = " + monitors.length + "\n"); + for (MonitorInfo mi : monitors) { + sb.append(INDENT + " - " + mi + " locked at \n"); + sb.append(INDENT + " " + mi.getLockedStackDepth() + " " + mi.getLockedStackFrame() + "\n"); + } + return sb.toString(); + } + + private static String getThreadInfo(ThreadInfo ti) { + StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + " Id=" + ti.getThreadId() + " in " + ti.getThreadState()); + if (ti.getLockName() != null) { + sb.append(" on lock=" + ti.getLockName()); + } + if (ti.isSuspended()) { + sb.append(" (suspended)"); + } + if (ti.isInNative()) { + sb.append(" (running in native)"); + } + sb.append("\n"); + if (ti.getLockOwnerName() != null) { + sb.append(INDENT + " owned by " + ti.getLockOwnerName() + " Id=" + ti.getLockOwnerId() + "\n"); + } + return sb.toString(); + } + + public static String readFileToString(String fileName) { + String encoding = "UTF-8"; + File file = new File(fileName); + Long filelength = file.length(); + byte[] filecontent = new byte[filelength.intValue()]; + FileInputStream in = null; + try { + in = new FileInputStream(file); + in.read(filecontent); + } catch (FileNotFoundException e) { + LOGGER.error("Error read data from file: " + fileName); + e.printStackTrace(); + } catch (IOException e) { + LOGGER.error("Error read data from file: " + fileName); + e.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + LOGGER.error("Error close input stream for file: " + fileName); + e.printStackTrace(); + } + } + } + try { + return new String(filecontent, encoding); + } catch (UnsupportedEncodingException e) { + System.err.println("The OS does not support " + encoding); + e.printStackTrace(); + return null; + } + } + + public static boolean writeStringToFile(File file, String string) { + FileOutputStream fos = null; + try { + if (!file.exists()) { + file.createNewFile(); + } + fos = new FileOutputStream(file); + byte[] bytesArray = string.getBytes("8859_1"); + fos.write(bytesArray); + fos.flush(); + } catch (IOException e) { + LOGGER.error("Fail write file: " + file.getName()); + e.printStackTrace(); + return false; + } finally { + try { + if (fos != null) { + fos.close(); + } + } catch (IOException e) { + LOGGER.error("Fail close file: " + file.getName()); + return false; + } + } + return true; + } + + private static boolean idLegalFilename(String fn) { + // TODO: more strict check of file path. + if (fn == null || fn.isEmpty()) { + return false; + } + return true; + } + + private static boolean isLegalFlameGraphEvent(String evt) { + if (evt == null || evt.isEmpty()) { + return false; + } + if (evt.equalsIgnoreCase("cpu") || evt.equalsIgnoreCase("alloc") || evt.equalsIgnoreCase("lock") || evt.equalsIgnoreCase("latency") || evt.equalsIgnoreCase("wall")) { + return true; + } + return true; + } + + private static boolean isLegalDuration(int dur) { + if (dur > 0) { + return true; + } + return false; + } + + // TODO. throw exception + public static void updateTaskStatus(String taskId, TaskStatus status, TaskStatusInfo info) { + TaskInfo tInfo = taskMap.get(taskId); + if (tInfo == null) { + LOGGER.error("Can not find taskInfo of taskId:" + taskId); + } else { + tInfo.setStatus(status, info); + } + } + + public static TaskInfo getTaskInfo(String taskId) { + TaskInfo tInfo = taskMap.get(taskId); + if (tInfo == null) { + return null; + } else { + return tInfo; + } + } + + + private static ResultPackage getGeneralMetrics(Command cmd) { + String retMsg = null; + + metrics.collect(); + TaskStatus status = TaskStatus.ERROR; + try { + retMsg = mapper.writeValueAsString(metrics); + status = TaskStatus.COMPLETED; + } catch (JsonProcessingException e) { + e.printStackTrace(); + LOGGER.error("Can not convert metrics to JSON"); + retMsg = null; + status = TaskStatus.ERROR; + } + ResultInfo ri = new ResultInfo(status.name(), "", retMsg); + return new ResultPackage(cmd.getTaskId(), ri); + } + + private List> getTopThreadList(int topThreads) { + boolean enabledCpu = false; + try { + if (threadBean.isThreadCpuTimeSupported()) { + if (!threadBean.isThreadCpuTimeEnabled()) { + enabledCpu = true; + threadBean.setThreadCpuTimeEnabled(true); + } + } else { + LOGGER.error("MBean doesn't support thread CPU Time"); + return null; + } + ThreadInfo[] allThreadInfos; + allThreadInfos = threadBean.dumpAllThreads(true, false); + Map threadInfos = new HashMap<>(); + for (ThreadInfo info : allThreadInfos) { + long threadId = info.getThreadId(); + if (Thread.currentThread().getId() == threadId) { + continue; + } + long cpuTime = threadBean.getThreadCpuTime(threadId); + if (cpuTime == -1) { + continue; + } + if (info == null) { + continue; + } + threadInfos.put(threadId, new JTopThreadInfo(cpuTime, info)); + } + + try { + Thread.sleep(TOP_INTERVAL); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + + for (ThreadInfo info : allThreadInfos) { + long threadId = info.getThreadId(); + if (Thread.currentThread().getId() == threadId) { + continue; + } + + long cpuTime = threadBean.getThreadCpuTime(threadId); + if (cpuTime == -1) { + threadInfos.remove(threadId); + continue; + } + if (info == null) { + threadInfos.remove(threadId); + continue; + } + JTopThreadInfo data = threadInfos.get(threadId); + if (data != null) { + data.setDelta(cpuTime, info); + } else { + threadInfos.remove(threadId); + } + } + List jtis = new ArrayList<>(threadInfos.values()); + SortedMap map = new TreeMap(); + for (int i = 0; i < jtis.size(); i++) { + JTopThreadInfo jti = jtis.get(i); + long cpuRatio = jti.cpuTime / (TOP_INTERVAL * 10000); + if (cpuRatio != 0) { + map.put(new Long(cpuRatio), jti.info); + } + } + + // build the thread list and sort it with cpuRatio + // in decreasing order + Set> set = map.entrySet(); + List> list = new ArrayList>(set); + Collections.reverse(list); + final int tops = Math.min(topThreads, list.size()); + List> subList = list.subList(0, tops); + return subList; + } finally { + if (enabledCpu) { + threadBean.setThreadCpuTimeEnabled(false); + } + } + } + + private static boolean isLegalTopThreads(int topThreads) { + if (topThreads > 0) { + return true; + } + return false; + } + + private static ResultPackage getJTopInfo(Command cmd) { + String argString = cmd.getMetaInfo(); + JTopArguments args = null; + int topThreads = defaultTopThreads; + try { + args = mapper.readValue(argString, JTopArguments.class); + } catch (IOException e) { + e.printStackTrace(); + LOGGER.warn("Can not parse jtop arguments, will use default ones."); + args = null; + } + if (args != null) { + int tt = args.getTopThreads(); + topThreads = isLegalTopThreads(tt) ? tt : topThreads; + } + + HotspotCommandProcessor hcp = new HotspotCommandProcessor(); + List> list = hcp.getTopThreadList(topThreads); + ThreadInfoList jiList = new ThreadInfoList(); + for (int i = 0; i < list.size(); i++) { + jiList.add(makeJTopInfoEntry(list.get(i).getKey(), list.get(i).getValue())); + } + + String jtopDumpSB = null; + try { + jtopDumpSB = mapper.writeValueAsString(jiList); + } catch (JsonProcessingException e) { + e.printStackTrace(); + String errMsg = "Error at writing thread Dump to json"; + LOGGER.error(errMsg); + ResultInfo rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), errMsg); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + LOGGER.debug("jtop dump info:" + jtopDumpSB); + ResultInfo rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), jtopDumpSB); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + private static JTopInfoEntry makeJTopInfoEntry(Long threadCPURatio, ThreadInfo info) { + JTopInfoEntry entry = new JTopInfoEntry(); + entry.setThreadName(info.getThreadName()); + entry.setThreadCPURatio(threadCPURatio.toString() + "%"); + StringBuilder sb = new StringBuilder("TId: " + info.getThreadId()); + + int i = 0; + boolean hasDumpNative = false; + StackTraceElement[] stackTrace = info.getStackTrace(); + for (; i < stackTrace.length && i < MAX_FRAMES; i++) { + StackTraceElement ste = stackTrace[i]; + if (ste.isNativeMethod() && !hasDumpNative) { + sb.append(" at " + ste.toString()); + sb.append('\n'); + hasDumpNative = true; + continue; + } else { + sb.append(" at " + ste.toString()); + sb.append('\n'); + break; + } + } + if (i < stackTrace.length) { + sb.append(" ..."); + sb.append('\n'); + } + sb.append("\n"); + entry.setThreadInfos(sb.toString()); + return entry; + } + + public static ResultPackage processCommandInternal(Command cmd, Instrumentation inst) { + String command = cmd.getType(); + ResultPackage rp = null; + + long enterTime = System.currentTimeMillis(); + if (cmd.getTaskId().equalsIgnoreCase("Qoco_getPid")) { + LOGGER.debug("enter: process command internal: " + cmd); + } else { + LOGGER.info("enter: process command internal: " + cmd); + } + + if (command.equalsIgnoreCase("ThreadDump")) { + rp = getThreadDump(cmd); + } else if (command.equalsIgnoreCase("DeadLockDetect")) { + rp = getDeadLockInfo(cmd); + } else if (command.equalsIgnoreCase("JavaFlameGraph")) { + rp = processJavaFlameGraphRequest(cmd); + } else if (command.equalsIgnoreCase("GetPid")) { + rp = getProcessId(cmd); + } else if (command.equalsIgnoreCase("Metrics")) { + rp = getGeneralMetrics(cmd); + } else if (command.equalsIgnoreCase("JTop")) { + rp = getJTopInfo(cmd); + } else if (command.equalsIgnoreCase("MethodProfile")) { + LOGGER.error("UnSupport Command: " + command); + rp = new ResultPackage(null, null); +// rp = processMethodProfile(cmd, inst); + } else if (command.equalsIgnoreCase("VMFlags")) { + rp = getVmFlags(cmd); + } else if (command.equalsIgnoreCase("HeapDump")) { + rp = getHeapDump(cmd); + } else if (command.equalsIgnoreCase("GCLogAnalyze")) { + rp = analyzeGCLog(cmd); + } else if (command.equalsIgnoreCase("JMapHisto")) { + rp = jmapHisto(cmd); + } else if (command.equalsIgnoreCase("JHat")) { + rp = jhatAnalyze(cmd); + } else { + LOGGER.error("Unknown Command: " + command); + return new ResultPackage(null, null); + } + + long duration = System.currentTimeMillis() - enterTime; + if (cmd.getTaskId().equalsIgnoreCase("Qoco_getPid")) { + LOGGER.debug("leave: Response package of command " + cmd + " duration: " + duration + " ms" + " message : " + rp); + } else { + if (cmd.getAction().equalsIgnoreCase("query") || command.equalsIgnoreCase("ThreadDump") || command.equalsIgnoreCase("DeadLockDetect")) { + LOGGER.info("leave: Response status of command " + cmd + " duration: " + duration + " ms" + " message : " + rp.getResultInfo().getStatus()); + + } else { + LOGGER.info("leave: Response package of command " + cmd + " duration: " + duration + " ms" + " message : " + rp); + } + } + return rp; + } + + public static ResultPackage getVmFlags(Command cmd) { + ResultInfo rInfo; + try { + String[] command = {"/bin/sh", "-c", "jcmd " + getPid() + " VM.flags > " + VMFLAGS_PATH}; + Process process = null; + process = Runtime.getRuntime().exec(command); + process.waitFor(); + String data = readFileToString(VMFLAGS_PATH); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", data); + } catch (Exception e) { + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "wrong query metaInfo"); + } + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + public static ResultPackage analyzeGCLog(Command cmd) { + GCLogAnalyzerArguments args = null; + String reportFileName = GCLOGREPORT_PATH; + String gcLogFile; + String path; + String metaString = cmd.getMetaInfo(); + ResultInfo rInfo; + if (metaString.equalsIgnoreCase("") || metaString == null) { + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "wrong query metaInfo"); + } else { + HotspotCommandProcessor.loadGarbagecatLib(); + try { + args = mapper.readValue(metaString, GCLogAnalyzerArguments.class); + gcLogFile = args.getFilename(); + File logFile = new File(gcLogFile); + if (!logFile.exists()) { + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "gc log file does not exist"); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + try { + File reportFile = new File(GCLOGREPORT_PATH); + if (reportFile.exists()) { + reportFile.delete(); + } + String[] command = {"/bin/sh", "-c", "java -jar " + garbagecatLib + " " + gcLogFile + " -o " + GCLOGREPORT_PATH}; + Process process = null; + process = Runtime.getRuntime().exec(command); + process.waitFor(); + String data = readFileToString(GCLOGREPORT_PATH); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", data); + + } catch (Exception e) { + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "analyze gc log fails"); + } + } catch (IOException e) { + e.printStackTrace(); + LOGGER.warn("Can not parse gclog analyze arguments, will use default ones."); + args = null; + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "wrong query metaInfo"); + } + } + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + public static boolean isLocalPortUsing(int port) { + boolean flag = true; + try { + flag = isPortUsing("127.0.0.1", port); + } catch (Exception e) { + e.printStackTrace(); + } + return flag; + } + + public static boolean isPortUsing(String host, int port) throws UnknownHostException { + boolean flag = false; + InetAddress address = InetAddress.getByName(host); + try { + Socket socket = new Socket(address, port); + flag = true; + } catch (IOException e) { + e.printStackTrace(); + } + return flag; + } + + public static ResultPackage jhatAnalyze(Command cmd) { + String action = cmd.getAction(); + ResultInfo rInfo; + String metaString = cmd.getMetaInfo(); + if (action.equalsIgnoreCase("collect")) { + if (isLocalPortUsing(JHAT_SERVER_PORT)) { + KillPortServer killPortServer = new KillPortServer(); + killPortServer.start(JHAT_SERVER_PORT); + } + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + File tmpDumpFile = File.createTempFile("Heapdump_", ".hprof"); + tmpDumpFile.delete(); + HeapDumper.dumpHeap(tmpDumpFile.getAbsolutePath(), true); + while (true) { + if (tmpDumpFile.length() != 0) { + String[] command = {"/bin/sh", "-c", "jhat -J-mx512m " + tmpDumpFile.getAbsolutePath()}; + Process process = null; + process = Runtime.getRuntime().exec(command); + process.waitFor(); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + thread.start(); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", "Start jhat analyzing"); + } else if (action.equalsIgnoreCase("query")) { + if (isLocalPortUsing(JHAT_SERVER_PORT)) { + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", ""); + } else { + rInfo = new ResultInfo(TaskStatus.BUSY.name(), "", "jhat is analyzing"); + } + } else { + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), ""); + } + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + public static ResultPackage jmapHisto(Command cmd) { + ResultInfo rInfo; + try { + File file = new File(JMAPTMP_PATH); + String[] command = {"/bin/sh", "-c", "jmap -histo:live " + getPid() + " > " + JMAPTMP_PATH}; + Process process = null; + process = Runtime.getRuntime().exec(command); + process.waitFor(); + + JmapInfoList jmapInfoList = new JmapInfoList(); + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + String str = null; + int i = 0; + while ((str = br.readLine()) != null) { + if (i < 3) { + i++; + continue; + } + JmapInfo jmapInfo = new JmapInfo(); + String[] ss = str.split("\\s+"); + if (ss.length == 6) { + jmapInfo.setInstances(ss[2]); + jmapInfo.setBytes(ss[3]); + jmapInfo.setClassName(ss[4] + ss[5]); + } else if (ss.length == 5) { + if (ss[1].contains(":")) { + jmapInfo.setInstances(ss[2]); + jmapInfo.setBytes(ss[3]); + jmapInfo.setClassName(ss[4]); + } else { + jmapInfo.setInstances(ss[1]); + jmapInfo.setBytes(ss[2]); + jmapInfo.setClassName(ss[3] + ss[4]); + } + } else if (ss.length == 4) { + jmapInfo.setInstances(ss[1]); + jmapInfo.setBytes(ss[2]); + jmapInfo.setClassName(ss[3]); + } else { + continue; + } + jmapInfoList.add(jmapInfo); + } + br.close(); + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", mapper.writeValueAsString(jmapInfoList)); + } catch (Exception e) { + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "jmap exec failed"); + } + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + private static ResultPackage processMethodProfile(Command cmd, Instrumentation inst) { + // '{"taskId":"test1","type”:”MethodProfile","action”:"trace","metaInfo":"{\”args\":\”class.*;\"}"}' + String action = cmd.getAction(); + String metaInfo = cmd.getMetaInfo(); + ResultInfo rInfo; + String data = ""; + LOGGER.debug("Start process method profile " + cmd); + MethodProfileMetaInfo meta; + try { + meta = mapper.readValue(metaInfo, MethodProfileMetaInfo.class); + } catch (IOException e) { + LOGGER.error("TaskId: " + cmd.getTaskId() + " Can not parse arguments of method profile"); + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "wrong query metaInfo"); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + String path = JvmMonitorUtils.getDataSavePath() + METHOD_TRACE_DUMP_FILE; + + if (action.equalsIgnoreCase("trace")) { + // action and args: \ + // "trace" {times, args} // method invocation trace + // "singleStep" {time, args} + // "profile" // method argument, return values. + // "Details" trace + profile. + String options = "action=" + action + ",count=" + meta.getProfileCount() + "," + meta.getArgs(); +// JvmMonitorBCIAgentOptions bciAgentOptions = new JvmMonitorBCIAgentOptions(); + + // delete the former profile log file + File file = new File(path); + if (file.exists()) { + file.delete(); + } + + try { +// JvmMonitorBCIAgent.agentmain(options, inst); + } catch (Exception e) { + e.printStackTrace(); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), "wrong options: " + options); + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + // just start profiling, instrumentation may not take effect at the moment. + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), TaskStatusInfo.SUCCESS_NULL.name(), "start Instrumentation"); + } else if (action.equalsIgnoreCase("query")) { + LOGGER.debug("process query: " + cmd); + File file = new File(path); + if (file.exists()) { + String retMsg = readFileToString(path); + if (retMsg.length() == 0) { + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.NO_DATA.name(), ""); + } else { + rInfo = new ResultInfo(TaskStatus.COMPLETED.name(), "", retMsg); + } + } else { + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.NO_DATA.name(), ""); + } + } else { + LOGGER.error("Unreachable action: " + action); + data = "TaskId: " + cmd.getTaskId() + " Unknown Flame graph action: " + action; + LOGGER.error(data); + rInfo = new ResultInfo(TaskStatus.ERROR.name(), TaskStatusInfo.MAL_FUNC.name(), data); + } + + return new ResultPackage(cmd.getTaskId(), rInfo); + } + + public static String processCommand(String cmdString, Instrumentation inst) { + String ret = null; + Command cmd = null; + try { + LOGGER.debug("Process Command: " + cmdString); + cmd = mapper.readValue(cmdString, Command.class); + // taskMap.put(cmd.getTaskID(), new TaskInfo(cmd, TaskStatus.BUSY)); + } catch (Exception e) { + LOGGER.error("Illegal request command:" + cmdString + " (E): " + e); + e.printStackTrace(); + // taskMap.put(cmd.getTaskID(), new TaskInfo(null, TaskStatus.ERROR)); + return null; + } + // TODO. record status? + if (cmd == null) { + LOGGER.error("Illegal request command:" + cmdString); + return null; + } + + ResultPackage result = processCommandInternal(cmd, inst); + if (result.getResultInfo() == null) { + LOGGER.error("Can not generate result package!"); + // updateTaskStatus(cmd.getTaskID(), TaskStatus.ERROR); + return null; + } + + try { + ret = mapper.writeValueAsString(result); + LOGGER.debug("resultPackage: " + ret); + return ret; + } catch (JsonProcessingException e) { + LOGGER.error("Fail generate response json String"); + e.printStackTrace(); + return null; + } + // update status + //updateTaskStatus(cmd.getTaskID(), TaskStatus.valueOf(result.getResultInfo().getStatus())); + } + + class JTopThreadInfo { + private boolean deltaDone; + public long cpuTime; + public ThreadInfo info; + + JTopThreadInfo(long cpuTime, ThreadInfo info) { + this.cpuTime = cpuTime; + this.info = info; + } + + public void setDelta(long cpuTime, ThreadInfo info) { + if (deltaDone) { + LOGGER.error("setDelta already called once"); + return; + } + deltaDone = true; + this.cpuTime = cpuTime - this.cpuTime; + this.info = info; + } + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotGeneralMetrics.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotGeneralMetrics.java new file mode 100644 index 00000000..c2c8b758 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotGeneralMetrics.java @@ -0,0 +1,459 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.tencent.femas.tencentcloudjvmmonitor.utils.Logger; + +import java.lang.management.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class HotspotGeneralMetrics { + private static final Logger LOGGER = Logger.getLogger(HotspotGeneralMetrics.class); + private static final int KB_shift = 10; + private static ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + private static ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); + private static MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + private static CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean(); + private static RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + private static OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + + private int elapsedTime; + private int cpuConsumingTime; + private int jitCompileTime; + private int heapUsed; + private int heapFree; + private int heapSize; + private int heapCommitted; + private int nonHeapUsed; + private int nonHeapSize; + private int nonHeapCommitted; + private int survivorSpaceUsed; + private int survivorSpaceSize; + private int survivorSpaceCommitted; + private int edenSpaceUsed; + private int edenSpaceSize; + private int edenSpaceCommitted; + private int oldSpaceSize; + private int oldSpaceUsed; + private int oldSpaceCommitted; + private int metaSpaceUsed; + private int metaSpaceSize; + private int metaSpaceCommitted; + private int objectPendingFinalizationCount; + private int youngGcCount; + private int fullGcCount; + private int youngGcTimeTotal; + private int fullGcTimeTotal; + private int activeThreadCount; + private int daemonThreadCount; + private int totalThreadCount; + private int cpuUtil; + private int classCount; + + @JsonIgnore + private long longYGcTimeTotal; + @JsonIgnore + private long longFGcTimeTotal; + + static final Set YOUNG_GC = new HashSet(3); + static final Set OLD_GC = new HashSet(3); + + static { + // young generation GC names + YOUNG_GC.add("PS Scavenge"); + YOUNG_GC.add("ParNew"); + YOUNG_GC.add("G1 Young Generation"); + YOUNG_GC.add("Copy"); + + // old generation GC names + OLD_GC.add("PS MarkSweep"); + OLD_GC.add("ConcurrentMarkSweep"); + OLD_GC.add("G1 Old Generation"); + OLD_GC.add("MarkSweepCompact"); + } + + + public void collect() { + + elapsedTime = (int) (runtimeMXBean.getUptime()); + com.sun.management.ThreadMXBean tmxBean = (com.sun.management.ThreadMXBean)threadMXBean; + long totalCpuConsumingTime = 0L; + if (false && threadMXBean.isCurrentThreadCpuTimeSupported()) { + LOGGER.info("current thread cpu time suported! " + tmxBean.getCurrentThreadCpuTime()); + cpuConsumingTime = (int)(tmxBean.getCurrentThreadCpuTime() / 1000000); + } else { + long[] ids = threadMXBean.getAllThreadIds(); + long[] ct = tmxBean.getThreadCpuTime(ids); + for (int i = 0; i < ct.length; i++) { + long t = ct[i]; + if (t < 0) { + continue; + } + totalCpuConsumingTime += t; + } + cpuConsumingTime = (int) (totalCpuConsumingTime / 1000000); + } + + List memoryPoolMXBeans + = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans) { + String name = memoryPoolMXBean.getName(); + if (name.contains("Eden Space")) { + edenSpaceSize = (int) (memoryPoolMXBean.getUsage().getMax() >> KB_shift); + edenSpaceCommitted = (int) (memoryPoolMXBean.getUsage().getCommitted() >> KB_shift); + edenSpaceUsed = (int) (memoryPoolMXBean.getUsage().getUsed() >> KB_shift); + } else if (name.contains("Survivor Space")) { + survivorSpaceSize = (int) (memoryPoolMXBean.getUsage().getMax() >> KB_shift); + survivorSpaceCommitted = (int) (memoryPoolMXBean.getUsage().getCommitted() >> KB_shift); + survivorSpaceUsed = (int) (memoryPoolMXBean.getUsage().getUsed() >> KB_shift); + } else if (name.contains("Old Gen")) { + oldSpaceSize = (int) (memoryPoolMXBean.getUsage().getMax() >> KB_shift); + oldSpaceCommitted = (int) (memoryPoolMXBean.getUsage().getCommitted() >> KB_shift); + oldSpaceUsed = (int) (memoryPoolMXBean.getUsage().getUsed() >> KB_shift); + } else if (name.contains("Metaspace")) { + metaSpaceSize = (int) (memoryPoolMXBean.getUsage().getMax() >> KB_shift); + metaSpaceCommitted = (int) (memoryPoolMXBean.getUsage().getCommitted() >> KB_shift); + metaSpaceUsed = (int) (memoryPoolMXBean.getUsage().getUsed() >> KB_shift); + } + } + jitCompileTime = (int) compilationMXBean.getTotalCompilationTime(); + + MemoryUsage mu = memoryMXBean.getHeapMemoryUsage(); + heapSize = (int) (mu.getMax() >> KB_shift); + heapUsed = (int) (mu.getUsed() >> KB_shift); + heapFree = heapSize - heapUsed; + heapCommitted = (int) (mu.getCommitted() >> KB_shift); + + mu = memoryMXBean.getNonHeapMemoryUsage(); + nonHeapCommitted = (int) (mu.getCommitted() >> KB_shift); + nonHeapSize = (int) (mu.getMax() >> KB_shift); + nonHeapUsed = (int) (mu.getUsed() >> KB_shift); + + objectPendingFinalizationCount = memoryMXBean.getObjectPendingFinalizationCount(); + + List mxBeans + = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean gc : mxBeans) { + long count = gc.getCollectionCount(); + if (count >= 0) { + if (YOUNG_GC.contains(gc.getName())) { + youngGcCount = (int)count; + longYGcTimeTotal = gc.getCollectionTime(); + youngGcTimeTotal = (int)(longYGcTimeTotal); + } else if (OLD_GC.contains(gc.getName())) { + fullGcCount = (int)count; + longFGcTimeTotal = gc.getCollectionTime(); + fullGcTimeTotal = (int)(longFGcTimeTotal); + } else { + fullGcCount = (int)count; + longFGcTimeTotal = gc.getCollectionTime(); + fullGcTimeTotal = (int)(longFGcTimeTotal); + } + } + } + + activeThreadCount = threadMXBean.getThreadCount(); + daemonThreadCount = threadMXBean.getDaemonThreadCount(); + totalThreadCount = (int) threadMXBean.getTotalStartedThreadCount(); + cpuUtil = calCpuLoad(); + classCount = classLoadingMXBean.getLoadedClassCount(); + } + + private int calCpuLoad() { + com.sun.management.OperatingSystemMXBean omx = (com.sun.management.OperatingSystemMXBean)operatingSystemMXBean; + double load = 0.0; + for (int i = 0; i < 10; i++) { + load = omx.getSystemCpuLoad(); + if (load >= 0.0 && load <= 1.0) { + break; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + LOGGER.error("Problem when calculate CPU load"); + break; + } + } + return (int)(load * 100.0); + } + + public int getElapsedTime() { + return elapsedTime; + } + + public void setElapsedTime(int elapsedTime) { + this.elapsedTime = elapsedTime; + } + + public int getCpuConsumingTime() { + return cpuConsumingTime; + } + + public void setCpuConsumingTime(int cpuConsumingTime) { + this.cpuConsumingTime = cpuConsumingTime; + } + + public int getJitCompileTime() { + return jitCompileTime; + } + + public void setJitCompileTime(int jitCompileTime) { + this.jitCompileTime = jitCompileTime; + } + + public int getHeapUsed() { + return heapUsed; + } + + public void setHeapUsed(int heapUsed) { + this.heapUsed = heapUsed; + } + + public int getHeapFree() { + return heapFree; + } + + public void setHeapFree(int heapFree) { + this.heapFree = heapFree; + } + + public int getHeapSize() { + return heapSize; + } + + public void setHeapSize(int heapSize) { + this.heapSize = heapSize; + } + + public int getHeapCommitted() { + return heapCommitted; + } + + public void setHeapCommitted(int heapCommitted) { + this.heapCommitted = heapCommitted; + } + + public int getObjectPendingFinalizationCount() { + return objectPendingFinalizationCount; + } + + public void setObjectPendingFinalizationCount(int objectPendingFinalizationCount) { + this.objectPendingFinalizationCount = objectPendingFinalizationCount; + } + + public int getYoungGcCount() { + return youngGcCount; + } + + public void setYoungGcCount(int youngGcCount) { + this.youngGcCount = youngGcCount; + } + + public int getFullGcCount() { + return fullGcCount; + } + + public void setFullGcCount(int fullGcCount) { + this.fullGcCount = fullGcCount; + } + + public int getYoungGcTimeTotal() { + return youngGcTimeTotal; + } + + public void setYoungGcTimeTotal(int youngGcTimeTotal) { + this.youngGcTimeTotal = youngGcTimeTotal; + } + + public int getFullGcTimeTotal() { + return fullGcTimeTotal; + } + + public void setFullGcTimeTotal(int fullGcTimeTotal) { + this.fullGcTimeTotal = fullGcTimeTotal; + } + + public int getActiveThreadCount() { + return activeThreadCount; + } + + public void setActiveThreadCount(int activeThreadCount) { + this.activeThreadCount = activeThreadCount; + } + + public int getClassCount() { + return classCount; + } + + public void setClassCount(int classCount) { + this.classCount = classCount; + } + + public int getDaemonThreadCount() { + return daemonThreadCount; + } + + public void setDaemonThreadCount(int daemonThreadCount) { + this.daemonThreadCount = daemonThreadCount; + } + + public void setTotalThreadCount(int totalThreadCount) { + this.totalThreadCount = totalThreadCount; + } + + public int getTotalThreadCount() { + return totalThreadCount; + } + + public int getCpuUtil() { + return cpuUtil; + } + + public void setCpuUtil(int cpuUtil) { + this.cpuUtil = cpuUtil; + } + + public int getEdenSpaceCommitted() { + return edenSpaceCommitted; + } + + public void setEdenSpaceCommitted(int edenSpaceCommitted) { + this.edenSpaceCommitted = edenSpaceCommitted; + } + + public int getEdenSpaceSize() { + return edenSpaceSize; + } + + public void setEdenSpaceSize(int edenSpaceSize) { + this.edenSpaceSize = edenSpaceSize; + } + + public int getEdenSpaceUsed() { + return edenSpaceUsed; + } + + public void setEdenSpaceUsed(int edenSpaceUsed) { + this.edenSpaceUsed = edenSpaceUsed; + } + + public int getSurvivorSpaceCommitted() { + return survivorSpaceCommitted; + } + + public void setSurvivorSpaceCommitted(int survivorSpaceCommitted) { + this.survivorSpaceCommitted = survivorSpaceCommitted; + } + + public int getSurvivorSpaceSize() { + return survivorSpaceSize; + } + + public void setSurvivorSpaceSize(int survivorSpaceSize) { + this.survivorSpaceSize = survivorSpaceSize; + } + + public int getSurvivorSpaceUsed() { + return survivorSpaceUsed; + } + + public void setSurvivorSpaceUsed(int survivorSpaceUsed) { + this.survivorSpaceUsed = survivorSpaceUsed; + } + + public void setOldSpaceCommitted(int oldSpaceCommitted) { + this.oldSpaceCommitted = oldSpaceCommitted; + } + + public int getOldSpaceCommitted() { + return oldSpaceCommitted; + } + + public void setOldSpaceSize(int oldSpaceSize) { + this.oldSpaceSize = oldSpaceSize; + } + + public int getOldSpaceSize() { + return oldSpaceSize; + } + + public void setOldSpaceUsed(int oldSpaceUsed) { + this.oldSpaceUsed = oldSpaceUsed; + } + + public int getOldSpaceUsed() { + return oldSpaceUsed; + } + + public void setMetaSpaceCommitted(int metaSpaceCommitted) { + this.metaSpaceCommitted = metaSpaceCommitted; + } + + public int getMetaSpaceCommitted() { + return metaSpaceCommitted; + } + + public void setMetaSpaceSize(int metaSpaceSize) { + this.metaSpaceSize = metaSpaceSize; + } + + public int getMetaSpaceSize() { + return metaSpaceSize; + } + + public void setMetaSpaceUsed(int metaSpaceUsed) { + this.metaSpaceUsed = metaSpaceUsed; + } + + public int getMetaSpaceUsed() { + return metaSpaceUsed; + } + + public void setNonHeapCommitted(int nonHeapCommitted) { + this.nonHeapCommitted = nonHeapCommitted; + } + + public int getNonHeapCommitted() { + return nonHeapCommitted; + } + + public void setNonHeapSize(int nonHeapSize) { + this.nonHeapSize = nonHeapSize; + } + + public int getNonHeapSize() { + return nonHeapSize; + } + + public void setNonHeapUsed(int nonHeapUsed) { + this.nonHeapUsed = nonHeapUsed; + } + + public int getNonHeapUsed() { + return nonHeapUsed; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotRequestHandler.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotRequestHandler.java new file mode 100644 index 00000000..0f34c53f --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/HotspotRequestHandler.java @@ -0,0 +1,45 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.tencent.femas.tencentcloudjvmmonitor.utils.JvmMonitorHttpRequestHandler; +import com.tencent.femas.tencentcloudjvmmonitor.utils.Logger; + +import java.lang.instrument.Instrumentation; + + +public final class HotspotRequestHandler extends JvmMonitorHttpRequestHandler { + + private static final Logger LOGGER = Logger.getLogger(HotspotRequestHandler.class); + private static final int BUFFER_SIZE = 1024; + private Instrumentation inst; + + public HotspotRequestHandler(Instrumentation inst) { + this.inst = inst; + } + + public String processCommand(String requestBody) { + return HotspotCommandProcessor.processCommand(requestBody, inst); + } +} \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JTopArguments.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JTopArguments.java new file mode 100644 index 00000000..e01fd172 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JTopArguments.java @@ -0,0 +1,36 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class JTopArguments { + int topThreads; + + public int getTopThreads() { + return topThreads; + } + + public void setTopThreads(int topThreads) { + this.topThreads = topThreads; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JTopInfoEntry.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JTopInfoEntry.java new file mode 100644 index 00000000..9f94b1bb --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JTopInfoEntry.java @@ -0,0 +1,54 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class JTopInfoEntry { + private String threadName; + private String threadCPURatio; + private String threadInfos; + + public String getThreadName() { + return threadName; + } + + public void setThreadName(String threadName) { + this.threadName = threadName; + } + + public String getThreadCPURatio() { + return threadCPURatio; + } + + public void setThreadCPURatio(String threadCPURatio) { + this.threadCPURatio = threadCPURatio; + } + + public String getThreadInfos() { + return threadInfos; + } + + public void setThreadInfos(String threadInfos) { + this.threadInfos = threadInfos; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JmapInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JmapInfo.java new file mode 100644 index 00000000..0a56de05 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JmapInfo.java @@ -0,0 +1,54 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class JmapInfo { + String instances; + String bytes; + String className; + + public String getInstances() { + return instances; + } + + public void setInstances(String instances) { + this.instances = instances; + } + + public String getBytes() { + return bytes; + } + + public void setBytes(String bytes) { + this.bytes = bytes; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JmapInfoList.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JmapInfoList.java new file mode 100644 index 00000000..ade85be9 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JmapInfoList.java @@ -0,0 +1,46 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import java.util.ArrayList; + +public class JmapInfoList { + ArrayList list; + + JmapInfoList() { + list = new ArrayList(); + } + + public void add(T entry) { + list.add(entry); + } + + public ArrayList getList() { + return list; + } + + public void setList(ArrayList list) { + this.list = list; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JvmMonitorAgentEntrance.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JvmMonitorAgentEntrance.java new file mode 100644 index 00000000..4acc23a6 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JvmMonitorAgentEntrance.java @@ -0,0 +1,272 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.tencent.femas.tencentcloudjvmmonitor.utils.*; +import org.jmxtrans.agent.JmxTransAgent; + +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.util.HashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class JvmMonitorAgentEntrance { + + private static final Logger LOGGER = Logger.getLogger(JvmMonitorAgentEntrance.class); + private static final String DEFAULT_PORT = "11099"; + private static final String DEFAULT_JMXCFG_FILE = "tsf_monitor_config.xml"; + private static final String DEFAULT_BCI_ARGUMENTS = ""; + public static final String JDK_CTX = "/jvm"; + + private static String configFile = ""; // = getDefaultJmxcfgFileFromJar(DEFAULT_JMXCFG_FILE); + private static String portNum = DEFAULT_PORT; + private static boolean enableDiagAgent = false; + private static boolean enableMonitorAgent = true; + private static String bciArguments = DEFAULT_BCI_ARGUMENTS; + private static boolean hasController = false; + private static ControllerType controllerType = ControllerType.HTTP; + private static RpcServer controllerServer; + private static volatile boolean agentLoaded = false; + public static HashMap countTable; + + enum ControllerType { + SOCKET, + HTTP + } + + public static String getDefaultJmxcfgFileFromJar(String cfg, String pathToAdd) { + String cfgFile; + LOGGER.debug("try to load default jmx cfg file: " + DEFAULT_JMXCFG_FILE); + try { + if (pathToAdd == null || pathToAdd.length() == 0) { + cfgFile = JvmMonitorUtils.getFileFromJar(cfg); + } else { + cfgFile = JvmMonitorUtils.getUpdatedFileFromJar(cfg, pathToAdd); + + } + } catch (Exception e) { + LOGGER.error("Fail load default performance monitoring config file:\n"); + e.printStackTrace(); + cfgFile = ""; + } + return cfgFile; + } + + private static void useDefaultOptions() { + + configFile = getDefaultJmxcfgFileFromJar(DEFAULT_JMXCFG_FILE, JvmMonitorUtils.getDataSavePath()); + LOGGER.debug("refreshed configFIle is " + configFile); + portNum = DEFAULT_PORT; + enableDiagAgent = false; + bciArguments = DEFAULT_BCI_ARGUMENTS; + hasController = false; + controllerType = ControllerType.HTTP; + } + + private static boolean parseBool(String string, String key) { + if (string.equalsIgnoreCase("true")) { + return true; + } else if (!string.equalsIgnoreCase("false")) { + LOGGER.warn("Invalid bool value for " + key + ", set to false"); + } + return false; + } + + + + private static boolean verifyArguments() { + if (((portNum == null || portNum.length() == 0) && (hasController == true)) + || configFile == null || configFile.length() == 0) { + LOGGER.error("invalid argument, disable Monitor and Diagnostic mode"); + enableMonitorAgent = false; + enableDiagAgent = false; + return false; + } + if (enableMonitorAgent == false && enableDiagAgent == false) { + LOGGER.error("must enable Monitor or Diagnostic mode"); + return false; + } + return true; + } + + private static void setDefaultArguments() { + // by default hasController is false. if set to true. + if (hasController) { + if (controllerType == null) { + controllerType = ControllerType.HTTP; + LOGGER.warn("use default controller type: " + controllerType); + } + if (portNum == null || portNum.length() == 0) { + if (controllerType == ControllerType.HTTP || controllerType == ControllerType.SOCKET) { + portNum = DEFAULT_PORT; + LOGGER.warn("use default controller port: " + portNum); + } + } + } else { + // No controller, monitor mode only. + enableMonitorAgent = true; + enableDiagAgent = false; + } + } + + private static void postProcessArguments() { + finalizeJmxConfigFile(); + setDefaultArguments(); + verifyArguments(); + } + + private static void finalizeJmxConfigFile() { + // user defined configFile. + LOGGER.debug("finalize JMXConfigFile: " + configFile); + if (configFile == null || configFile.length() == 0) { + // only consider dataFile if it is TSFFileOutputWritter + configFile = getDefaultJmxcfgFileFromJar(DEFAULT_JMXCFG_FILE, JvmMonitorUtils.getDataSavePath()); + } + LOGGER.debug("Use config File at: " + configFile); + + } + + private static boolean fillOption(String kvPair) { + String[] values = kvPair.split("="); + if (values.length != 2) { + LOGGER.error("Invalid Arguments pair: " + kvPair); + return false; + } + String key = values[0]; + String val = values[1]; + LOGGER.debug("process options: " + key + " = " + val); + if (key.equalsIgnoreCase("jmxConf")) { + configFile = val; + } else if (key.equalsIgnoreCase("portNum")) { + portNum = val; + } else if (key.equalsIgnoreCase("enableDiagnosticAgent")) { + enableDiagAgent = parseBool(val, key); + } else if (key.equalsIgnoreCase("enableMonitorAgent")) { + enableMonitorAgent = parseBool(val, key); + } else if (key.equalsIgnoreCase("hasController")) { + hasController = parseBool(val, key); + } else if (key.equalsIgnoreCase("controllerType")) { + if (val.equalsIgnoreCase("HTTP")) { + controllerType = ControllerType.HTTP; + } else if (val.equalsIgnoreCase(("SOCKET"))) { + controllerType = ControllerType.SOCKET; + } else { + LOGGER.error("Invalid Arguments pair: " + kvPair); + return false; + } + } else if (key.equalsIgnoreCase("dataSavePath")) { + JvmMonitorUtils.setDataSavePath(val); + } else { + LOGGER.error("Invalid Arguments pair: " + kvPair); + return false; + } + return true; + } + + private static void processArguments(String options) { + if (options == null || options.length() == 0) { + useDefaultOptions(); + return; + } + String[] opts = options.split(";"); + try { + for (String kvPair : opts) { + // TODO. warning if fillOption returns false? + fillOption(kvPair); + } + postProcessArguments(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + + public static RpcServer createControllerServer(ControllerType type, Instrumentation inst) { + if (type == ControllerType.SOCKET) { + return RpcSocketServer.getInstance(portNum, inst); + } else if (type == ControllerType.HTTP) { + RpcServer instance = + HttpSocketServer.getInstance(portNum, new HotspotRequestHandler(inst)); + return instance; + } + LOGGER.error("Unsupported controller communication type: " + type.name()); + return null; + } + +// public static void profilerAgentMain(String options, Instrumentation inst) throws Exception { +// JvmMonitorBCIAgent.agentmain(options, inst); +// } + + public static void main(String options, Instrumentation inst) { + if (agentLoaded) { + LOGGER.warn("JvmMonitorAgent already loaded"); + return; + } + agentLoaded = true; + LOGGER.debug("Start parsing options: " + options); + processArguments(options); + + if (!JvmMonitorUtils.createDataPath()) { + LOGGER.error("Fail create folder " + JvmMonitorUtils.getDataSavePath() + "disable JvmMonitorAgent"); + return; + } + + // Monitoring has no relationship with hasController. + if (enableMonitorAgent) { + LOGGER.info("Start jmxtrans with config file " + configFile); + JmxTransAgent.premain(configFile, inst); + } + + if (hasController) { + controllerServer = createControllerServer(controllerType, inst); + if (controllerServer == null) { + LOGGER.error("Fail create RPC Controller Server: " + controllerType.name() + + ". will work in monitor Only mode"); + enableDiagAgent = false; + } else { + LOGGER.info("Start Controller Server: " + controllerType.name()); + LOGGER.debug("Controller port: " + portNum); + } + } + + countTable = new HashMap(); + + ExecutorService exec = Executors.newSingleThreadExecutor(new DaemonThreadFactory()); + exec.execute(new Runnable() { + @Override + public void run() { + if (controllerServer != null) { + try { + controllerServer.start(JDK_CTX); + } catch (IOException e) { + LOGGER.error("Error processing RPC Controller"); + e.printStackTrace(); + } + } + } + }); + } + + +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JvmMonitorClassloader.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JvmMonitorClassloader.java new file mode 100644 index 00000000..f580dd88 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/JvmMonitorClassloader.java @@ -0,0 +1,69 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.tencent.femas.tencentcloudjvmmonitor.utils.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; + +public class JvmMonitorClassloader extends URLClassLoader { + private static final Logger LOGGER = Logger.getLogger(JvmMonitorClassloader.class); + // only enable at develop phase. + private static final Boolean DUMP_LOADED_CLASSES = false; + + public JvmMonitorClassloader(URL[] urls) { + super(urls); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + byte[] b = loadClassData(name); + return defineClass(name, b, 0, b.length); + } + + private byte[] loadClassData(String className) { + if (DUMP_LOADED_CLASSES) { + LOGGER.debug("load class: " + className.replace(".", "/") + ".class"); + } + // read class + InputStream is = getClass().getClassLoader().getResourceAsStream( + className.replace(".", "/") + ".class"); + ByteArrayOutputStream byteSt = new ByteArrayOutputStream(); + // write into byte + int len = 0; + try { + while ((len = is.read()) != -1) { + byteSt.write(len); + } + } catch (IOException e) { + e.printStackTrace(); + } + // convert into byte array + return byteSt.toByteArray(); + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/KillPortServer.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/KillPortServer.java new file mode 100644 index 00000000..5db8a309 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/KillPortServer.java @@ -0,0 +1,154 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class KillPortServer { + private Set ports; + + public void start(int port) { + Runtime runtime = Runtime.getRuntime(); + try { + //查找进程号 + Process p = runtime.exec("cmd /c netstat -ano | findstr \"" + port + "\""); + InputStream inputStream = p.getInputStream(); + List read = read(inputStream, "UTF-8"); + if (read.size() == 0) { + try { + Thread.sleep(6000); + System.exit(0); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + for (String string : read) { + System.out.println(string); + } + kill(read); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 验证此行是否为指定的端口,因为 findstr命令会是把包含的找出来,例如查找80端口,但是会把8099查找出来 + * @param str + * @return + */ + private boolean validPort(String str) { + Pattern pattern = Pattern.compile("^ *[a-zA-Z]+ +\\S+"); + Matcher matcher = pattern.matcher(str); + + matcher.find(); + String find = matcher.group(); + int spstart = find.lastIndexOf(":"); + find = find.substring(spstart + 1); + + int port = 0; + try { + port = Integer.parseInt(find); + } catch (NumberFormatException e) { + return false; + } + if (this.ports.contains(port)) { + return true; + } else { + return false; + } + } + + /** + * 更换为一个Set,去掉重复的pid值 + * @param data + */ + public void kill(List data) { + Set pids = new HashSet<>(); + for (String line : data) { + int offset = line.lastIndexOf(" "); + String spid = line.substring(offset); + spid = spid.replaceAll(" ", ""); + int pid = 0; + try { + pid = Integer.parseInt(spid); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + pids.add(pid); + } + killWithPid(pids); + } + + /** + * 一次性杀除所有的端口 + * @param pids + */ + public void killWithPid(Set pids) { + for (Integer pid : pids) { + try { + Process process = Runtime.getRuntime().exec("taskkill /F /pid " + pid + ""); + InputStream inputStream = process.getInputStream(); + String txt = readTxt(inputStream, "GBK"); + System.out.println(txt); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private List read(InputStream in,String charset) throws IOException { + List data = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset)); + String line; + while ((line = reader.readLine()) != null) { + boolean validPort = validPort(line); + if (validPort) { + data.add(line); + } + } + reader.close(); + return data; + } + + public String readTxt(InputStream in,String charset) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset)); + StringBuffer sb = new StringBuffer(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + return sb.toString(); + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/MethodDistData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/MethodDistData.java new file mode 100644 index 00000000..da349239 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/MethodDistData.java @@ -0,0 +1,54 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class MethodDistData { + private String methodId; + private String methodName; + private int count; + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public String getMethodId() { + return methodId; + } + + public void setMethodId(String methodId) { + this.methodId = methodId; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/MethodProfileMetaInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/MethodProfileMetaInfo.java new file mode 100644 index 00000000..2c090cc0 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/MethodProfileMetaInfo.java @@ -0,0 +1,38 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class MethodProfileMetaInfo { + // how many times for method call profile {0 - 2^32) + String profileCount; + String args; + + public String getArgs() { + return args; + } + + public String getProfileCount() { + return profileCount; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/NativeStatData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/NativeStatData.java new file mode 100644 index 00000000..e1684f57 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/NativeStatData.java @@ -0,0 +1,45 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class NativeStatData { + private int javaThreadCount; + private int nonJavaCount; + + public int getJavaThreadCount() { + return javaThreadCount; + } + + public void setJavaThreadCount(int javaThreadCount) { + this.javaThreadCount = javaThreadCount; + } + + public int getNonJavaCount() { + return nonJavaCount; + } + + public void setNonJavaCount(int nonJavaCount) { + this.nonJavaCount = nonJavaCount; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/PerThreadDrawData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/PerThreadDrawData.java new file mode 100644 index 00000000..40ca4173 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/PerThreadDrawData.java @@ -0,0 +1,75 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class PerThreadDrawData { + private String threadName; + private Thread.State threadState; + private boolean isNative; + private String methodInfo; + + public String getTraceInfo() { + return traceInfo; + } + + public void setTraceInfo(String traceInfo) { + this.traceInfo = traceInfo; + } + + private String traceInfo; + + public String getThreadName() { + return threadName; + } + + public void setThreadName(String threadName) { + this.threadName = threadName; + } + + public Thread.State getThreadState() { + return threadState; + } + + public void setThreadState(Thread.State threadState) { + this.threadState = threadState; + } + + public boolean isNative() { + return isNative; + } + + public void setNative(boolean aNative) { + isNative = aNative; + } + + public String getMethodInfo() { + return methodInfo; + } + + public void setMethodInfo(String methodInfo) { + this.methodInfo = methodInfo; + } + + +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/TencentJvmMonitorAgent.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/TencentJvmMonitorAgent.java new file mode 100644 index 00000000..53d1c738 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/TencentJvmMonitorAgent.java @@ -0,0 +1,308 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import com.tencent.femas.tencentcloudjvmmonitor.utils.Logger; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.List; + +public class TencentJvmMonitorAgent { + private static final String QOCO_CLASS_NAME = + "com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent.JvmMonitorAgentEntrance"; + private static final String QOCO_MAIN_METHOD = "main"; + private static final String QOCO_PROFILERAGENTMAIN_METHOD = "profilerAgentMain"; + private static final Logger LOGGER = Logger.getLogger(TencentJvmMonitorAgent.class); + // must be same as DEFAULT_PORT in JvmMonitorAgentEntrance, but we dont want to load it. + private static final String DEFAULT_PORT = "11099"; + // must be same as JDK_CTX in JvmMonitorAgentEntrance + public static final String JDK_CTX = "/jvm"; + private static boolean alreadyLoaded = false; + private static JvmMonitorClassloader qocoLoader = null; + private static Class qClass = null; + + static { + try { + LOGGER.debug("class path: " + System.getProperty("java.class.path") + + " Loader: " + TencentJvmMonitorAgent.class.getClassLoader()); + String classPath = System.getProperty("java.class.path"); + String[] cpArr = classPath.split(":"); + int len = cpArr.length; + URL[] urls = new URL[len]; + for (int i = 0; i < len; i++) { + urls[i] = Paths.get(cpArr[i]).toUri().toURL(); + } + qocoLoader = new JvmMonitorClassloader(urls); + // Thread.currentThread().setContextClassLoader(qocoLoader); + qClass = qocoLoader.loadClass(QOCO_CLASS_NAME); + } catch (ClassNotFoundException e) { + LOGGER.error("Fail load class " + QOCO_CLASS_NAME); + e.printStackTrace(); + } catch (MalformedURLException e) { + LOGGER.error("Fail create URL for method " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } + } + + public static JvmMonitorClassloader getQocoLoader() { + return qocoLoader; + } + + public static void premain(String options, Instrumentation inst) { + //main(options, inst); + if (alreadyLoaded != true) { + alreadyLoaded = true; + } else { + return; + } + + LOGGER.debug("premain options: " + options); + String version = TencentJvmMonitorAgent.class.getPackage().getImplementationVersion(); + LOGGER.info("Agent VERSION: " + version); + String portNum = extractPortNum(options); + LOGGER.debug("testing JvmMonitoring port " + portNum); + + // For compatibility. + if (maybeInUse(portNum)) { + LOGGER.warn("JvmMonitor port (" + portNum + ") already in use, caused by duplication of JVM monitor agent"); + return; + } + + try { + Method mainMethod = qClass.getMethod(QOCO_MAIN_METHOD, String.class, Instrumentation.class); + mainMethod.invoke(null, options, inst); + } catch (NoSuchMethodException e) { + LOGGER.error("Fail find method " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } catch (IllegalAccessException e) { + LOGGER.error("Fail invoke method " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } catch (InvocationTargetException e) { + LOGGER.error("Fail invethod " + QOCO_MAIN_METHOD); + e.printStackTrace(); + } + } + + private static String extractPortNum(String options) { + if (options == null || options.length() == 0) { + return DEFAULT_PORT; + } + String[] opts = options.split(";"); + + for (String kvPair : opts) { + // TODO. warning if fillOption returns false? + if (kvPair.startsWith("portNum=")) { + String[] kv = kvPair.split("="); + if (kv.length != 2) { + return DEFAULT_PORT; + } else { + return kv[1]; + } + } + } + return DEFAULT_PORT; + } + + + // TODO - implement attach? + public static void agentmain(String options, Instrumentation inst) { + AgentMainOptions mainOptions = new AgentMainOptions(options); + if (mainOptions.activateJvmMonitor) { + // TODO: implement the start/stop of jvm monitor + premain(options, inst); + } + if (mainOptions.activateJvmProfiler) { + try { + Method profilerAgentMain = qClass.getMethod(QOCO_PROFILERAGENTMAIN_METHOD); + profilerAgentMain.setAccessible(true); + + profilerAgentMain.invoke(null, mainOptions.methodsToBeTraced, inst); + } catch (IllegalAccessException e) { + LOGGER.error("Fail invoke method " + QOCO_PROFILERAGENTMAIN_METHOD); + e.printStackTrace(); + } catch (InvocationTargetException e) { + LOGGER.error("Fail invoke method " + QOCO_PROFILERAGENTMAIN_METHOD); + e.printStackTrace(); + } catch (NoSuchMethodException e) { + LOGGER.error("Can not method " + QOCO_PROFILERAGENTMAIN_METHOD); + e.printStackTrace(); + } + } + } + + private static boolean maybeInUse(String portNum) { + // Test whether TencentCloudJvmMonitor exist more than once. + // if yes, testing port... + if (dupInArguments("TencentCloudJvmMonitor")) { + LOGGER.warn("multiple TecnentCloudJvmMonitor agent found, processing... it may take several seconds"); + // Test whether port is in use. + if (portInUse(portNum)) { + LOGGER.warn("jvm monitor port (" + portNum + ") already in Use, duplicated JvmMonitor agent?"); + return true; + } + } + return false; + } + + // following code not work because of class loading + private static boolean dupInArguments(String agentName) { + RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); + List args = bean.getInputArguments(); + Iterator iter = args.iterator(); + boolean dup = false; + boolean exist = false; + while (iter.hasNext()) { + String arg = (String) iter.next(); + LOGGER.debug("Argumnets: " + arg); + if (arg.contains(agentName)) { + if (exist) { + LOGGER.warn("Found dupilicated JvmMonitor agents"); + dup = true; + } else { + exist = true; + } + } + } + return dup; + } + + private static boolean portInUse(String port) { + //testing by send command to self + String url = "http://localhost:" + port + JDK_CTX; + String cmdString = "{\"taskId\":\"Qoco_getPid\",\"type\":\"getpid\",\"action\":\"\",\"metaInfo\":\"\"}"; + CloseableHttpClient client = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + try { + StringEntity entity = new StringEntity(cmdString); + httpPost.setEntity(entity); + CloseableHttpResponse response = client.execute(httpPost); + if (response.getStatusLine().getStatusCode() == 200) { + return true; + } else { + LOGGER.debug("test port response no-OK code: " + response.getStatusLine().getStatusCode()); + return false; + } + } catch (Exception e) { + LOGGER.debug(url + " port is not used: exception " + e); + return false; + } finally { + try { + client.close(); + } catch (IOException e) { + LOGGER.warn("http client for port testing can not be closed safely"); + } + } + } + + private static class AgentMainOptions { + private boolean activateJvmMonitor; + private boolean activateJvmProfiler; + private String methodsToBeTraced; + + public AgentMainOptions(String options) { + // JvmMonitor=on|off + reset(); + processOptions(options); + } + + private void processOptions(String options) { + if (options == null || options.length() == 0) { + return; + } + String[] opts = options.split(";"); + + for (String kvPair : opts) { + if (kvPair.startsWith("JvmMonitor=")) { + String[] kv = kvPair.split("="); + if (kv.length != 2) { + reset(); + return; + } else { + if (kv[1].equalsIgnoreCase("on")) { + activateJvmMonitor = true; + } else if (kv[1].equalsIgnoreCase("off")) { + activateJvmMonitor = false; + } + } + } else if (kvPair.startsWith("JvmProfiler=")) { + String[] kv = kvPair.split("="); + if (kv.length != 2) { + reset(); + return; + } else { + if (kv[1].equalsIgnoreCase("on")) { + activateJvmProfiler = true; + } else if (kv[1].equalsIgnoreCase("off")) { + activateJvmProfiler = false; + } else { + LOGGER.error("Invalid option for JvmProfiler= " + kv[1]); + reset(); + return; + } + } + } else if (kvPair.startsWith("TraceMethods=")) { + String[] kv = kvPair.split("="); + if (kv.length != 2) { + reset(); + return; + } else { + methodsToBeTraced = kv[1]; + if (methodsToBeTraced == null || methodsToBeTraced.length() == 0) { + LOGGER.error("No methods specified for TraceMethod="); + reset(); + return; + } + } + } + } + } + + private void reset() { + activateJvmMonitor = true; + activateJvmProfiler = false; + methodsToBeTraced = null; + } + + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadGroupData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadGroupData.java new file mode 100644 index 00000000..f1bfee9f --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadGroupData.java @@ -0,0 +1,45 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class ThreadGroupData { + private String groupName; + private int count; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoArguments.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoArguments.java new file mode 100644 index 00000000..394eaf15 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoArguments.java @@ -0,0 +1,45 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class ThreadInfoArguments { + boolean compression; + boolean drawdata; + + public boolean isCompression() { + return compression; + } + + public void setCompression(boolean compression) { + this.compression = compression; + } + + public boolean isDrawdata() { + return drawdata; + } + + public void setDrawdata(boolean drawdata) { + this.drawdata = drawdata; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoDrawData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoDrawData.java new file mode 100644 index 00000000..1584c893 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoDrawData.java @@ -0,0 +1,183 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import java.util.ArrayList; + +public class ThreadInfoDrawData { + private ThreadStateData threadStateData; + private ArrayList threadGroupDataList; + private int totalThreadGroupCount; + private NativeStatData nativeThreadCountData; + private int totalTraceGroupCount; + private ArrayList threadTraceGroupDataList; + private int totalMethodDistCount; + private ArrayList methodDistDataList; + + public ThreadInfoDrawData() { + threadStateData = new ThreadStateData(); + threadGroupDataList = new ArrayList<>(); + nativeThreadCountData = new NativeStatData(); + threadTraceGroupDataList = new ArrayList<>(); + methodDistDataList = new ArrayList<>(); + + totalThreadGroupCount = 0; + totalTraceGroupCount = 0; + totalMethodDistCount = 0; + } + + public int getTotalTraceGroupCount() { + return totalTraceGroupCount; + } + + public void setTotalTraceGroupCount(int totalTraceGroupCount) { + this.totalTraceGroupCount = totalTraceGroupCount; + } + + public int getTotalMethodDistCount() { + return totalMethodDistCount; + } + + public void setTotalMethodDistCount(int totalMethodDistCount) { + this.totalMethodDistCount = totalMethodDistCount; + } + + public int getTotalThreadGroupCount() { + return totalThreadGroupCount; + } + + public void setTotalThreadGroupCount(int totalThreadGroupCount) { + this.totalThreadGroupCount = totalThreadGroupCount; + } + + public ThreadStateData getThreadStateData() { + return threadStateData; + } + + public void setThreadStateData(ThreadStateData threadStateData) { + this.threadStateData = threadStateData; + } + + public ArrayList getThreadGroupDataList() { + return threadGroupDataList; + } + + public void setThreadGroupDataList(ArrayList threadGroupDataList) { + this.threadGroupDataList = threadGroupDataList; + } + + public NativeStatData getNativeThreadCountData() { + return nativeThreadCountData; + } + + public void setNativeThreadCountData(NativeStatData nativeThreadCountData) { + this.nativeThreadCountData = nativeThreadCountData; + } + + public ArrayList getThreadTraceGroupDataList() { + return threadTraceGroupDataList; + } + + public void setThreadTraceGroupDataList(ArrayList threadTraceGroupDataList) { + this.threadTraceGroupDataList = threadTraceGroupDataList; + } + + public ArrayList getMethodDistDataList() { + return methodDistDataList; + } + + public void setMethodDistDataList(ArrayList methodDistDataList) { + this.methodDistDataList = methodDistDataList; + } + + /* + * update Data from perThreadDrawData. + */ + public void update(PerThreadDrawData perThreadDrawData) { + String threadName = perThreadDrawData.getThreadName(); + updateThreadGroupDataList(threadName); + + Thread.State state = perThreadDrawData.getThreadState(); + threadStateData.updateCount(state); + + String methodInfo = perThreadDrawData.getMethodInfo(); + updateMethodDistData(methodInfo); + + String traceInfo = perThreadDrawData.getTraceInfo(); + updateThreadTraceGroupDataList(traceInfo); + + if (perThreadDrawData.isNative()) { + nativeThreadCountData.setNonJavaCount(nativeThreadCountData.getNonJavaCount() + 1); + } else { + nativeThreadCountData.setJavaThreadCount(nativeThreadCountData.getJavaThreadCount() + 1); + } + } + + private void updateMethodDistData(String methodInfo) { + for (MethodDistData entry : methodDistDataList) { + if (methodInfo.equals(entry.getMethodName())) { + entry.setCount(entry.getCount() + 1); + return; + } + } + + MethodDistData data = new MethodDistData(); + data.setMethodName(methodInfo); + data.setCount(1); + data.setMethodId("method_" + totalMethodDistCount); + totalMethodDistCount++; + methodDistDataList.add(data); + } + + private void updateThreadTraceGroupDataList(String methodInfo) { + for (ThreadTraceGroupData entry : threadTraceGroupDataList) { + if (methodInfo.equals(entry.getTraceString())) { + entry.setCount(entry.getCount() + 1); + return; + } + } + + ThreadTraceGroupData data = new ThreadTraceGroupData(); + data.setTraceString(methodInfo); + data.setCount(1); + data.setTraceId("stackTrace_" + totalTraceGroupCount); + totalTraceGroupCount++; + threadTraceGroupDataList.add(data); + } + + private void updateThreadGroupDataList(String threadName) { + String name = threadName.replaceAll("\\d*$", ""); + for (ThreadGroupData entry : threadGroupDataList) { + if (name.equals(entry.getGroupName())) { + entry.setCount(entry.getCount() + 1); + return; + } + } + ThreadGroupData data = new ThreadGroupData(); + data.setGroupName(name); + data.setCount(1); + threadGroupDataList.add(data); + totalThreadGroupCount++; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoEntry.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoEntry.java new file mode 100644 index 00000000..33924295 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoEntry.java @@ -0,0 +1,92 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class ThreadInfoEntry { + private String threadName; + private String threadState; + private String threadCpuUtil; + private String threadAllocatedBytes; + private String threadCpuTime; + private String threadBlockCount; + private String threadInfos; + + + public String getThreadName() { + return threadName; + } + + public void setThreadName(String threadName) { + this.threadName = threadName; + } + + public String getThreadState() { + return threadState; + } + + public void setThreadState(String threadState) { + this.threadState = threadState; + } + + public String getThreadInfos() { + return threadInfos; + } + + public void setThreadInfos(String threadInfos) { + this.threadInfos = threadInfos; + } + + public String getThreadCpuUtil() { + return threadCpuUtil; + } + + public void setThreadCpuUtil(String threadCpuUtil) { + this.threadCpuUtil = threadCpuUtil; + } + + public String getThreadAllocatedBytes() { + return threadAllocatedBytes; + } + + public void setThreadAllocatedBytes(String threadAllocatedBytes) { + this.threadAllocatedBytes = threadAllocatedBytes; + } + + public String getThreadCpuTime() { + return threadCpuTime; + } + + public void setThreadCpuTime(String threadCpuTime) { + this.threadCpuTime = threadCpuTime; + } + + public String getThreadBlockCount() { + return threadBlockCount; + } + + public void setThreadBlockCount(String threadBlockCount) { + this.threadBlockCount = threadBlockCount; + } + +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoEntryAndDrawData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoEntryAndDrawData.java new file mode 100644 index 00000000..e50f46ba --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoEntryAndDrawData.java @@ -0,0 +1,46 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class ThreadInfoEntryAndDrawData { + + private ThreadInfoEntry threadInfoEntry; + private ThreadInfoDrawData threadInfoDrawData; + + public ThreadInfoEntry getThreadInfoEntry() { + return threadInfoEntry; + } + + public void setThreadInfoEntry(ThreadInfoEntry threadInfoEntry) { + this.threadInfoEntry = threadInfoEntry; + } + + public ThreadInfoDrawData getThreadInfoDrawData() { + return threadInfoDrawData; + } + + public void setThreadInfoDrawData(ThreadInfoDrawData threadInfoDrawData) { + this.threadInfoDrawData = threadInfoDrawData; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoList.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoList.java new file mode 100644 index 00000000..b51c575b --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadInfoList.java @@ -0,0 +1,47 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +import java.util.ArrayList; + +public class ThreadInfoList { + + ArrayList list; + + ThreadInfoList() { + list = new ArrayList(); + } + + public void add(T entry) { + list.add(entry); + } + + public ArrayList getList() { + return list; + } + + public void setList(ArrayList list) { + this.list = list; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadStateData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadStateData.java new file mode 100644 index 00000000..0e7ca9ad --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadStateData.java @@ -0,0 +1,115 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class ThreadStateData { + private int totalCount; + private int newCount; + private int runnableCount; + private int blockedCount; + private int waitingCount; + private int timedWaitingCount; + private int terminatedCount; + + public int getTotalCount() { + return totalCount; + } + + public void setTotalCount(int totalCount) { + this.totalCount = totalCount; + } + + public int getNewCount() { + return newCount; + } + + public void setNewCount(int newCount) { + this.newCount = newCount; + } + + public int getRunnableCount() { + return runnableCount; + } + + public void setRunnableCount(int runnableCount) { + this.runnableCount = runnableCount; + } + + public int getBlockedCount() { + return blockedCount; + } + + public void setBlockedCount(int blockedCount) { + this.blockedCount = blockedCount; + } + + public int getWaitingCount() { + return waitingCount; + } + + public void setWaitingCount(int waitingCount) { + this.waitingCount = waitingCount; + } + + public int getTimedWaitingCount() { + return timedWaitingCount; + } + + public void setTimedWaitingCount(int timedWaitingCount) { + this.timedWaitingCount = timedWaitingCount; + } + + public int getTerminatedCount() { + return terminatedCount; + } + + public void setTerminatedCount(int terminatedCount) { + this.terminatedCount = terminatedCount; + } + + public void updateCount(Thread.State state) { + switch (state) { + case NEW: + newCount++; + break; + case BLOCKED: + blockedCount++; + break; + case WAITING: + waitingCount++; + break; + case TIMED_WAITING: + timedWaitingCount++; + break; + case TERMINATED: + terminatedCount++; + break; + case RUNNABLE: + runnableCount++; + break; + default: + break; + } + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadTraceGroupData.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadTraceGroupData.java new file mode 100644 index 00000000..b025568d --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/ThreadTraceGroupData.java @@ -0,0 +1,54 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class ThreadTraceGroupData { + private String traceId; + private String traceString; + private int count; + + public String getTraceString() { + return traceString; + } + + public void setTraceString(String traceString) { + this.traceString = traceString; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public String getTraceId() { + return traceId; + } + + public void setTraceId(String traceId) { + this.traceId = traceId; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/TimePair.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/TimePair.java new file mode 100644 index 00000000..a5131afc --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/jvmmonitoragent/TimePair.java @@ -0,0 +1,50 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.jvmmonitoragent; + +public class TimePair { + private long lastThreadCpuTime; + private long lastNanoTime; + + public TimePair(long threadCpuTime, long now) { + lastThreadCpuTime = threadCpuTime; + lastNanoTime = now; + } + + public long getLastCpuTime() { + return lastThreadCpuTime; + } + + public void setLastCpuTime(long lastCpuTime) { + this.lastThreadCpuTime = lastCpuTime; + } + + public long getLastNanoTime() { + return lastNanoTime; + } + + public void setLastNanoTime(long lastNanoTime) { + this.lastNanoTime = lastNanoTime; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Command.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Command.java new file mode 100644 index 00000000..6dc61fe1 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Command.java @@ -0,0 +1,88 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public class Command { + private String taskId; + private String type; // see HotspotCommandProcessor.processCommandInternal + private String action; + private String metaInfo; // could be map + + public Command() { + this("","","",""); + } + + public Command(String taskID, String type, String action) { + this(taskID, type, action, ""); + } + + public Command(String taskID, String type) { + this(taskID, type, "", ""); + } + + public Command(String taskID, String type, String action, String mInfo) { + this.taskId = taskID; + this.type = type; + this.action = action; + this.metaInfo = mInfo; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getMetaInfo() { + return metaInfo; + } + + public void setMetaInfo(String metaInfo) { + this.metaInfo = metaInfo; + } + + @Override + public String toString() { + return "Command{" + "taskId='" + taskId + '\'' + ", type='" + type + '\'' + ", action='" + action + '\'' + + ", metaInfo='" + metaInfo + '\'' + '}'; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/DaemonThreadFactory.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/DaemonThreadFactory.java new file mode 100644 index 00000000..10872826 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/DaemonThreadFactory.java @@ -0,0 +1,38 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ThreadFactory; + +public class DaemonThreadFactory implements ThreadFactory { + @Override + public Thread newThread(@NotNull Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + t.setContextClassLoader(this.getClass().getClassLoader()); + return t; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Decompressor.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Decompressor.java new file mode 100644 index 00000000..c23c13de --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Decompressor.java @@ -0,0 +1,135 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import org.apache.commons.codec.binary.Base64; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.GZIPInputStream; + +public class Decompressor { + private static final Logger LOGGER = Logger.getLogger(Decompressor.class); + + /* + * Decompress the given file. + */ + public static void main(String[] args) { + Path path = Paths.get(args[1]); + + byte[] bytes = null; + String raw = null; + try { + bytes = Files.readAllBytes(path); + raw = uncompress(new String(bytes, "8859_1")); + } catch (IOException e) { + // Handle exception + e.printStackTrace(); + } + if (raw != null) { + writeStringToFile(new File("output.txt"), raw); + } + } + + public static String uncompress(String compressedStr) { + if (compressedStr == null) { + return null; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = null; + GZIPInputStream ginzip = null; + byte[] compressed = null; + String decompressed = null; + try { + Base64 base64decoder = new Base64(); + compressed = base64decoder.decodeBase64(compressedStr); + in = new ByteArrayInputStream(compressed); + LOGGER.debug("decoded data: " + new String(compressed)); + ginzip = new GZIPInputStream(in); + + byte[] buffer = new byte[1024]; + int offset = -1; + while ((offset = ginzip.read(buffer)) != -1) { + out.write(buffer, 0, offset); + } + decompressed = out.toString(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (ginzip != null) { + try { + ginzip.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return decompressed; + } + + /* + * Write string to file + */ + public static boolean writeStringToFile(File file, String string) { + FileOutputStream fos = null; + try { + if (!file.exists()) { + file.createNewFile(); + } + fos = new FileOutputStream(file); + byte[] bytesArray = string.getBytes(); + fos.write(bytesArray); + fos.flush(); + } catch (IOException e) { + + e.printStackTrace(); + return false; + } finally { + try { + if (fos != null) { + fos.close(); + } + } catch (IOException e) { + + return false; + } + } + return true; + } + +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/FlameGraphUtil.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/FlameGraphUtil.java new file mode 100644 index 00000000..f130b345 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/FlameGraphUtil.java @@ -0,0 +1,420 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * 火焰图数据解析工具类 + * + * @author v_lquanlin + */ +public class FlameGraphUtil { + private static final Logger LOGGER = Logger.getLogger(FlameGraphUtil.class); + private static ObjectMapper mapper = new ObjectMapper(); + + public static String parseJsonFromString(String rawString) throws IOException { + if (rawString == null || rawString.length() == 0) { + return null; + } + + long b; + b = System.currentTimeMillis(); + List metadata = new ArrayList<>(); + + Reader inputString = new StringReader(rawString); + BufferedReader bufferedReader = new BufferedReader(inputString); + + + String l; + + while ((l = bufferedReader.readLine()) != null) { + //LOGGER.debug("read line from file: " + l); + metadata.add(l); + } + + LOGGER.debug("metaData size is: " + metadata.size()); + + FlameGraphNode rootNode = new FlameGraphNode(); + rootNode.setName("root"); + + int rootValue = 0; + int maxTier = 0; + //int objectCount = 0; //TODO 记录产生的对象数量 可删除 + List> nodeChainList = new ArrayList<>(); + + for (int j = 0; j < metadata.size(); j++) { + String line = metadata.get(j); + int num = Integer.valueOf(line.substring(line.lastIndexOf(" ") + 1)); + line = line.substring(0, line.lastIndexOf(" ")); + String[] elements = line.split("\\|"); + + LinkedList nodeChain = new LinkedList<>(); + nodeChainList.add(nodeChain); + for (int i = 1; i <= elements.length; i++) { + FlameGraphNode node = new FlameGraphNode(elements[i - 1],num,metadata.size() - j,metadata.size() - j); + nodeChain.add(node); + + + //objectCount++; //TODO 记录产生的对象数量 可删除 + if (i == 1) { + node.setPreName(rootNode.getName()); + } else { + node.setPreName(elements[i - 2]); + } + + } + if (maxTier < elements.length) { + maxTier = elements.length; + } + rootValue += num; + } + + List> tierNodesList = new ArrayList<>(); + for (int i = 0; i < maxTier; i++) { + List thisList = new ArrayList<>(); + //objectCount++; //TODO 记录产生的对象数量 可删除 + for (LinkedList nodeChain : nodeChainList) { + if (nodeChain.size() >= i + 1) { + FlameGraphNode node = nodeChain.get(i); + thisList.add(node); + } + } + + LinkedList mergeList = new LinkedList<>(); + tierNodesList.add(mergeList); + // objectCount++; //TODO 记录产生的对象数量 可删除 + + for (FlameGraphNode node : thisList) { + if (mergeList.size() == 0 || !mergeList.getLast().getName().equals(node.getName())) { + // mergeList.add(node.deepClone()); + mergeList.add(node); + } else { + FlameGraphNode last = mergeList.getLast(); + if (node.getyHigh().equals(last.getyLow() - 1) && node.getPreName().equals(last.getPreName())) { + last.setyLow(node.getyLow()); + last.setValue(last.getValue() + node.getValue()); + } else { + // mergeList.add(node.deepClone()); + mergeList.add(node); + } + } + } + } + + for (int i = 0; i < tierNodesList.size(); i++) { + List thisTierNodes = tierNodesList.get(i); + if (i == 0) { + rootNode.setChildren(thisTierNodes); + } + + List nextTierNodes; + if (i < tierNodesList.size() - 1) { + nextTierNodes = tierNodesList.get(i + 1); + } else { + break; + } + + for (FlameGraphNode thisTierNode : thisTierNodes) { + List children = new ArrayList<>(); + //objectCount++; //TODO 记录产生的对象数量 可删除 + for (FlameGraphNode nextTierNode : nextTierNodes) { + if (nextTierNode.getyHigh() <= thisTierNode.getyHigh() + && nextTierNode.getyLow() >= thisTierNode.getyLow()) { + children.add(nextTierNode); + } else if (nextTierNode.getyHigh() < thisTierNode.getyLow()) { + break; + } + } + + if (!children.isEmpty()) { + thisTierNode.setChildren(children); + } + } + + } + rootNode.setValue(rootValue); + + String flameGraphJson = mapper.writeValueAsString(rootNode); + + System.out.println("execute time: " + (System.currentTimeMillis() - b)); + // System.out.println("produce object number:" + " " + objectCount); + + return flameGraphJson; + } + + public static String parseJson(File file) throws IOException { + + long b; + b = System.currentTimeMillis(); + List metadata = new ArrayList<>(); + + if (!file.isFile() || !file.exists()) { + LOGGER.debug("Fail open file: " + file.getName()); + throw new IOException("can not find file."); + } + + try (InputStreamReader read = new InputStreamReader(new FileInputStream(file), + StandardCharsets.UTF_8); + BufferedReader bufferedReader = new BufferedReader(read)) { + String line; + + while ((line = bufferedReader.readLine()) != null) { + //LOGGER.debug("read line from file: " + line); + metadata.add(line); + } + } + LOGGER.debug("metaData size is: " + metadata.size()); + + FlameGraphNode rootNode = new FlameGraphNode(); + rootNode.setName("root"); + + int rootValue = 0; + int maxTier = 0; + //int objectCount = 0; //TODO 记录产生的对象数量 可删除 + List> nodeChainList = new ArrayList<>(); + + for (int j = 0; j < metadata.size(); j++) { + String line = metadata.get(j); + int num = Integer.valueOf(line.substring(line.lastIndexOf(" ") + 1)); + line = line.substring(0, line.lastIndexOf(" ")); + String[] elements = line.split("\\|"); + + LinkedList nodeChain = new LinkedList<>(); + nodeChainList.add(nodeChain); + for (int i = 1; i <= elements.length; i++) { + FlameGraphNode node = + new FlameGraphNode(elements[i - 1],num,metadata.size() - j,metadata.size() - j); + nodeChain.add(node); + + + //objectCount++; //TODO 记录产生的对象数量 可删除 + if (i == 1) { + node.setPreName(rootNode.getName()); + } else { + node.setPreName(elements[i - 2]); + } + + } + if (maxTier < elements.length) { + maxTier = elements.length; + } + rootValue += num; + } + + List> tierNodesList = new ArrayList<>(); + for (int i = 0; i < maxTier; i++) { + List thisList = new ArrayList<>(); + //objectCount++; //TODO 记录产生的对象数量 可删除 + for (LinkedList nodeChain : nodeChainList) { + if (nodeChain.size() >= i + 1) { + FlameGraphNode node = nodeChain.get(i); + thisList.add(node); + } + } + + LinkedList mergeList = new LinkedList<>(); + tierNodesList.add(mergeList); + // objectCount++; //TODO 记录产生的对象数量 可删除 + + for (FlameGraphNode node : thisList) { + if (mergeList.size() == 0 || !mergeList.getLast().getName().equals(node.getName())) { + // mergeList.add(node.deepClone()); + mergeList.add(node); + } else { + FlameGraphNode last = mergeList.getLast(); + if (node.getyHigh().equals(last.getyLow() - 1) && node.getPreName().equals(last.getPreName())) { + last.setyLow(node.getyLow()); + last.setValue(last.getValue() + node.getValue()); + } else { + // mergeList.add(node.deepClone()); + mergeList.add(node); + } + } + } + } + + for (int i = 0; i < tierNodesList.size(); i++) { + List thisTierNodes = tierNodesList.get(i); + if (i == 0) { + rootNode.setChildren(thisTierNodes); + } + + List nextTierNodes; + if (i < tierNodesList.size() - 1) { + nextTierNodes = tierNodesList.get(i + 1); + } else { + break; + } + + for (FlameGraphNode thisTierNode : thisTierNodes) { + List children = new ArrayList<>(); + //objectCount++; //TODO 记录产生的对象数量 可删除 + for (FlameGraphNode nextTierNode : nextTierNodes) { + if (nextTierNode.getyHigh() <= thisTierNode.getyHigh() + && nextTierNode.getyLow() >= thisTierNode.getyLow()) { + children.add(nextTierNode); + } else if (nextTierNode.getyHigh() < thisTierNode.getyLow()) { + break; + } + } + + if (!children.isEmpty()) { + thisTierNode.setChildren(children); + } + } + + } + rootNode.setValue(rootValue); + + String flameGraphJson = mapper.writeValueAsString(rootNode); + + System.out.println("execute time: " + (System.currentTimeMillis() - b)); + // System.out.println("produce object number:" + " " + objectCount); + + return flameGraphJson; + } + + /** + * 将火焰图数据解析为多个火焰图节点进行json序列化. + */ + static class FlameGraphNode implements Serializable { + + private static final long serialVersionUID = 1668513735481340291L; + + /** + * 子节点,非空时才序列化 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private List children; + + /** + * 节点名称 + */ + private String name; + + /** + * 节点宽度 + */ + private Integer value; + + /** + * 仅在解析时使用,不进行序列化 + */ + @JsonIgnore + private Integer yHigh; + + /** + * 仅在解析时使用,不进行序列化 + */ + @JsonIgnore + private Integer yLow; + + /** + * 仅在解析时使用,不进行序列化 + */ + @JsonIgnore + private String preName; + + public FlameGraphNode() {} + + public FlameGraphNode(String name, Integer value, Integer yHigh, Integer yLow) { + this.name = name; + this.value = value; + this.yHigh = yHigh; + this.yLow = yLow; + } + + public String getPreName() { + return preName; + } + + public void setPreName(String preName) { + this.preName = preName; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + + public Integer getyHigh() { + return yHigh; + } + + public void setyHigh(Integer yHigh) { + this.yHigh = yHigh; + } + + public Integer getyLow() { + return yLow; + } + + public void setyLow(Integer yLow) { + this.yLow = yLow; + } + + /** + * 深拷贝 + */ + public FlameGraphNode deepClone() throws IOException, ClassNotFoundException { + //将对象写入流中 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); + objectOutputStream.writeObject(this); + //从流中取出 + ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); + return (FlameGraphNode)objectInputStream.readObject(); + + } + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/HttpSocketServer.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/HttpSocketServer.java new file mode 100644 index 00000000..9c34b4a5 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/HttpSocketServer.java @@ -0,0 +1,114 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; + +public class HttpSocketServer implements RpcServer { + private static final Logger LOGGER = Logger.getLogger(HttpSocketServer.class); + private static volatile HttpSocketServer instance; + private static HttpHandler handler; + private static AtomicBoolean stopped; + private HttpServer httpServer; + private String port; + private static final int PARALLEL_THREAD_NUMBER = 4; + + public static HttpSocketServer getInstance(String port, HttpHandler handler) { + if (instance == null) { + synchronized (HttpSocketServer.class) { + if (instance == null) { + instance = new HttpSocketServer(port, handler); + } + } + } + + // Handler and port update. TODO: don't use singleton! + if (!instance.port.equalsIgnoreCase(port)) { + instance.port = port; + } + if (!instance.handler.equals(handler)) { + instance.handler = handler; + } + return instance; + } + + private HttpSocketServer(String port, HttpHandler handler) { + this.port = port; + this.handler = handler; + stopped = new AtomicBoolean(true); + } + + public void start(String url) throws IOException { + assert handler != null; + // Can only start when stopped. + assert stopped.get() != false; + LOGGER.debug("Starting listen on http url: http://localhost:" + port + url); + httpServer = HttpServer.create(new InetSocketAddress(Integer.parseInt(port)), 0); + + HttpContext context = httpServer.createContext(url, handler); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + stopHook(); + } + }); + + final ThreadFactory threadFactory = new ThreadFactory() { + @Override + public Thread newThread(@NotNull Runnable runnable) { + final Thread thread = new Thread(runnable, "TencentCloudJvmMonitor-cmd-processor"); + thread.setDaemon(true); + thread.setContextClassLoader(this.getClass().getClassLoader()); + return thread; + } + }; + httpServer.setExecutor(Executors.newFixedThreadPool(PARALLEL_THREAD_NUMBER, threadFactory)); + httpServer.start(); + } + + private void stop() { + if (stopped.get() == true) { + return; + } + stopped.set(true); + httpServer.stop(2); + } + + private static void stopHook() { + LOGGER.debug("Stop httpSocketServer in ShutdownHook"); + if (instance != null) { + instance.stop(); + } + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorDummyClass.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorDummyClass.java new file mode 100644 index 00000000..f37dcb84 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorDummyClass.java @@ -0,0 +1,31 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +// Used only for profiler to clear all classes. do not change this file. +public class JvmMonitorDummyClass { + public static void init() { + System.out.println("dummy!!!"); + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorHttpRequestHandler.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorHttpRequestHandler.java new file mode 100644 index 00000000..039c9431 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorHttpRequestHandler.java @@ -0,0 +1,84 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; + +public abstract class JvmMonitorHttpRequestHandler implements HttpHandler { + + private static final Logger LOGGER = + Logger.getLogger(JvmMonitorHttpRequestHandler.class); + + private static final int BUFFER_SIZE = 1024; + + public void handle(HttpExchange exchange) throws IOException { + String requestMethod = exchange.getRequestMethod(); + LOGGER.debug("get request: " + requestMethod); + if (requestMethod.equalsIgnoreCase("POST")) { + String requestBodyString = readFromStream(exchange.getRequestBody()); + String response = processCommand(requestBodyString); + if (response != null && (response.length() > 0)) { + exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length()); + LOGGER.debug("JvmMonitorAgent response request: <" + requestBodyString + ">," + + "response <" + new String(response.getBytes()) + ">"); + // RESPONSE Body + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.flush(); + os.close(); + } else { + LOGGER.error("Can not generate response data of request " + + requestMethod + " (" + requestBodyString + ")"); + exchange.sendResponseHeaders(HttpURLConnection.HTTP_BAD_REQUEST, 0); + } + } else { + exchange.sendResponseHeaders(HttpURLConnection.HTTP_BAD_METHOD,0); + } + exchange.close(); + } + + private static String readFromStream(InputStream is) { + try { + byte[] ba = new byte [BUFFER_SIZE]; + int off = 0; + int c = 0; + while ((c = is.read(ba, off, ba.length)) != -1) { + off += c; + } + return new String(ba, 0, off, "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error("Fail read request body"); + } + return null; + } + + public abstract String processCommand(String requestBody); +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorUtils.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorUtils.java new file mode 100644 index 00000000..f6c25be9 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/JvmMonitorUtils.java @@ -0,0 +1,121 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import java.io.*; + +public class JvmMonitorUtils { + private static final Logger LOGGER = Logger.getLogger(JvmMonitorUtils.class); + private static String dataSavePath = "/Users/momo/data/tsf_apm/monitor/jvm-metrics/"; + // only enable manually at develop phase + private static final Boolean DUMP_FILE_CONTENT = false; + + public static String getFileFromJar(String name) throws Exception { + InputStream in = JvmMonitorUtils.class.getClassLoader().getResourceAsStream(name); + byte[] buffer = new byte[1024]; + int read = -1; + File temp = File.createTempFile(name, ""); + temp.deleteOnExit(); + FileOutputStream fos = new FileOutputStream(temp); + + while ((read = in.read(buffer)) != -1) { + if (DUMP_FILE_CONTENT) { + LOGGER.debug("read file (" + name + ") : " + new String(buffer)); + } + fos.write(buffer, 0, read); + } + fos.close(); + in.close(); + + return temp.getAbsolutePath(); + } + + public static String getUpdatedFileFromJar(String name, String pathToAdd) throws Exception { + InputStream in = JvmMonitorUtils.class.getClassLoader().getResourceAsStream(name); + byte[] buffer = new byte[1024]; + File temp = File.createTempFile(name, ""); + temp.deleteOnExit(); + FileOutputStream fos = new FileOutputStream(temp); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + LOGGER.debug("temp file for jmxtrans config: " + temp.getAbsolutePath()); + while (br.ready()) { + String line = br.readLine(); + if (DUMP_FILE_CONTENT) { + LOGGER.debug("read line: " + line); + } + if (line.contains("")) { + String sep = "/"; + if (pathToAdd.charAt(pathToAdd.length() - 1) == '/') { + sep = ""; + } + String pathString = "" + pathToAdd + sep + "jmxtrans-agent.data" + "\n"; + LOGGER.debug("Add jmx config file path: " + pathString); + fos.write(pathString.getBytes(),0, pathString.length()); + } + fos.write(line.getBytes(), 0, line.length()); + } + fos.close(); + br.close(); + in.close(); + return temp.getAbsolutePath(); + } + + + public static boolean createDataPath() { + String path = dataSavePath; + if (path.length() == 0 || path.equalsIgnoreCase("./")) { + LOGGER.info("Use Data Dir: " + path); + return true; + } else { + try { + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + } catch (Exception e) { + LOGGER.error("Fail create data dir: " + path + e); + return false; + } + } + LOGGER.info("Use Data Dir: " + path); + return true; + } + + public static String getDataSavePath() { + LOGGER.debug("get Data Save path: " + dataSavePath + " loader " + + JvmMonitorUtils.class.getClassLoader().toString()); + if (dataSavePath == null) { + return ""; + } + if (dataSavePath.charAt(dataSavePath.length() - 1) != '/') { + return dataSavePath + "/"; + } + return dataSavePath; + } + + public static void setDataSavePath(String dataSavePath) { + LOGGER.debug("ZLIN - update dataSavePath: " + dataSavePath); + JvmMonitorUtils.dataSavePath = dataSavePath; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Logger.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Logger.java new file mode 100644 index 00000000..242d8181 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/Logger.java @@ -0,0 +1,152 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import java.io.PrintStream; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +/** + * @author Cyrille Le Clerc + */ +public class Logger { + + public static Logger getLogger(String name) { + return new Logger(name); + } + + public static Logger getLogger(Class klass) { + return new Logger(klass.getName()); + } + + private final String name; + + public static PrintStream out; + + private static final Level default_level = Level.INFO; + + static { + if ("stderr".equalsIgnoreCase(System.getenv("JMX_TRANS_AGENT_CONSOLE")) + || "stderr".equalsIgnoreCase(System.getProperty(Logger.class.getName() + ".console"))) { + Logger.out = System.err; + } else { + Logger.out = System.out; + } + } + + public static Level level = Level.INFO; + + public static Level parseLevel(String level, Level defaultValue) { + + Map julLevelsByName = new HashMap() { + { + put("TRACE", Level.FINEST); + put("FINEST", Level.FINEST); + put("FINER", Level.FINER); + put("FINE", Level.FINE); + put("DEBUG", Level.FINE); + put("INFO", Level.INFO); + put("WARNING", Level.WARNING); + put("WARN", Level.WARNING); + put("SEVERE", Level.SEVERE); + put("ERROR", Level.SEVERE); + put("ALL", Level.ALL); + + } + }; + + for (Map.Entry entry: julLevelsByName.entrySet()) { + if (entry.getKey().equalsIgnoreCase(level)) { + return entry.getValue(); + } + } + return defaultValue; + } + + static { + String level = System.getProperty(Logger.class.getName() + ".level", "INFO"); + System.out.println(Logger.class.getName() + ".level is " + level); + Logger.level = parseLevel(level, default_level); + } + + public Logger(String name) { + this.name = name; + } + + public void log(Level level, String msg) { + log(level, msg, null); + } + + public void log(Level level, String msg, Throwable thrown) { + if (!isLoggable(level)) { + return; + } + Logger.out.println(new Timestamp(System.currentTimeMillis()) + " " + level + " [" + + Thread.currentThread().getName() + "] " + name + " - " + msg); + if (thrown != null) { + thrown.printStackTrace(Logger.out); + } + } + + public void finest(String msg) { + log(Level.FINEST, msg); + } + + public void finer(String msg) { + log(Level.FINER, msg); + } + + public void fine(String msg) { + log(Level.FINE, msg); + } + + public void info(String msg) { + log(Level.INFO, msg); + } + + public void warning(String msg) { + log(Level.WARNING, msg); + } + + public void debug(String msg) { + fine(msg); + } + + public void warn(String msg) { + warning(msg); + } + + public void error(String msg) { + log(Level.SEVERE, msg); + } + + public boolean isLoggable(Level level) { + if (level.intValue() < this.level.intValue()) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/OSChecker.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/OSChecker.java new file mode 100644 index 00000000..2da2ee3e --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/OSChecker.java @@ -0,0 +1,50 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public class OSChecker { + private static String OS = System.getProperty("os.name").toLowerCase(); + private static String PT = System.getProperty("os.arch").toLowerCase(); + + public static boolean isWindows() { + return (OS.indexOf("win") >= 0); + } + + public static boolean isMac() { + return (OS.indexOf("mac") >= 0); + } + + public static boolean isUnix() { + return (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0); + } + + public static boolean isSolaris() { + + return (OS.indexOf("sunos") >= 0); + } + + public static boolean isAarch64() { + return (PT.indexOf("aarch64") >= 0); + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/ResultInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/ResultInfo.java new file mode 100644 index 00000000..cb5b5ace --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/ResultInfo.java @@ -0,0 +1,108 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public class ResultInfo { + String status; + String statusInfo; + String data; + boolean compression; + + public ResultInfo() { + status = ""; + statusInfo = ""; + data = ""; + compression = false; + } + + public ResultInfo(String st, String info, String dat) { + status = st; + statusInfo = info; + data = dat; + compression = false; + } + + public ResultInfo(String st, String info, String dat, boolean compression) { + status = st; + statusInfo = info; + data = dat; + this.compression = compression; + } + + public ResultInfo(String st, String dat) { + status = st; + statusInfo = ""; + data = dat; + compression = false; + } + + public ResultInfo(String st) { + status = st; + statusInfo = ""; + data = ""; + compression = false; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getStatusInfo() { + return statusInfo; + } + + public void setStatusInfo(String statusInfo) { + this.statusInfo = statusInfo; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public String toString() { + return "ResultInfo{" + + "status='" + status + '\'' + + ", statusInfo='" + statusInfo + '\'' + + ", data='" + data + '\'' + + ", compression = '" + compression + '\'' + + '}'; + } + + public boolean isCompression() { + return compression; + } + + public void setCompression(boolean compression) { + this.compression = compression; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/ResultPackage.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/ResultPackage.java new file mode 100644 index 00000000..91c9db6d --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/ResultPackage.java @@ -0,0 +1,49 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public class ResultPackage { + private String taskId; + private ResultInfo resultInfo; + + public ResultPackage() {} + + public ResultPackage(String tid, ResultInfo result) { + taskId = tid; + resultInfo = result; + } + + public String getTaskId() { + return taskId; + } + + public ResultInfo getResultInfo() { + return resultInfo; + } + + @Override + public String toString() { + return "ResultPackage{" + "taskId='" + taskId + '\'' + ", resultInfo=" + resultInfo + '}'; + } +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/RpcServer.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/RpcServer.java new file mode 100644 index 00000000..68c65997 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/RpcServer.java @@ -0,0 +1,32 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +public interface RpcServer { + void start(@Nullable String url) throws IOException; +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/RpcSocketServer.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/RpcSocketServer.java new file mode 100644 index 00000000..99c374b3 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/RpcSocketServer.java @@ -0,0 +1,128 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.instrument.Instrumentation; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +public class RpcSocketServer implements RpcServer { + private static final Logger LOGGER = Logger.getLogger(RpcSocketServer.class); + private static volatile RpcSocketServer socketServer; + private ServerSocket svcSocket; + private String port; + + public static RpcSocketServer getInstance(String port, Instrumentation inst) { + if (socketServer == null) { + synchronized (RpcSocketServer.class) { + if (socketServer == null) { + socketServer = new RpcSocketServer(port); + } + } + } + return socketServer; + } + + private RpcSocketServer(String port) { + this.port = port; + } + + public void start(String s) throws IOException { + svcSocket = new ServerSocket(Integer.parseInt(port)); + Runnable rpcServerTask = new Runnable() { + public void run() { + try { + LOGGER.info("RPC Server waiting for connection..."); + while (true) { + Socket client = svcSocket.accept(); + new HandlerThread(client); + } + } catch (SocketException e) { + LOGGER.warn("RpcSocketServer closed"); + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + Thread serverThread; + serverThread = new Thread(rpcServerTask, "RPC Server Thread"); + serverThread.start(); + } + + public void stop() { + if (!svcSocket.isClosed()) { + try { + svcSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private class HandlerThread implements Runnable { + private Socket socket; + + HandlerThread(Socket socket) { + this.socket = socket; + new Thread(this).start(); + } + + public void run() { + try { + DataInputStream input = new DataInputStream(socket.getInputStream()); + String command = input.readUTF(); + LOGGER.debug("Get command " + command + " from socket."); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + String response = processCommand(command); + out.write(response.getBytes()); + out.close(); + input.close(); + } catch (IOException e) { + LOGGER.error("Error Reading Data from Socket"); + e.printStackTrace(); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (Exception e) { + socket = null; + System.out.println("Server Wrong at finally" + e.getMessage()); + } + } + } + } + + private String processCommand(String command) { + // TODO: impl me. + + return command; + } + } + + +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskInfo.java new file mode 100644 index 00000000..710814c2 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskInfo.java @@ -0,0 +1,52 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public class TaskInfo { + private String type; + private String action; + private TaskStatus status; + private TaskStatusInfo taskStatusInfo; + + public TaskInfo(Command cmd, TaskStatus stat, TaskStatusInfo info) { + type = cmd.getType(); + action = cmd.getAction(); + status = stat; + taskStatusInfo = info; + } + + public void setStatus(TaskStatus status, TaskStatusInfo info) { + this.status = status; + this.taskStatusInfo = info; + } + + public TaskStatus getStatus() { + return status; + } + + public TaskStatusInfo getTaskStatusInfo() { + return taskStatusInfo; + } + +} diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskStatus.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskStatus.java new file mode 100644 index 00000000..4c43a44e --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskStatus.java @@ -0,0 +1,31 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public enum TaskStatus { + AVAILABLE, + BUSY, + COMPLETED, + ERROR; +} \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskStatusInfo.java b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskStatusInfo.java new file mode 100644 index 00000000..fede108d --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/java/com/tencent/femas/tencentcloudjvmmonitor/utils/TaskStatusInfo.java @@ -0,0 +1,35 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.femas.tencentcloudjvmmonitor.utils; + +public enum TaskStatusInfo { + EXCEED_SIZE, + MAL_FUNC, + BUSY, + NO_CONNECTION, + NO_FILE, + NO_DATA, + FAIL_TRANSFER, + SUCCESS_NULL /* not used */ +} diff --git a/femas-agent/femas-agent-jts/src/main/resources/cmdline_config.properties b/femas-agent/femas-agent-jts/src/main/resources/cmdline_config.properties new file mode 100644 index 00000000..e3a1461b --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/resources/cmdline_config.properties @@ -0,0 +1,9 @@ +http-server-port=39411 +hotspot-ip=localhost +hotspot-port=11099 +mode=monitor +pingHotspotInterval=0 +# connect_mode=REMOTE +# not supported because monitor agent starts before cmaline. +# monitor_config=/tmp/tsf_monitor_config.xml +# java -javaagent:./TencentCloudJvmMonitor-1.0-SNAPSHOT.jar=hascontroller=true -cp .:./lib -Djava.library.path=./lib/ GCBench \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/src/main/resources/tsf_monitor_config.xml b/femas-agent/femas-agent-jts/src/main/resources/tsf_monitor_config.xml new file mode 100644 index 00000000..cde3b90a --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/resources/tsf_monitor_config.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + true + + /Users/momo/data/tsf_apm/monitor/jvm-metrics/jmxtrans-agent.data + 50 + 8 + + + 10 + + diff --git a/femas-agent/femas-agent-jts/src/main/resources/tsf_monitor_config_bk.xml b/femas-agent/femas-agent-jts/src/main/resources/tsf_monitor_config_bk.xml new file mode 100644 index 00000000..940b27f8 --- /dev/null +++ b/femas-agent/femas-agent-jts/src/main/resources/tsf_monitor_config_bk.xml @@ -0,0 +1,52 @@ + + + 1 + + + + + + + + + + + + true + 50 + 8 + + + \ No newline at end of file diff --git a/femas-agent/femas-agent-jts/test/run.sh b/femas-agent/femas-agent-jts/test/run.sh new file mode 100644 index 00000000..d1ccb38c --- /dev/null +++ b/femas-agent/femas-agent-jts/test/run.sh @@ -0,0 +1 @@ +curl -X POST --data '{"taskId":"test1","type":"ThreadDump","action":"","metaInfo":"{}"}' http://localhost:11099/jvm diff --git a/femas-agent/femas-agent-plugin/femas-springcloud-greenwich-plugin/femas-springcloud-greenwich-feign-plugin/pom.xml b/femas-agent/femas-agent-plugin/femas-springcloud-greenwich-plugin/femas-springcloud-greenwich-feign-plugin/pom.xml index 16ed034d..63c3cb0b 100644 --- a/femas-agent/femas-agent-plugin/femas-springcloud-greenwich-plugin/femas-springcloud-greenwich-feign-plugin/pom.xml +++ b/femas-agent/femas-agent-plugin/femas-springcloud-greenwich-plugin/femas-springcloud-greenwich-feign-plugin/pom.xml @@ -35,7 +35,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.1 true false diff --git a/femas-agent/femas-agent-starter/src/main/java/com/tencent/tsf/femas/agent/FemasAgent.java b/femas-agent/femas-agent-starter/src/main/java/com/tencent/tsf/femas/agent/FemasAgent.java index ce86caca..1666ceb1 100644 --- a/femas-agent/femas-agent-starter/src/main/java/com/tencent/tsf/femas/agent/FemasAgent.java +++ b/femas-agent/femas-agent-starter/src/main/java/com/tencent/tsf/femas/agent/FemasAgent.java @@ -23,7 +23,6 @@ import com.tencent.tsf.femas.agent.config.*; import com.tencent.tsf.femas.agent.interceptor.wrapper.*; - import com.tencent.tsf.femas.agent.tools.AgentLogger; import com.tencent.tsf.femas.agent.tools.JvmRuntimeInfo; import com.tencent.tsf.femas.agent.transformer.CompositeTransformer; @@ -225,7 +224,6 @@ private synchronized static void initializeAgent(String agentArguments, Instrume InterceptPlugin interceptPlugin = plugin.getPlugin(); agentBuilder = pluginAgentBuilder(agentBuilder, interceptPlugin); } - if (ACTIVATE_CROSS_THREAD_TRANSFORMER.equalsIgnoreCase(agentArguments)) { instrumentation.addTransformer(new CompositeTransformer(new ExecutorTransformer(), new TimerTaskTransformer(), new ForkJoinTransformer()), true); } diff --git a/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/AgentLogger.java b/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/AgentLogger.java index 8a95bd0b..bad8affd 100644 --- a/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/AgentLogger.java +++ b/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/AgentLogger.java @@ -50,6 +50,10 @@ public void warn(String msg, Throwable throwable) { logger.log(Level.WARNING, formatMessage); } + public void error(String msg) { + error(msg, null); + } + public void error(String msg, Throwable throwable) { String exceptionMessage = toString(throwable); String stackTrace = getStackTraceString(throwable); diff --git a/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/Logger.java b/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/Logger.java new file mode 100644 index 00000000..1c782d56 --- /dev/null +++ b/femas-agent/femas-agent-tools/src/main/java/com/tencent/tsf/femas/agent/tools/Logger.java @@ -0,0 +1,152 @@ +/** + * Copyright 2010-2021 the original author or authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.tsf.femas.agent.tools; + +import java.io.PrintStream; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +/** + * @author Cyrille Le Clerc + */ +public class Logger { + + public static Logger getLogger(String name) { + return new Logger(name); + } + + public static Logger getLogger(Class klass) { + return new Logger(klass.getName()); + } + + private final String name; + + public static PrintStream out; + + private static final Level default_level = Level.INFO; + + static { + if ("stderr".equalsIgnoreCase(System.getenv("JMX_TRANS_AGENT_CONSOLE")) + || "stderr".equalsIgnoreCase(System.getProperty(Logger.class.getName() + ".console"))) { + Logger.out = System.err; + } else { + Logger.out = System.out; + } + } + + public static Level level = Level.INFO; + + public static Level parseLevel(String level, Level defaultValue) { + + Map julLevelsByName = new HashMap() { + { + put("TRACE", Level.FINEST); + put("FINEST", Level.FINEST); + put("FINER", Level.FINER); + put("FINE", Level.FINE); + put("DEBUG", Level.FINE); + put("INFO", Level.INFO); + put("WARNING", Level.WARNING); + put("WARN", Level.WARNING); + put("SEVERE", Level.SEVERE); + put("ERROR", Level.SEVERE); + put("ALL", Level.ALL); + + } + }; + + for (Map.Entry entry: julLevelsByName.entrySet()) { + if (entry.getKey().equalsIgnoreCase(level)) { + return entry.getValue(); + } + } + return defaultValue; + } + + static { + String level = System.getProperty(Logger.class.getName() + ".level", "INFO"); + System.out.println(Logger.class.getName() + ".level is " + level); + Logger.level = parseLevel(level, default_level); + } + + public Logger(String name) { + this.name = name; + } + + public void log(Level level, String msg) { + log(level, msg, null); + } + + public void log(Level level, String msg, Throwable thrown) { + if (!isLoggable(level)) { + return; + } + Logger.out.println(new Timestamp(System.currentTimeMillis()) + " " + level + " [" + + Thread.currentThread().getName() + "] " + name + " - " + msg); + if (thrown != null) { + thrown.printStackTrace(Logger.out); + } + } + + public void finest(String msg) { + log(Level.FINEST, msg); + } + + public void finer(String msg) { + log(Level.FINER, msg); + } + + public void fine(String msg) { + log(Level.FINE, msg); + } + + public void info(String msg) { + log(Level.INFO, msg); + } + + public void warning(String msg) { + log(Level.WARNING, msg); + } + + public void debug(String msg) { + fine(msg); + } + + public void warn(String msg) { + warning(msg); + } + + public void error(String msg) { + log(Level.SEVERE, msg); + } + + public boolean isLoggable(Level level) { + if (level.intValue() < this.level.intValue()) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/femas-agent/pom.xml b/femas-agent/pom.xml index 3c1d9791..9f4881dd 100644 --- a/femas-agent/pom.xml +++ b/femas-agent/pom.xml @@ -31,6 +31,7 @@ femas-agent-starter femas-agent-tools femas-agent-plugin + femas-agent-jts femas-agent-example @@ -136,29 +137,4 @@ - - - - nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - false - - - true - - - - - nexus-releases - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - true - - - false - - - - \ No newline at end of file diff --git a/femas-common/src/main/java/com/tencent/tsf/femas/common/httpclient/factory/ApacheDefaultHttpClientFactory.java b/femas-common/src/main/java/com/tencent/tsf/femas/common/httpclient/factory/ApacheDefaultHttpClientFactory.java index 3f6b5065..e3e7b7ea 100644 --- a/femas-common/src/main/java/com/tencent/tsf/femas/common/httpclient/factory/ApacheDefaultHttpClientFactory.java +++ b/femas-common/src/main/java/com/tencent/tsf/femas/common/httpclient/factory/ApacheDefaultHttpClientFactory.java @@ -7,8 +7,6 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.RequestContent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * @Author leoziltong @@ -18,8 +16,6 @@ */ public class ApacheDefaultHttpClientFactory implements HttpClientFactory { - private static final Logger log = LoggerFactory.getLogger(ApacheDefaultHttpClientFactory.class); - private boolean authenticateSslClients = true; private long connectionTimeToLive; diff --git a/femas-dependencies-bom/pom.xml b/femas-dependencies-bom/pom.xml index dd5be7df..0f5221ac 100755 --- a/femas-dependencies-bom/pom.xml +++ b/femas-dependencies-bom/pom.xml @@ -264,6 +264,12 @@ ${project.version}
+ + com.tencent.tsf + femas-agent-jts + ${project.version} + + com.tencent.tsf femas-extension-springcloud-common diff --git a/pom.xml b/pom.xml index df3c05a6..b5f66c62 100755 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 0.8.3 3.1.0 1.8 - 3.1.1 + 3.2.2 3.1.0 3.8 2.1.16.RELEASE @@ -167,21 +167,6 @@ - - - - - - - - - - - - - - - org.apache.maven.plugins maven-project-info-reports-plugin @@ -342,29 +327,5 @@ - - - - nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - false - - - true - - - - - nexus-releases - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - true - - - false - - - diff --git a/settings-all.xml b/settings-all.xml index c9ee52b5..cacaf608 100644 --- a/settings-all.xml +++ b/settings-all.xml @@ -1,321 +1,321 @@ - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - dev - true - http - - - - 8080 - - --> - - - - - - - - - - - - - - - - nexus - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - maven-public - https://mirrors.tencent.com/nexus/repository/maven-public/ - - true - - - false - - + + + + + + + + + + - - thirdparty - https://mirrors.tencent.com/repository/maven/thirdparty/ - - true - - - false - - + + + + + + + + + + - - qcloud-releases - http://nexus.qcloud.oa.com/repository/maven-releases/ - - true - - - false - - + + + + + + + + + + - - qcloud-snapshots - - - false - - - true - - + + + + + + + + + + - - tsf - https://mirrors.tencent.com/repository/maven/tsf/ - - true - - - false - - + + + + + + + + + + - - tsf-snapshot - https://mirrors.tencent.com/repository/maven/tsf-snapshot/ - - false - - - true - - + + + + + + + + + + - - csig-prod-mid-dev02 - https://mirrors.tencent.com/repository/maven/csig-prod-mid-dev02/ - - true - - - false - - + + + + + + + + + + - - csig-prod-mid-dev02-snapshot - - https://mirrors.tencent.com/repository/maven/csig-prod-mid-dev02-snapshot/ - - - false - - - true - - - + + + + + + + + + + + + + - + - - public-plugin - https://mirrors.tencent.com/nexus/repository/maven-public/ - - true - - - false - - + + + + + + + + + + - - thirdparty-plugin - https://mirrors.tencent.com/repository/maven/thirdparty/ - - true - - - false - - - - - - - - nexus - + + + + + + + + + + + + + + + + + + + + + + + + - +