diff --git a/.gitignore b/.gitignore
index 6093f6bb04ffb29a014cb2abc76c6d480f44dceb..5fd13d867676e4f88848768fac5d25e9e8ab4212 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,9 @@ tools/install_dependency/src/component
component/LkpTests/compatibility_help/compatibility_testing
component/LkpTests/compatibility_help/compatibility_testing.tar.gz
component/LkpTests/lkp_help/lkp-tests.tar.gz
-component/DevkitDistribute/devkit_distribute/config/log.ini
-component/DevkitDistribute/devkit_distribute/config/devkit_distribute.yaml
-devkitdependencies
\ No newline at end of file
+component/DevKitTester/devkit_tester/config/log.ini
+component/DevKitTester/devkit_tester/config/devkit_distribute.yaml
+devkitdependencies
+component/DevKitTester/JFRParser/.idea
+component/DevKitTester/JFRParser/.gitignore
+component/DevKitTester/JFRParser/target
\ No newline at end of file
diff --git a/ci/cenos_7_env_prepare.sh b/ci/cenos_7_env_prepare.sh
new file mode 100644
index 0000000000000000000000000000000000000000..cb35c54473a2611d31f82f517e50b4cbcc8f5504
--- /dev/null
+++ b/ci/cenos_7_env_prepare.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+
+bash centos7_python3_prepare.sh
+
+# 准备centos 构建环境
+yum -y install git.aarch64 vim.aarch64 java-11-openjdk-devel.aarch64
+
+wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
+
+tar -xvzf apache-maven-3.9.6-bin.tar.gz -C /opt --no-same-owner
+
+export PATH=/opt/apache-maven-3.9.6/bin:$PATH
+
+
diff --git a/ci/centos7_build_clamav.sh b/ci/centos7_build_clamav.sh
new file mode 100644
index 0000000000000000000000000000000000000000..12905de07a02df93484996f00454974c44be4ad2
--- /dev/null
+++ b/ci/centos7_build_clamav.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+yum install -y \
+ gcc gcc-c++ make python3 python3-pip valgrind expect \
+ bzip2-devel check-devel json-c-devel libcurl-devel libxml2-devel \
+ ncurses-devel openssl-devel pcre2-devel sendmail-devel zlib-devel
+
+# shellcheck disable=SC2024
+curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf > rust.sh
+chmod +x rust.sh
+
+# shellcheck disable=SC2121
+expect RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
+# shellcheck disable=SC2121
+expect RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
+
+# bash rust.sh
+bash expect/install_rust.sh
+# shellcheck disable=SC1090
+source ~/.bashrc
+
+
+wget https://github.com/Kitware/CMake/archive/refs/tags/v3.29.3.tar.gz
+tar -xvzf v3.29.3.tar.gz
+
+# shellcheck disable=SC2164
+pushd "CMake-3.29.3"
+./configure --prefix=/opt/local
+make
+make install
+
+export LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH
+export PATH=/opt/local/bin:$PATH
+
+wget https://clamav-site.s3.amazonaws.com/production/release_files/files/000/001/607/original/clamav-1.3.1.tar.gz
+tar -xvzf clamav-1.3.1.tar.gz
+cmake -DCMAKE_INSTALL_PREFIX=/opt/clamav-1.3.1 ..
+make -j
+make install
+cp /opt/clamav-1.3.1/etc/clamd.conf.sample /opt/clamav-1.3.1/etc/clamd.conf
+cp /opt/clamav-1.3.1/etc/freshclam.conf.sample /opt/clamav-1.3.1/etc/freshclam.conf
+# 注释两个文件中的example
+
diff --git a/ci/centos7_python3_prepare.sh b/ci/centos7_python3_prepare.sh
new file mode 100644
index 0000000000000000000000000000000000000000..58dd87f64e9f882ef5a763d7f46bafd9b47684e2
--- /dev/null
+++ b/ci/centos7_python3_prepare.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+# 准备centos 构建环境
+yum -y install wget.aarch64 gcc.aarch64 gcc-c++.aarch64 libffi-devel.a
+# 准备openssl 1.1.1
+mkdir /home/package
+
+# shellcheck disable=SC2164
+pushd /home/package
+export LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH
+export PATH=/opt/local/bin:$PATH
+
+wget https://github.com/Perl/perl5/archive/refs/tags/v5.28.0.tar.gz
+tar -xvzf v5.28.0.tar.gz
+# shellcheck disable=SC2164
+pushd perl5-5.28.0/
+./Configure -des -Dprefix=/opt/local/
+make -j 16
+make install
+# shellcheck disable=SC2164
+popd
+
+wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1w.tar.gz
+tar -xvzf openssl-1.1.1w.tar.gz
+# shellcheck disable=SC2164
+pushd openssl-1.1.1w
+./config --prefix=/opt/local
+make -j 16
+make install
+# shellcheck disable=SC2164
+popd
+
+# 准备python3.9.7
+yum -y install libffi-devel.aarch64 bzip2-devel.aarch64 zlib-devel.aarch64 readline-devel gdbm-devel tk-devel.aarch64 uuid.aarch64 sqlite-devel.aarch64 ncurses-devel.aarch64 xz-devel.aarch64
+wget https://mirrors.huaweicloud.com/python/3.9.7/Python-3.9.7.tgz
+tar -xvzf Python-3.9.7.tgz
+# shellcheck disable=SC2164
+pushd /home/package/Python-3.9.7
+./configure -prefix=/opt/python3 -with-openssl=/opt/local/ -enable-optimizations -enable-shared
+make -j 4
+make install
+# shellcheck disable=SC2164
+popd
+
+export LD_LIBRARY_PATH=/opt/python3/lib:/opt/local/lib:$LD_LIBRARY_PATH
+export PATH=/opt/python3/bin:/opt/local/bin:$PATH
+
+pip3 config set global.index-url https://mirrors.huaweicloud.com/repository/pypi/simple
+pip3 install Pyinstaller
+pip3 install urllib3
+pip3 install requests
+pip3 install paramiko
+
+pip3 install wget
+pip3 install pyyaml
+pip3 install timeout_decorator
+
diff --git a/ci/env_for_users_build.sh b/ci/env_for_users_build.sh
new file mode 100644
index 0000000000000000000000000000000000000000..73ac07aae683bf8aeffac2e3e89a8c750c640f9d
--- /dev/null
+++ b/ci/env_for_users_build.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# shellcheck disable=SC2129
+# shellcheck disable=SC2016
+echo 'export PATH=/opt/apache-maven-3.9.6/bin:$PATH' >>~/.bashrc
+echo 'export PATH=/opt/python3/bin:/opt/local/bin:$PATH' >>~/.bashrc
+echo 'export LD_LIBRARY_PATH=/opt/python3/lib:/opt/local/lib:$LD_LIBRARY_PATH' >>~/.bashrc
+
+mkdir "${HOME}/.m2"
+cp maven3/settings.xml "${HOME}/.m2/settings.xml"
\ No newline at end of file
diff --git a/ci/expect/install_rust.sh b/ci/expect/install_rust.sh
new file mode 100644
index 0000000000000000000000000000000000000000..88c2f2ab550d1eea0a09c0c6abcbe5a258eaa2f9
--- /dev/null
+++ b/ci/expect/install_rust.sh
@@ -0,0 +1,18 @@
+#!/bin/expect
+
+set timeout 30
+
+# shellcheck disable=SC2121
+set RUSTUP_DIST_SERVER https://mirrors.ustc.edu.cn/rust-static
+# shellcheck disable=SC2121
+set RUSTUP_UPDATE_ROOT https://mirrors.ustc.edu.cn/rust-static/rustup
+
+spawn bash rust.sh
+expect "Cancel installation"
+send "1"
+
+expect "Cancel installation"
+send "Y"
+
+expect eof
+exit
\ No newline at end of file
diff --git a/ci/maven3/settings.xml b/ci/maven3/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9b916d37386dccce0e4d1c98db4590da81fe845e
--- /dev/null
+++ b/ci/maven3/settings.xml
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ maven-default-http-blocker
+ external:http:*
+ Pseudo repository to mirror external repositories initially using HTTP.
+ http://0.0.0.0/
+ true
+
+
+ jboss
+ *
+ https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/component/DevKitTester/JFRParser/build.sh b/component/DevKitTester/JFRParser/build.sh
new file mode 100644
index 0000000000000000000000000000000000000000..b9e1283b648563190366f8148b3664a3f3f8edc8
--- /dev/null
+++ b/component/DevKitTester/JFRParser/build.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+current_dir=$(cd $(dirname "$0"); pwd)
+
+echo "${current_dir}"
+
+pushd "$current_dir"
+
+mvn clean package
+
+popd
\ No newline at end of file
diff --git a/component/DevKitTester/JFRParser/pom.xml b/component/DevKitTester/JFRParser/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..36289eebb1d5a7920afed2e5475d41e31a8b8596
--- /dev/null
+++ b/component/DevKitTester/JFRParser/pom.xml
@@ -0,0 +1,105 @@
+
+
+ 4.0.0
+
+ com.huawei
+ JFRParser
+ 1.0-SNAPSHOT
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.19.0
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.19.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.16.1
+
+
+ args4j
+ args4j
+ 2.33
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.9.2
+ test
+
+
+ org.mockito
+ mockito-core
+ 4.11.0
+ test
+
+
+ org.jetbrains
+ annotations
+ 24.0.0
+ compile
+
+
+
+
+
+
+ org.codehaus.mojo
+ appassembler-maven-plugin
+ 1.2.1
+
+
+ make-assembly
+ package
+
+ assemble
+
+
+
+
+
+ flat
+ config
+ src/main/resources
+ true
+ true
+
+ ${project.build.directory}/JFRParser
+
+ -Xms128m
+
+ .sh
+
+
+ unix
+
+ lib
+
+
+
+ com.huawei.devkit.pipeline.Main
+
+ generate_jmeter_result
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/Main.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..155a65ccfa9ad3291feaeac4dbbd81fec852222d
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/Main.java
@@ -0,0 +1,60 @@
+package com.huawei.devkit.pipeline;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.huawei.devkit.pipeline.bo.CommandLineParams;
+import com.huawei.devkit.pipeline.bo.LatencyTopInfo;
+import com.huawei.devkit.pipeline.bo.PerformanceTestResult;
+import com.huawei.devkit.pipeline.parser.JFRParser;
+import com.huawei.devkit.pipeline.parser.JmeterResultParser;
+import com.huawei.devkit.pipeline.parser.ParamsParser;
+import com.huawei.devkit.pipeline.utils.Top10RT;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.huawei.devkit.pipeline.constants.JFRConstants.TOTAL_LABEL;
+
+public class Main {
+ private static final Logger logger = LogManager.getLogger(Main.class);
+
+ public static void main(String[] args) {
+ try {
+ CommandLineParams params = ParamsParser.parse(args);
+ params.fillMaps();
+
+ PerformanceTestResult result = new PerformanceTestResult();
+ // jmeter结果解析
+ logger.info("start to parse jmeter result !!!");
+ JmeterResultParser.parse(params.getJmeterResult(), result);
+ logger.info("finish to parse jmeter result !!!");
+
+ // jfr解析
+ logger.info("start to parse jfr !!!");
+ List latencyKes = Top10RT.getTopTen(result.getRtMap().get(TOTAL_LABEL), Top10RT.TOP10);
+ for (Map.Entry> entry : params.getJfrPathMap().entrySet()) {
+ String gap = params.getNodesTimeGapMap().get(entry.getKey());
+ int timeGap = gap == null ? 0 : Integer.parseInt(gap);
+ result.getCpuMap().put(entry.getKey(), new HashMap<>());
+ result.getMemoryMap().put(entry.getKey(), new HashMap<>());
+ for (String jfrPath : entry.getValue()) {
+ JFRParser.parse(jfrPath, latencyKes, timeGap, result, entry.getKey());
+ }
+ }
+ result.toStandardFlames();
+ result.toSimpleObject();
+ logger.info("finish to parse jfr !!!");
+
+ //数据持久化
+ ObjectMapper mapper = new ObjectMapper();
+ File file = new File(params.getOutput() + "/result.json");
+ mapper.writeValue(file, result);
+ logger.info("the end");
+ } catch (Exception ex) {
+ logger.error(ex.getMessage(), ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/CommandLineParams.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/CommandLineParams.java
new file mode 100644
index 0000000000000000000000000000000000000000..206d48ca6220f73e331bf220d0418d453510a760
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/CommandLineParams.java
@@ -0,0 +1,108 @@
+package com.huawei.devkit.pipeline.bo;
+
+import com.huawei.devkit.pipeline.strategy.MultiHandlerFactory;
+import org.kohsuke.args4j.Option;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class CommandLineParams {
+
+ @Option(name = "-j", usage = "the jmeter result path", metaVar = "jmeter_result_path", required = true)
+ private String jmeterResult;
+
+ @Option(name = "-o", usage = "the output path", metaVar = "output", required = true)
+ private String output;
+
+ @Option(name = "-n", usage = "the jfr path. example 127.0.0.1:+154,127.0.0.2:-58421",
+ metaVar = "display", required = true,
+ handler = MultiHandlerFactory.MultiFieldOptionHandler.class)
+ private List nodeTimeGaps;
+
+ private Map nodesTimeGapMap;
+
+ @Option(name = "-f", usage = "the jfr path. example 127.0.0.1:app.jfr,127.0.0.1:app2.jfr,127.0.0.2:app.jfr",
+ metaVar = "display", required = true,
+ handler = MultiHandlerFactory.MultiFieldOptionHandler.class)
+ private List jfrPaths;
+
+ private Map> jfrPathMap;
+
+ public CommandLineParams() {
+ this.jmeterResult = "";
+ this.output = "";
+ this.nodeTimeGaps = new ArrayList<>();
+ this.nodesTimeGapMap = new HashMap<>();
+ this.jfrPathMap = new HashMap<>();
+ this.jfrPaths = new ArrayList<>();
+ }
+
+ public void fillMaps() {
+ for (String argument : jfrPaths) {
+ String[] values = argument.split(":");
+ List jfrs = jfrPathMap.get(values[0]);
+ if (Objects.isNull(jfrs)) {
+ jfrs = new ArrayList<>();
+ jfrPathMap.put(values[0], jfrs);
+ }
+ jfrs.add(values[1]);
+ }
+
+ for (String argument : nodeTimeGaps) {
+ String[] values = argument.split(":");
+ nodesTimeGapMap.put(values[0], values[1]);
+ }
+ }
+
+ public String getJmeterResult() {
+ return jmeterResult;
+ }
+
+ public void setJmeterResult(String jmeterResult) {
+ this.jmeterResult = jmeterResult;
+ }
+
+ public String getOutput() {
+ return output;
+ }
+
+ public void setOutput(String output) {
+ this.output = output;
+ }
+
+ public List getNodeTimeGaps() {
+ return nodeTimeGaps;
+ }
+
+ public void setNodeTimeGaps(List nodeTimeGaps) {
+ this.nodeTimeGaps = nodeTimeGaps;
+ }
+
+ public Map getNodesTimeGapMap() {
+ return nodesTimeGapMap;
+ }
+
+ public void setNodesTimeGapMap(Map nodesTimeGapMap) {
+ this.nodesTimeGapMap = nodesTimeGapMap;
+ }
+
+ public List getJfrPaths() {
+ return jfrPaths;
+ }
+
+ public void setJfrPaths(List jfrPaths) {
+ this.jfrPaths = jfrPaths;
+ }
+
+ public Map> getJfrPathMap() {
+ return jfrPathMap;
+ }
+
+ public void setJfrPathMap(Map> jfrPathMap) {
+ this.jfrPathMap = jfrPathMap;
+ }
+}
+
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/CpuInfo.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/CpuInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..b37e5d436033b337b0db8902900b02d1e70c7d7b
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/CpuInfo.java
@@ -0,0 +1,54 @@
+package com.huawei.devkit.pipeline.bo;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CpuInfo {
+ @JsonProperty("t")
+ private long startTime;
+ @JsonProperty("s")
+ private float jvmSystem;
+ @JsonProperty("u")
+ private float jvmUser;
+ @JsonProperty("m")
+ private float machineTotal;
+
+ public CpuInfo(long startTime, float jvmSystem, float jvmUser, float machineTotal) {
+ this.startTime = startTime;
+ this.jvmSystem = jvmSystem;
+ this.jvmUser = jvmUser;
+ this.machineTotal = machineTotal;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public float getJvmSystem() {
+ return jvmSystem;
+ }
+
+ public void setJvmSystem(float jvmSystem) {
+ this.jvmSystem = jvmSystem;
+ }
+
+ public float getJvmUser() {
+ return jvmUser;
+ }
+
+ public void setJvmUser(float jvmUser) {
+ this.jvmUser = jvmUser;
+ }
+
+ public float getMachineTotal() {
+ return machineTotal;
+ }
+
+ public void setMachineTotal(float machineTotal) {
+ this.machineTotal = machineTotal;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/FlameDiagram.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/FlameDiagram.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5e98b538f3aa9f82eeffa7a8cd55669ffb24eb1
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/FlameDiagram.java
@@ -0,0 +1,9 @@
+package com.huawei.devkit.pipeline.bo;
+
+
+import java.util.List;
+import java.util.Map;
+
+public class FlameDiagram {
+ private Map> flame;
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/FlameItem.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/FlameItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a3f2744cc44560e9b66c56d94494aa94fd09d84
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/FlameItem.java
@@ -0,0 +1,100 @@
+package com.huawei.devkit.pipeline.bo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.huawei.devkit.pipeline.utils.JfrMethodSignatureParser;
+import jdk.jfr.consumer.RecordedFrame;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class FlameItem {
+ public FlameItem(String name, int value) {
+ this.name = name;
+ this.value = value;
+ this.subMap = new HashMap<>();
+ }
+
+ @JsonProperty("n")
+ private String name;
+ @JsonProperty("v")
+ private int value;
+ @JsonProperty("c")
+ private Collection sub;
+
+ @JsonIgnore
+ private final Map subMap;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+ public void increase() {
+ this.value++;
+ }
+
+ public Collection getSub() {
+ return sub;
+ }
+
+ public void setSub(Collection sub) {
+ this.sub = sub;
+ }
+
+ public Map getSubMap() {
+ return subMap;
+ }
+
+ public void toStandardFlame() {
+ this.sub = this.subMap.values();
+ for (FlameItem item : this.sub) {
+ item.toStandardFlame();
+ }
+ }
+
+ public void addSubFlameItem(FlameItem item) {
+ this.subMap.put(item.getName(), item);
+ this.value += item.getValue();
+ }
+
+ public void addFlameItemByRecordedFrame(List frames, String nodeIP, String filename) {
+ Map loopMap = subMap;
+ loopMap = addFlameOneItem(nodeIP, loopMap);
+ loopMap = addFlameOneItem(filename, loopMap);
+ for (int i = frames.size() - 1; i >= 0; i--) {
+ RecordedFrame frame = frames.get(i);
+ String methodName = frame.getMethod().getType().getName() + "." + frame.getMethod().getName();
+ String name = JfrMethodSignatureParser
+ .convertMethodSignatureWithoutReturnType(frame.getMethod().getDescriptor(), methodName);
+ loopMap = addFlameOneItem(name, loopMap);
+ }
+ this.increase();
+ }
+
+ private static Map addFlameOneItem(String label, Map loopMap) {
+ FlameItem item = loopMap.get(label);
+ if (item != null) {
+ item.increase();
+ } else {
+ item = new FlameItem(label, 1);
+ loopMap.put(label, item);
+ }
+ loopMap = item.getSubMap();
+ return loopMap;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterRT.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterRT.java
new file mode 100644
index 0000000000000000000000000000000000000000..651c6dde1905906944e5d1f1c60428f4a1645322
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterRT.java
@@ -0,0 +1,32 @@
+package com.huawei.devkit.pipeline.bo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class JmeterRT {
+ @JsonIgnore
+ private Long startTime;
+ @JsonProperty("s")
+ private Double responseTime;
+
+ public JmeterRT(long startTime, Double responseTime) {
+ this.startTime = startTime;
+ this.responseTime = responseTime;
+ }
+
+ public Long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(Long startTime) {
+ this.startTime = startTime;
+ }
+
+ public Double getResponseTime() {
+ return responseTime;
+ }
+
+ public void setResponseTime(Double responseTime) {
+ this.responseTime = responseTime;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterReportSummary.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterReportSummary.java
new file mode 100644
index 0000000000000000000000000000000000000000..01417eb367cd4f302db9643a32e9adb199fa337d
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterReportSummary.java
@@ -0,0 +1,126 @@
+package com.huawei.devkit.pipeline.bo;
+
+public class JmeterReportSummary {
+
+ private String label;
+
+ private long samples;
+
+ private long failSamples;
+
+ private double averageLatency;
+
+ private long minLatency;
+
+ private long maxLatency;
+
+ private long median;
+
+ private long latency99;
+
+ private long latency95;
+
+ private long latency90;
+
+ private long throughput;
+
+ public JmeterReportSummary(String label) {
+ this.label = label;
+ }
+
+ public void samplesIncrease() {
+ this.samples++;
+ }
+
+ public void failSamplesIncrease() {
+ this.failSamples++;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public long getSamples() {
+ return samples;
+ }
+
+ public void setSamples(long samples) {
+ this.samples = samples;
+ }
+
+ public long getFailSamples() {
+ return failSamples;
+ }
+
+ public void setFailSamples(long failSamples) {
+ this.failSamples = failSamples;
+ }
+
+ public double getAverageLatency() {
+ return averageLatency;
+ }
+
+ public void setAverageLatency(double averageLatency) {
+ this.averageLatency = averageLatency;
+ }
+
+ public long getMinLatency() {
+ return minLatency;
+ }
+
+ public void setMinLatency(long minLatency) {
+ this.minLatency = minLatency;
+ }
+
+ public long getMaxLatency() {
+ return maxLatency;
+ }
+
+ public void setMaxLatency(long maxLatency) {
+ this.maxLatency = maxLatency;
+ }
+
+ public long getMedian() {
+ return median;
+ }
+
+ public void setMedian(long median) {
+ this.median = median;
+ }
+
+ public long getLatency99() {
+ return latency99;
+ }
+
+ public void setLatency99(long latency99) {
+ this.latency99 = latency99;
+ }
+
+ public long getLatency95() {
+ return latency95;
+ }
+
+ public void setLatency95(long latency95) {
+ this.latency95 = latency95;
+ }
+
+ public long getLatency90() {
+ return latency90;
+ }
+
+ public void setLatency90(long latency90) {
+ this.latency90 = latency90;
+ }
+
+ public long getThroughput() {
+ return throughput;
+ }
+
+ public void setThroughput(long throughput) {
+ this.throughput = throughput;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterResult.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..8fb37d1079a9d03f22e586213f2e7a4499a107f5
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterResult.java
@@ -0,0 +1,47 @@
+package com.huawei.devkit.pipeline.bo;
+
+public class JmeterResult {
+ private long startTime;
+ private int responseCode;
+ private int latency;
+ private String label;
+
+ public JmeterResult(long startTime, int responseCode, int latency, String label) {
+ this.startTime = startTime;
+ this.responseCode = responseCode;
+ this.latency = latency;
+ this.label = label;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+
+ public int getLatency() {
+ return latency;
+ }
+
+ public void setLatency(int latency) {
+ this.latency = latency;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterTPS.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterTPS.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b5d1c2d64d5247bffe3fe17bf6adf44bd933aff
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/JmeterTPS.java
@@ -0,0 +1,17 @@
+package com.huawei.devkit.pipeline.bo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class JmeterTPS {
+ @JsonIgnore
+ private Long startTime;
+ @JsonProperty("s")
+ private int tps;
+
+ public JmeterTPS(long startTime, int tps) {
+ this.startTime = startTime;
+ this.tps = tps;
+ }
+
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/LatencyTopInfo.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/LatencyTopInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7002dce07f6b7d8a3a2e8b78271700e84be6d52
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/LatencyTopInfo.java
@@ -0,0 +1,49 @@
+package com.huawei.devkit.pipeline.bo;
+
+public class LatencyTopInfo {
+ private long startTime;
+ private long endTime;
+ private long key;
+
+ private FlameItem flame;
+
+ public LatencyTopInfo(long startTime, long endTime, long key) {
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.key = key;
+ this.flame = new FlameItem("all", 0);
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public long getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(long endTime) {
+ this.endTime = endTime;
+ }
+
+ public long getKey() {
+ return key;
+ }
+
+ public void setKey(long key) {
+ this.key = key;
+ }
+
+ public FlameItem getFlame() {
+ return flame;
+ }
+
+ public void setFlame(FlameItem flame) {
+ this.flame = flame;
+ }
+
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/MemInfo.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/MemInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..eef71220312b69535628d7225a6e686e45029ba0
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/MemInfo.java
@@ -0,0 +1,53 @@
+package com.huawei.devkit.pipeline.bo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MemInfo {
+ @JsonProperty("t")
+ private long startTime;
+ @JsonProperty("c")
+ private long committedSize;
+ @JsonProperty("r")
+ private long reservedSize;
+ @JsonProperty("u")
+ private long heapUsed;
+
+ public MemInfo(long startTime, long committedSize, long reservedSize, long heapUsed) {
+ this.startTime = startTime;
+ this.committedSize = committedSize;
+ this.reservedSize = reservedSize;
+ this.heapUsed = heapUsed;
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public long getCommittedSize() {
+ return committedSize;
+ }
+
+ public void setCommittedSize(long committedSize) {
+ this.committedSize = committedSize;
+ }
+
+ public long getReservedSize() {
+ return reservedSize;
+ }
+
+ public void setReservedSize(long reservedSize) {
+ this.reservedSize = reservedSize;
+ }
+
+ public long getHeapUsed() {
+ return heapUsed;
+ }
+
+ public void setHeapUsed(long heapUsed) {
+ this.heapUsed = heapUsed;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/PerformanceTestResult.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/PerformanceTestResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..226cb208ed465bf56f61646dc1abee656e1d3c4f
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/bo/PerformanceTestResult.java
@@ -0,0 +1,206 @@
+package com.huawei.devkit.pipeline.bo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.huawei.devkit.pipeline.constants.JFRConstants;
+import com.huawei.devkit.pipeline.parser.JFRParser;
+import com.huawei.devkit.pipeline.utils.SimplifyResponse;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class PerformanceTestResult {
+
+ private List summaries;
+
+ private List startTime;
+
+ @JsonIgnore
+ private Map> rtMap;
+
+ private Map>> rt;
+
+ @JsonIgnore
+ private Map> frtMap;
+
+ private Map>> frt;
+
+ @JsonIgnore
+ private Map> tpsMap;
+
+ private Map>> tps;
+
+ @JsonIgnore
+ private Map>> memoryMap;
+
+ private Map>>> memory;
+
+ @JsonIgnore
+ private Map>> cpuMap;
+
+ private Map>>> cpu;
+
+ private Map flame;
+
+ public PerformanceTestResult() {
+ this.summaries = new ArrayList<>();
+ this.startTime = new ArrayList<>();
+ this.rtMap = new HashMap<>();
+ this.rt = new HashMap<>();
+ this.frtMap = new HashMap<>();
+ this.frt = new HashMap<>();
+ this.tpsMap = new HashMap<>();
+ this.tps = new HashMap<>();
+ this.memoryMap = new HashMap<>();
+ this.memory = new HashMap<>();
+ this.cpuMap = new HashMap<>();
+ this.cpu = new HashMap<>();
+ this.flame = new HashMap<>();
+ // 初始化火焰图root
+ this.flame.put(JFRParser.ALL, new FlameItem("all", 0));
+ }
+
+ /**
+ * 简化返回的响应结果,进一部节省字节
+ */
+ public void toSimpleObject() {
+ List totalRTS = this.rtMap.get(JFRConstants.TOTAL_LABEL);
+ if (totalRTS != null) {
+ this.startTime = totalRTS.stream().map(JmeterRT::getStartTime).collect(Collectors.toList());
+ this.toSimpleObject(this.rtMap, this.rt, JmeterRT.class);
+ this.toSimpleObject(this.frtMap, this.frt, JmeterRT.class);
+ this.toSimpleObject(this.tpsMap, this.tps, JmeterTPS.class);
+ }
+ this.toSimpleObject2(this.memoryMap, this.memory, MemInfo.class);
+ this.toSimpleObject2(this.cpuMap, this.cpu, CpuInfo.class);
+ }
+
+ public void toStandardFlames() {
+ for (FlameItem item : this.flame.values()) {
+ item.toStandardFlame();
+ }
+ }
+
+ public List getSummaries() {
+ return summaries;
+ }
+
+ public void setSummaries(List summaries) {
+ this.summaries = summaries;
+ }
+
+ public Map> getRtMap() {
+ return rtMap;
+ }
+
+ public void setRtMap(Map> rtMap) {
+ this.rtMap = rtMap;
+ }
+
+ public Map> getFrtMap() {
+ return frtMap;
+ }
+
+ public void setFrtMap(Map> frtMap) {
+ this.frtMap = frtMap;
+ }
+
+ public Map> getTpsMap() {
+ return tpsMap;
+ }
+
+ public void setTpsMap(Map> tpsMap) {
+ this.tpsMap = tpsMap;
+ }
+
+ public Map getFlame() {
+ return flame;
+ }
+
+ public void setFlame(Map flame) {
+ this.flame = flame;
+ }
+
+ public Map>> getMemoryMap() {
+ return memoryMap;
+ }
+
+ public void setMemoryMap(Map>> memoryMap) {
+ this.memoryMap = memoryMap;
+ }
+
+ public Map>>> getMemory() {
+ return memory;
+ }
+
+ public void setMemory(Map>>> memory) {
+ this.memory = memory;
+ }
+
+ public Map>> getCpuMap() {
+ return cpuMap;
+ }
+
+ public void setCpuMap(Map>> cpuMap) {
+ this.cpuMap = cpuMap;
+ }
+
+ public Map>>> getCpu() {
+ return cpu;
+ }
+
+ public void setCpu(Map>>> cpu) {
+ this.cpu = cpu;
+ }
+
+ public Map>> getRt() {
+ return rt;
+ }
+
+ public void setRt(Map>> rt) {
+ this.rt = rt;
+ }
+
+ public Map>> getFrt() {
+ return frt;
+ }
+
+ public void setFrt(Map>> frt) {
+ this.frt = frt;
+ }
+
+ public Map>> getTps() {
+ return tps;
+ }
+
+ public void setTps(Map>> tps) {
+ this.tps = tps;
+ }
+
+ public List getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(List startTime) {
+ this.startTime = startTime;
+ }
+
+ private void toSimpleObject(Map> origin, Map>> target, Class clazz) {
+ for (Map.Entry> entry : origin.entrySet()) {
+ target.put(entry.getKey(), SimplifyResponse.simplify(entry.getValue(), clazz));
+ }
+ }
+
+ private void toSimpleObject2(Map>> origin,
+ Map>>> target, Class clazz) {
+ for (Map.Entry>> entry : origin.entrySet()) {
+ Map>> targetItem = new HashMap<>();
+ for (Map.Entry> inner : entry.getValue().entrySet()) {
+ targetItem.put(inner.getKey(), SimplifyResponse.simplify(inner.getValue(), clazz));
+ }
+ target.put(entry.getKey(), targetItem);
+ }
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/constants/JFRConstants.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/constants/JFRConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a9f3692a4d828e58fc78cfd1eb67ef1d7ce6bf8
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/constants/JFRConstants.java
@@ -0,0 +1,11 @@
+package com.huawei.devkit.pipeline.constants;
+
+public class JFRConstants {
+ public final static int MS_TO_S = 1000;
+
+ public final static int MS_1000 = 1000;
+
+ public final static String TOTAL_LABEL = "Total";
+
+
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/JFRParser.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/JFRParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca3bcb33bf0afc76a54476269fc80ca03e18297a
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/JFRParser.java
@@ -0,0 +1,83 @@
+package com.huawei.devkit.pipeline.parser;
+
+import com.huawei.devkit.pipeline.bo.CpuInfo;
+import com.huawei.devkit.pipeline.bo.FlameItem;
+import com.huawei.devkit.pipeline.bo.LatencyTopInfo;
+import com.huawei.devkit.pipeline.bo.MemInfo;
+import com.huawei.devkit.pipeline.bo.PerformanceTestResult;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.consumer.RecordedStackTrace;
+import jdk.jfr.consumer.RecordingFile;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class JFRParser {
+ public static Long ALL = -1L;
+
+ private static final String GC_HEAP_SUMMARY_EVENT = "jdk.GCHeapSummary";
+ private static final String EXECUTION_SAMPLE_EVENT = "jdk.ExecutionSample";
+ private static final String NATIVE_METHOD_SAMPLE_EVENT = "jdk.NativeMethodSample";
+ private static final String CPU_LOAD_EVENT = "jdk.CPULoad";
+
+ public static void parse(String filePath, List top10,
+ int timeGap, PerformanceTestResult result, String nodeIP) throws Exception {
+ Path path = Paths.get(filePath);
+ if (!Files.exists(path)) {
+ throw new Exception("the file not exist");
+ }
+
+ List memInfos = new ArrayList<>();
+ List cpuInfos = new ArrayList<>();
+ String fileName = path.getFileName().toString();
+ FlameItem flame = result.getFlame().get(ALL);
+ try (RecordingFile file = new RecordingFile(path)) {
+ while (file.hasMoreEvents()) {
+ RecordedEvent event = file.readEvent();
+ long startTime = event.getStartTime().toEpochMilli() + timeGap;
+ if (event.getEventType().getName().equals(GC_HEAP_SUMMARY_EVENT)) {
+ RecordedObject headSpace = event.getValue("heapSpace");
+ long committedSize = headSpace.getLong("committedSize");
+ long reservedSize = headSpace.getLong("reservedSize");
+ long heapUsed = event.getLong("heapUsed");
+ memInfos.add(new MemInfo(startTime, committedSize, reservedSize, heapUsed));
+ } else if (event.getEventType().getName().equals(EXECUTION_SAMPLE_EVENT)) {
+ RecordedStackTrace stackTrace = event.getStackTrace();
+ flame.addFlameItemByRecordedFrame(stackTrace.getFrames(), nodeIP, fileName);
+ addDurationFlame(startTime, stackTrace.getFrames(), top10, nodeIP, fileName);
+ } else if (event.getEventType().getName().equals(NATIVE_METHOD_SAMPLE_EVENT)) {
+ RecordedStackTrace stackTrace = event.getStackTrace();
+ flame.addFlameItemByRecordedFrame(stackTrace.getFrames(), nodeIP, fileName);
+ addDurationFlame(startTime, stackTrace.getFrames(), top10, nodeIP, fileName);
+ } else if (event.getEventType().getName().equals(CPU_LOAD_EVENT)) {
+ float jvmSystem = event.getFloat("jvmSystem");
+ float jvmUser = event.getFloat("jvmUser");
+ float machineTotal = event.getFloat("machineTotal");
+ cpuInfos.add(new CpuInfo(startTime, jvmSystem, jvmUser, machineTotal));
+ }
+ }
+ }
+ Map> cpuMap = result.getCpuMap().get(nodeIP);
+ cpuMap.put(fileName, cpuInfos);
+ Map> memoryMap = result.getMemoryMap().get(nodeIP);
+ memoryMap.put(fileName, memInfos);
+ for (LatencyTopInfo latencyTop : top10) {
+ result.getFlame().put(latencyTop.getKey(), latencyTop.getFlame());
+ }
+ }
+
+ private static void addDurationFlame(long startTime, List frames, List top10,
+ String nodeIP, String filename) {
+ for (LatencyTopInfo latencyTop : top10) {
+ if (startTime > latencyTop.getStartTime() && startTime < latencyTop.getEndTime()) {
+ latencyTop.getFlame().addFlameItemByRecordedFrame(frames, nodeIP, filename);
+ }
+ }
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/JmeterResultParser.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/JmeterResultParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..f277eaff1dc5ba05440c8f106116e6ef191dfdd1
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/JmeterResultParser.java
@@ -0,0 +1,184 @@
+package com.huawei.devkit.pipeline.parser;
+
+import com.huawei.devkit.pipeline.bo.JmeterRT;
+import com.huawei.devkit.pipeline.bo.JmeterReportSummary;
+import com.huawei.devkit.pipeline.bo.JmeterResult;
+import com.huawei.devkit.pipeline.bo.JmeterTPS;
+import com.huawei.devkit.pipeline.bo.PerformanceTestResult;
+import com.huawei.devkit.pipeline.constants.JFRConstants;
+import com.huawei.devkit.pipeline.utils.JmeterResultTransfer;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.huawei.devkit.pipeline.constants.JFRConstants.TOTAL_LABEL;
+
+public class JmeterResultParser {
+ private static final Logger logger = LogManager.getLogger(JmeterResultParser.class);
+
+ private static final int MIN_ERROR_CODE = 400;
+ private static final int PERCENT_50 = 50;
+ private static final int PERCENT_90 = 90;
+ private static final int PERCENT_95 = 95;
+ private static final int PERCENT_99 = 99;
+
+ private JmeterReportSummary summary;
+
+ private List rtList;
+
+ private List frtList;
+
+ private List tpsList;
+
+ public JmeterResultParser(String label) {
+ this.summary = new JmeterReportSummary(label);
+ this.rtList = new ArrayList<>(1000);
+ this.frtList = new ArrayList<>(1000);
+ this.tpsList = new ArrayList<>(1000);
+ }
+
+ public static void parse(String resultPath, PerformanceTestResult result) throws Exception {
+ List results = JmeterResultTransfer.transfer(resultPath);
+ if (results.isEmpty()) {
+ result.getSummaries().add(new JmeterReportSummary(TOTAL_LABEL));
+ return;
+ }
+ // 解决多个线程造成的数据错误问题
+ results = results.parallelStream().sorted(Comparator.comparingLong(JmeterResult::getStartTime))
+ .collect(Collectors.toList());
+ long startTime = results.get(0).getStartTime();
+ long endTime = getEndTime(startTime, results.get(results.size() - 1).getStartTime());
+
+ JmeterResultParser parser = new JmeterResultParser(TOTAL_LABEL);
+ parser.calcTPSAndRT(results, startTime, endTime);
+ result.getSummaries().add(parser.getSummary());
+ result.getRtMap().put(TOTAL_LABEL, parser.getRtList());
+ result.getFrtMap().put(TOTAL_LABEL, parser.getFrtList());
+ result.getTpsMap().put(TOTAL_LABEL, parser.getTpsList());
+ Map> map = results.stream().collect(Collectors.groupingBy(JmeterResult::getLabel));
+ for (Map.Entry> entry : map.entrySet()) {
+ JmeterResultParser parserPer = new JmeterResultParser(entry.getKey());
+ parserPer.calcTPSAndRT(entry.getValue(), startTime, endTime);
+ result.getSummaries().add(parserPer.getSummary());
+ result.getRtMap().put(entry.getKey(), parserPer.getRtList());
+ result.getFrtMap().put(entry.getKey(), parserPer.getFrtList());
+ result.getTpsMap().put(entry.getKey(), parserPer.getTpsList());
+ }
+ }
+
+ private void calcTPSAndRT(List results, long startTime, long endTime) {
+ if (Objects.isNull(results) || results.isEmpty()) {
+ return;
+ }
+
+ int samplePer = 0;
+ int latencyTotalPerSec = 0;
+ int latencyFailPerSec = 0;
+ long latencyTotal = 0;
+
+ long start = startTime;
+ long end = startTime + JFRConstants.MS_1000;
+
+ boolean exist = false;
+ for (int i = 0; start < endTime; start += JFRConstants.MS_1000, end += JFRConstants.MS_1000) {
+
+ while (i < results.size() && results.get(i).getStartTime() >= start && results.get(i).getStartTime() < end) {
+ summary.samplesIncrease();
+ latencyTotalPerSec += results.get(i).getLatency();
+ latencyTotal += results.get(i).getLatency();
+ samplePer++;
+ if (results.get(i).getResponseCode() >= MIN_ERROR_CODE) {
+ summary.failSamplesIncrease();
+ latencyFailPerSec += results.get(i).getLatency();
+ }
+ i++;
+ exist = true;
+ }
+ if (exist) {
+ rtList.add(new JmeterRT(start, latencyTotalPerSec / (double) samplePer));
+ frtList.add(new JmeterRT(start, latencyFailPerSec / (double) samplePer));
+ tpsList.add(new JmeterTPS(start, samplePer));
+ } else {
+ rtList.add(new JmeterRT(start, null));
+ frtList.add(new JmeterRT(start, null));
+ tpsList.add(new JmeterTPS(start, 0));
+ }
+ // re init
+ latencyTotalPerSec = 0;
+ samplePer = 0;
+ latencyFailPerSec = 0;
+ exist = false;
+ }
+ summary.setThroughput(summary.getSamples() * JFRConstants.MS_TO_S /
+ getThroughputTime(startTime, results.get(results.size() - 1).getStartTime()));
+ summary.setAverageLatency(latencyTotal / (double) summary.getSamples());
+ this.filledSummary(results);
+ }
+
+ public JmeterReportSummary getSummary() {
+ return summary;
+ }
+
+ public void setSummary(JmeterReportSummary summary) {
+ this.summary = summary;
+ }
+
+ public List getRtList() {
+ return rtList;
+ }
+
+ public void setRtList(List rtList) {
+ this.rtList = rtList;
+ }
+
+ public List getFrtList() {
+ return frtList;
+ }
+
+ public void setFrtList(List frtList) {
+ this.frtList = frtList;
+ }
+
+ public List getTpsList() {
+ return tpsList;
+ }
+
+ public void setTpsList(List tpsList) {
+ this.tpsList = tpsList;
+ }
+
+ private void filledSummary(List results) {
+ List jmeterResults = results.stream()
+ .sorted(Comparator.comparingLong(JmeterResult::getLatency)).collect(Collectors.toList());
+ JmeterResult result = jmeterResults.get(0);
+ summary.setMinLatency(result.getLatency());
+ summary.setMaxLatency(jmeterResults.get(jmeterResults.size() - 1).getLatency());
+ int position50 = jmeterResults.size() * PERCENT_50 / 100 - 1;
+ summary.setMedian(jmeterResults.get(Math.max(position50, 0)).getLatency());
+ int position90 = jmeterResults.size() * PERCENT_90 / 100 - 1;
+ summary.setLatency90(jmeterResults.get(Math.max(position90, 0)).getLatency());
+ int position95 = jmeterResults.size() * PERCENT_95 / 100 - 1;
+ summary.setLatency95(jmeterResults.get(Math.max(position95, 0)).getLatency());
+ int position99 = jmeterResults.size() * PERCENT_99 / 100 - 1;
+ summary.setLatency99(jmeterResults.get(Math.max(position99, 0)).getLatency());
+ }
+
+ private static long getEndTime(long startTime, long lastTime) {
+ long duration = (lastTime - startTime) / JFRConstants.MS_1000 + 1;
+ return duration * JFRConstants.MS_1000 + startTime;
+ }
+
+ private static long getThroughputTime(long startTime, long lastTime) {
+ if (lastTime > startTime) {
+ return lastTime - startTime;
+ } else {
+ return JFRConstants.MS_1000;
+ }
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/ParamsParser.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/ParamsParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..0aae83b1458ff87ac208003ca20769646d5fd7d7
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/parser/ParamsParser.java
@@ -0,0 +1,20 @@
+package com.huawei.devkit.pipeline.parser;
+
+import com.huawei.devkit.pipeline.bo.CommandLineParams;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+
+public class ParamsParser {
+ public static CommandLineParams parse(String[] args) throws CmdLineException {
+ CommandLineParams params = new CommandLineParams();
+ CmdLineParser parser = new CmdLineParser(params);
+ try {
+ parser.parseArgument(args);
+ return params;
+ } catch (CmdLineException ex) {
+ System.err.println(ex.getMessage());
+ new CmdLineParser(new CommandLineParams()).printUsage(System.err);
+ throw ex;
+ }
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/strategy/MultiHandlerFactory.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/strategy/MultiHandlerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..29d179331920a76da6bb24e100504e6494a5a7b4
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/strategy/MultiHandlerFactory.java
@@ -0,0 +1,27 @@
+package com.huawei.devkit.pipeline.strategy;
+
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.DelimitedOptionHandler;
+import org.kohsuke.args4j.spi.IntOptionHandler;
+import org.kohsuke.args4j.spi.Setter;
+
+public class MultiHandlerFactory {
+ private static final String DELIMITER = ",";
+
+ public static class MultiFieldOptionHandler extends DelimitedOptionHandler {
+
+ public MultiFieldOptionHandler(CmdLineParser parser, OptionDef option, Setter super String> setter) {
+ super(parser, option, setter, DELIMITER, new StringOptionHandler(parser, option, setter));
+ }
+
+ }
+
+ public static class MultiIntegerOptionHandler extends DelimitedOptionHandler {
+
+ public MultiIntegerOptionHandler(CmdLineParser parser, OptionDef option, Setter super Integer> setter) {
+ super(parser, option, setter, DELIMITER, new IntOptionHandler(parser, option, setter));
+ }
+ }
+
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/strategy/StringOptionHandler.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/strategy/StringOptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d4a497135b9c31b712591286b50fca3ed8f27c1
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/strategy/StringOptionHandler.java
@@ -0,0 +1,17 @@
+package com.huawei.devkit.pipeline.strategy;
+
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OneArgumentOptionHandler;
+import org.kohsuke.args4j.spi.Setter;
+
+public class StringOptionHandler extends OneArgumentOptionHandler {
+ public StringOptionHandler(CmdLineParser parser, OptionDef option, Setter super String> setter) {
+ super(parser, option, setter);
+ }
+
+ @Override
+ protected String parse(String argument) throws NumberFormatException {
+ return argument;
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/JfrMethodSignatureParser.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/JfrMethodSignatureParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa03f05cfb3218dea8dbc34bfbb428317b3ae63c
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/JfrMethodSignatureParser.java
@@ -0,0 +1,138 @@
+package com.huawei.devkit.pipeline.utils;
+
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class JfrMethodSignatureParser {
+
+ private static final Map TYPE_MAPPINGS;
+ private static final int PARAMS_START_INDEX = 1;
+
+ static {
+ TYPE_MAPPINGS = new HashMap<>();
+ TYPE_MAPPINGS.put('V', "void");
+ TYPE_MAPPINGS.put('Z', "boolean");
+ TYPE_MAPPINGS.put('B', "byte");
+ TYPE_MAPPINGS.put('C', "char");
+ TYPE_MAPPINGS.put('D', "double");
+ TYPE_MAPPINGS.put('F', "float");
+ TYPE_MAPPINGS.put('I', "int");
+ TYPE_MAPPINGS.put('J', "long");
+ TYPE_MAPPINGS.put('S', "short");
+ }
+
+ /**
+ * 转换成正常方法签名
+ *
+ * @param signature 方法签名
+ * @param methodName 方法名
+ * @return 正常的方法签名
+ */
+ public static String convertMethodSignature(String signature, String methodName) {
+ List params = new ArrayList<>();
+ int end = parseParams(signature, params);
+ String returnDesc = parseReturn(signature, end);
+ return returnDesc + " " + methodName + "(" + Strings.join(params.iterator(), ',') + ")";
+ }
+
+ /**
+ * 转换成没有返回值正常方法签名
+ *
+ * @param signature 方法签名
+ * @param methodName 方法名
+ * @return 正常的方法签名
+ */
+ public static String convertMethodSignatureWithoutReturnType(String signature, String methodName) {
+ List params = new ArrayList<>();
+ parseParams(signature, params);
+ return methodName + "(" + Strings.join(params.iterator(), ',') + ")";
+ }
+
+ private static String parseReturn(String signature, int indexForRetrun) {
+ if (indexForRetrun < signature.length()) {
+ char returnType = signature.charAt(indexForRetrun);
+ if (returnType == 'L') {
+ int endIndex = signature.indexOf(';', indexForRetrun);
+ if (endIndex == -1) {
+ throw new IllegalArgumentException("Invalid method signature: " + signature);
+ }
+ return signature.substring(indexForRetrun + 1, endIndex).replace('/', '.');
+ } else if (returnType == '[') {
+ // Skip over array dimensions
+ int arrayDimension = 0;
+ while (indexForRetrun < signature.length() && signature.charAt(indexForRetrun) == '[') {
+ indexForRetrun++;
+ arrayDimension++;
+ }
+ if (indexForRetrun == signature.length()) {
+ throw new IllegalArgumentException("Invalid method signature: " + signature);
+ }
+ StringBuilder builder = new StringBuilder();
+ setNameToClassParamDesc(signature, indexForRetrun, builder);
+ return builder.append("[]".repeat(arrayDimension)).toString();
+ } else if (TYPE_MAPPINGS.containsKey(returnType)) {
+ return TYPE_MAPPINGS.get(returnType);
+ }
+ }
+ throw new IllegalArgumentException("Invalid type character in signature: " + signature);
+ }
+
+ private static int parseParams(String signature, List params) {
+ int index = PARAMS_START_INDEX;
+ while (index < signature.length()) {
+ char c = signature.charAt(index);
+ if (c == ')') {
+ index++;
+ break; // End of parameters, start of return type
+ } else if (c == 'L') {
+ int endIndex = signature.indexOf(';', index);
+ if (endIndex == -1) {
+ throw new IllegalArgumentException("Invalid method signature: " + signature);
+ }
+ String className = signature.substring(index + 1, endIndex).replace('/', '.');
+ params.add(className);
+ index = endIndex + 1;
+ } else if (c == '[') {
+ // Skip over array dimensions
+ int arrayDimension = 0;
+ while (index < signature.length() && signature.charAt(index) == '[') {
+ index++;
+ arrayDimension++;
+ }
+ if (index == signature.length()) {
+ throw new IllegalArgumentException("Invalid method signature: " + signature);
+ }
+ StringBuilder builder = new StringBuilder();
+ index = setNameToClassParamDesc(signature, index, builder);
+ params.add(builder.append("[]".repeat(arrayDimension)).toString());
+ } else if (TYPE_MAPPINGS.containsKey(c)) {
+ params.add(TYPE_MAPPINGS.get(c));
+ index++;
+ } else {
+ throw new IllegalArgumentException("Invalid type character in signature: " + c);
+ }
+ }
+ return index;
+ }
+
+ private static int setNameToClassParamDesc(String signature, int index, StringBuilder builder) {
+ char c = signature.charAt(index);
+ if (c == 'L') {
+ int endIndex = signature.indexOf(';', index);
+ if (endIndex == -1) {
+ throw new IllegalArgumentException("Invalid method signature: " + signature);
+ }
+ String className = signature.substring(index + 1, endIndex).replace('/', '.');
+ builder.append(className);
+ return endIndex + 1;
+ } else if (TYPE_MAPPINGS.containsKey(c)) {
+ builder.append(TYPE_MAPPINGS.get(c));
+ return index + 1;
+ }
+ throw new IllegalArgumentException("Invalid method signature: " + signature);
+ }
+}
\ No newline at end of file
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/JmeterResultTransfer.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/JmeterResultTransfer.java
new file mode 100644
index 0000000000000000000000000000000000000000..312001e27cfaad0cfb66715f49c4f1eeb0ce704c
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/JmeterResultTransfer.java
@@ -0,0 +1,77 @@
+package com.huawei.devkit.pipeline.utils;
+
+import com.huawei.devkit.pipeline.bo.JmeterResult;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JmeterResultTransfer {
+ private int timeStampIndex;
+
+ private int latencyIndex;
+
+ private int responseCodeIndex;
+
+ private int labelIndex;
+
+ public static List transfer(String resultPath) throws Exception {
+ return new JmeterResultTransfer().transferInner(resultPath);
+ }
+
+ public static boolean isNum(String str) {
+ if (str == null || str.isEmpty())
+ return false;
+ for (int i = 0; i < str.length(); i++) {
+ if (!Character.isDigit(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private List transferInner(String resultPath) throws Exception {
+ Path path = Paths.get(resultPath);
+ if (!Files.exists(path)) {
+ throw new Exception("the file not exist");
+ }
+ List results = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new FileReader(resultPath))) {
+ String headers = reader.readLine();
+ this.parseHeader(headers);
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] fields = line.split(",");
+ int responseCode = 700;
+ // 当url完全错误时,不会有responseCode
+ if (isNum(fields[responseCodeIndex])) {
+ responseCode = Integer.parseInt(fields[responseCodeIndex]);
+ }
+ results.add(new JmeterResult(Long.parseLong(fields[timeStampIndex]),
+ responseCode,
+ Integer.parseInt(fields[latencyIndex]),
+ fields[labelIndex]));
+ }
+ }
+ return results;
+ }
+
+ private void parseHeader(String headersCombined) {
+ String[] header = headersCombined.split(",");
+ for (int i = 0; i < header.length; i++) {
+ if ("elapsed".equalsIgnoreCase(header[i])) {
+ latencyIndex = i;
+ } else if ("timestamp".equalsIgnoreCase(header[i])) {
+ timeStampIndex = i;
+ } else if ("responseCode".equalsIgnoreCase(header[i])) {
+ responseCodeIndex = i;
+ } else if ("label".equalsIgnoreCase(header[i])) {
+ labelIndex = i;
+ }
+ }
+ }
+}
diff --git a/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/SimplifyResponse.java b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/SimplifyResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..188e2609bfc6894d610735e3ebb48e84876f2479
--- /dev/null
+++ b/component/DevKitTester/JFRParser/src/main/java/com/huawei/devkit/pipeline/utils/SimplifyResponse.java
@@ -0,0 +1,39 @@
+package com.huawei.devkit.pipeline.utils;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class SimplifyResponse {
+ private static final Logger logger = LogManager.getLogger(SimplifyResponse.class);
+
+ public static Map> simplify(List before, Class clazz) {
+ Map> result = new HashMap<>();
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ field.setAccessible(true); // 允许访问私有字段
+ JsonIgnore ignore = field.getAnnotation(JsonIgnore.class);
+ if (Objects.nonNull(ignore)) {
+ continue;
+ }
+ List