states = stateStorage.getValues();
49 | if (states != null) {
50 | for (String key : states.keySet()) {
51 | stateStorage.putValue(key, state);
52 | }
53 | }
54 | }
55 | stateStorage.flush();
56 | }
57 | }
58 | return null;
59 | }
60 |
61 | private void updateDefaultDeselectedMetrics(SBuildType buildType, String[] deselectedSeries) {
62 | CustomDataStorage stateStorage = buildType.getCustomDataStorage(PluginConstants.STORAGE_ID_DEFAULT_DESELECTED_SERIES);
63 | for (PerformanceStatisticMetrics metric: PerformanceStatisticMetrics.values()) {
64 | stateStorage.putValue(metric.getKey(), "false");
65 | }
66 | if (deselectedSeries.length > 0 && !deselectedSeries[0].isEmpty()) {
67 | for (String series: deselectedSeries) {
68 | stateStorage.putValue(series, "true");
69 | }
70 | }
71 | stateStorage.flush();
72 | }
73 | }
--------------------------------------------------------------------------------
/server/resources/statistic/perfChartsTC.jsp:
--------------------------------------------------------------------------------
1 | <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
2 | <%@ taglib prefix="stats" tagdir="/WEB-INF/tags/graph" %>
3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
4 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
31 |
32 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/server/resources/editFeatureRemoteMonitoring.jsp:
--------------------------------------------------------------------------------
1 | <%@ taglib prefix="props" tagdir="/WEB-INF/tags/props" %>
2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
3 | <%@ taglib prefix="bs" tagdir="/WEB-INF/tags" %>
4 | <%@ taglib prefix="forms" tagdir="/WEB-INF/tags/forms" %>
5 | <%@ taglib prefix="l" tagdir="/WEB-INF/tags/layout" %>
6 |
7 |
8 |
9 | <%--Remote performance monitoring--%>
10 |
11 |
12 | |
13 | Enable to run tests from another machine (non-Build Agent).
14 | |
15 |
16 |
17 | | Build step to analyze: |
18 |
19 |
20 | The name of the build step with performance tests
21 |
22 | |
23 |
24 |
25 | |
26 |
27 |
28 |
29 | |
30 |
31 |
32 |
33 | |
34 |
35 |
36 | |
37 |
38 |
39 |
40 | |
41 |
42 |
43 | |
44 |
45 |
46 | in seconds. Set to sync the time of monitoring between the build agent and the test machine
47 | |
48 |
49 |
50 | |
51 |
52 |
53 |
54 | |
55 |
56 |
57 | in seconds
58 | |
59 |
60 |
61 |
62 |
63 |
64 | Collected metrics: (system) cpu system/user/all; memory used; disk i/o reads/writes;
65 | (jmx) memory heap, pools commited/usage; gc time; class loaded;
66 |
67 | |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/server/src/META-INF/build-server-plugin-jmeter.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/server/server.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | file://$MODULE_DIR$/fake-teamcity-server-plugin-context.xml
8 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/META-INF/build-server-declarative-extensibility.xml
9 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/META-INF/build-server-plugin-agents-statistics.xml
10 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/META-INF/buildServerCompatMode.xml
11 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/META-INF/buildServerPermissions.xml
12 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/META-INF/buildServerPluginsSupport.xml
13 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/META-INF/buildServerSpring.xml
14 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/common-impl.jar!/META-INF/shared-spring-sub-container.xml
15 | jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/common-impl.jar!/META-INF/plugin-model-shared-spring.xml
16 | file://$TeamCityDistribution$/webapps/ROOT/WEB-INF/buildServerSpringWeb.xml
17 | file://$MODULE_DIR$/src/META-INF/build-server-plugin-jmeter.xml
18 | jar://$TeamCityDistribution$/buildAgent/lib/common-impl.jar!/META-INF/per-plugin-shared-spring.xml
19 | jar://$TeamCityDistribution$/buildAgent/lib/common-impl.jar!/META-INF/plugin-model-shared-spring.xml
20 | jar://$TeamCityDistribution$/buildAgent/lib/common-impl.jar!/META-INF/shared-spring-sub-container.xml
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/agent/src/perf_statistic/agent/metric_aggregation/counting/BaseAggregation.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.agent.metric_aggregation.counting;
2 |
3 | import com.intellij.util.containers.SortedList;
4 | import org.jetbrains.annotations.NotNull;
5 | import perf_statistic.agent.metric_aggregation.AggregationProperties;
6 | import perf_statistic.common.PerformanceStatisticMetrics;
7 |
8 | import java.util.Comparator;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | /**
14 | * Base class to count aggregate values
15 | */
16 | public abstract class BaseAggregation {
17 | protected boolean isAggregationCalculated = false;
18 |
19 | protected final String myTitle;
20 |
21 | protected final Map codes;
22 | protected final AggregationProperties myProperties;
23 |
24 | protected double sum;
25 | protected int count;
26 | protected double mean;
27 | protected double min = Double.MAX_VALUE;
28 | protected double max = Double.MIN_VALUE;
29 | protected List responseTimesToAggregate; //to calculate quantiles
30 |
31 | protected BaseAggregation(String title, AggregationProperties properties) {
32 | myTitle = title;
33 | myProperties = properties;
34 | codes = new HashMap();
35 | responseTimesToAggregate = new SortedList(new Comparator() {
36 | @Override
37 | public int compare(Double o1, Double o2) {
38 | return o1.compareTo(o2);
39 | }
40 | });
41 | }
42 |
43 | public String getTitle() {
44 | return myTitle;
45 | }
46 |
47 | public void addItem(Item item) {
48 | if (!myProperties.isCheckAssertions() || item.isSuccessful()) {
49 | addCalculatedValue(item.getResponseTime());
50 | }
51 | if (myProperties.isCalculateResponseCodes()) {
52 | Long count = codes.get(item.getResponseCode());
53 | codes.put(item.getResponseCode(), count != null ? ++count : 1);
54 | }
55 | }
56 |
57 | private void addCalculatedValue(double itemValue) {
58 | isAggregationCalculated = true;
59 | count++;
60 | sum += itemValue;
61 | mean = sum / count;
62 | min = Math.min(itemValue, min);
63 | max = Math.max(itemValue, max);
64 | responseTimesToAggregate.add(itemValue);
65 | }
66 |
67 | public Double getAggregateValue(@NotNull final PerformanceStatisticMetrics param)
68 | {
69 | switch (param) {
70 | case AVERAGE:
71 | return mean;
72 | case MAX:
73 | return max;
74 | case MIN:
75 | return min;
76 | case LINE90: {
77 | int ind90 = (int) Math.round(responseTimesToAggregate.size() * 0.9d);
78 | return responseTimesToAggregate.get(ind90 - 1);
79 | }
80 | default:
81 | return null;
82 | }
83 | }
84 |
85 | public Map getCodes() {
86 | return codes;
87 | }
88 |
89 | public boolean isAggregationCalculated() {
90 | return isAggregationCalculated;
91 | }
92 | /* public String checkValue(@NotNull final PerformanceStatisticMetrics metric, double referenceValue, double variation) {
93 | Double currentValue = getAggregateValue(metric);
94 | if (currentValue > referenceValue * (1 + variation)) {
95 | return "Metric - " + metric.getTitle() + "; testName - " + myTitle
96 | + "; \nreference value: " + Math.round(referenceValue)
97 | + "; current value: " + Math.round(currentValue)
98 | + "; variation: " + variation;
99 | }
100 | return null;
101 | }*/
102 | }
103 |
--------------------------------------------------------------------------------
/server/resources/monitoring/remotePerfMon.jsp:
--------------------------------------------------------------------------------
1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2 | <%@ taglib prefix="bs" tagdir="/WEB-INF/tags" %>
3 | <%@ taglib prefix="forms" tagdir="/WEB-INF/tags/forms" %>
4 |
5 | <%--@elvariable id="metrics" type="java.util.Collection"--%>
6 | <%--@elvariable id="build" type="jetbrains.buildServer.serverSide.SBuild>"--%>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ${metric.title}
37 |
38 |
39 | |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | |
61 |
62 |
63 |
64 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/common/src/perf_statistic/common/PluginConstants.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.common;
2 |
3 | public class PluginConstants {
4 | private PluginConstants() {}
5 | public static final String FEATURE_TYPE_AGGREGATION = "performance_test_analyzer";
6 | public static final String FEATURE_TYPE_REMOTE_MONITORING = "performance_remote_monitoring";
7 |
8 |
9 | //Feature's parameters
10 | public static final String PARAMS_AGGREGATE_FILE = "perfTest.agg.file";
11 | public static final String PARAMS_METRIC_MAX = "perfTest.agg.max";
12 | public static final String PARAMS_METRIC_MIN = "perfTest.agg.min";
13 | public static final String PARAMS_METRIC_AVG = "perfTest.agg.avg";
14 | public static final String PARAMS_METRIC_LINE90 = "perfTest.agg.90line";
15 | public static final String PARAMS_HTTP_RESPONSE_CODE = "perfTest.agg.respCode";
16 | public static final String PARAMS_USED_TEST_FORMAT = "perfTest.agg.testFormat";
17 | public static final String PARAMS_CALC_TOTAL = "perfTest.agg.total";
18 | public static final String PARAMS_CHECK_ASSERT = "perfTest.agg.assert";
19 | public static final String PARAMS_TEST_GROUPS = "perfTest.agg.testGroups";
20 |
21 | public static final String PARAMS_REF_CHECK = "perfTest.check.ref.data";
22 |
23 | public static final String PARAMS_REF_TYPE_FILE = "perfTest.ref.type.file";
24 | public static final String PARAMS_REF_DATA_FILE = "perfTest.ref.data";
25 |
26 | public static final String PARAMS_REF_TYPE_BUILD_HISTORY = "perfTest.ref.type.builds";
27 | public static final String PARAMS_REF_BUILD_COUNT = "perfTest.ref.buildCount";
28 | public static final String PARAMS_REF_METRIC_MAX = "perfTest.agg.ref.max";
29 | public static final String PARAMS_REF_METRIC_AVG = "perfTest.agg.ref.avg";
30 | public static final String PARAMS_REF_METRIC_LINE90 = "perfTest.agg.ref.90line";
31 |
32 | public static final String PARAMS_REF_TYPE = "perfTest.ref.type";
33 |
34 | public static final String PARAMS_VARIATION_CRITICAL = "perfTest.ref.variation";
35 | public static final String PARAMS_VARIATION_WARN = "perfTest.ref.warn.variation";
36 |
37 | public static final String PARAMS_BUILD_STEP_TO_ANALYZE = "perfTest.buildStep";
38 | public static final String PARAMS_REMOTE_PERF_MON_HOST = "perfTest.mon.host";
39 | public static final String PARAMS_REMOTE_PERF_MON_PORT = "perfTest.mon.port";
40 | public static final String PARAMS_REMOTE_INTERVAL = "perfTest.mon.interval";
41 | public static final String PARAMS_REMOTE_CLOCK_DELAY = "perfTest.mon.clock.delay";
42 |
43 | public static final String AGGREGATION_TOTAL_NAME = "Total";
44 | public static final String MONITORING_RESULT_FILE = "monitoring.csv";
45 |
46 | // build problem type
47 | public static final String CRITICAL_PERFORMANCE_PROBLEM_TYPE = "Performance worsened (critical)";
48 | public static final String WARN_PERFORMANCE_PROBLEM_TYPE = "Performance worsened";
49 | public static final String ASSERTION_FAILED_PROBLEM_TYPE = "Assertion failed";
50 |
51 | // custom build type storage constants
52 | public static final String STORAGE_ID_COMMON_JMETER = "teamcity.jmeter.statistic";
53 | public static final String STORAGE_ID_TEST_ALIAS = "teamcity.jmeter.sample.keys";
54 | public static final String STORAGE_ID_DEFAULT_DESELECTED_SERIES = "teamcity.jmeter.default.deselected.series";
55 | public static final String STORAGE_ID_WARNINGS = "teamcity.jmeter.build.warnings";
56 |
57 |
58 | public static final String STORAGE_KEY_METRIC = "Metrics";
59 | public static final String STORAGE_KEY_SAMPLES = "Samples";
60 | public static final String STORAGE_KEY_CODE = "Codes";
61 |
62 | // Log constants
63 | public static final String PERFORMANCE_TESTS_ACTIVITY_NAME = "Performance tests statistic";
64 | public static final String CHECK_REFERENCE_ACTIVITY_NAME = "Check reference values from FILE";
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/agent/src/perf_statistic/agent/common/PerformanceLogger.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.agent.common;
2 |
3 | import jetbrains.buildServer.BuildProblemData;
4 | import jetbrains.buildServer.agent.BuildProgressLogger;
5 | import jetbrains.buildServer.messages.DefaultMessagesInfo;
6 | import org.jetbrains.annotations.NotNull;
7 | import perf_statistic.common.PerformanceMessageParser;
8 | import perf_statistic.common.StringUtils;
9 |
10 |
11 | public class PerformanceLogger {
12 | private final BuildProgressLogger logger;
13 |
14 | public PerformanceLogger(@NotNull BuildProgressLogger logger) {
15 | this.logger = logger;
16 | }
17 |
18 | public void logMessage(final String ... messageArrays) {
19 | StringBuilder builder = new StringBuilder();
20 | for (String value : messageArrays) {
21 | builder.append(value);
22 | }
23 | logMessage(builder.toString());
24 | }
25 |
26 | public void logMessage(final String message) {
27 | logger.logMessage(DefaultMessagesInfo.createTextMessage(message));
28 | }
29 |
30 | public void logMessage(final String testGroup, final String testName, final String metricName, final long value, final String code, final boolean warning) {
31 | String message = PerformanceMessageParser.createJMeterMessage(testGroup, testName, metricName, value, code, warning);
32 | logger.logMessage(DefaultMessagesInfo.createTextMessage(message));
33 | }
34 |
35 | public void logWarningMessage(final String testGroup, final String testName, final String metricName, final long value, final String code, long currValue, double variation) {
36 | String message = PerformanceMessageParser.createJMeterWarningMessage(testGroup, testName, metricName, value, code, currValue, variation);
37 | logger.logMessage(DefaultMessagesInfo.createTextMessage(message));
38 | logger.warning("Exceed variation:" + testGroup + " " + testName + "; metric = " + metricName);
39 | }
40 |
41 | public void logWarningBuildStatus() {
42 | logger.logMessage(DefaultMessagesInfo.createTextMessage("##teamcity[buildStatus text='{build.status.text}; Warning: exceed non-critical variation!']"));
43 | }
44 |
45 | public void logBuildProblem(final String metric, final String testName, final String type, final String description) {
46 | BuildProblemData buildProblem = BuildProblemData.createBuildProblem(StringUtils.getBuildProblemId(metric, testName), type, description, testName);
47 | System.out.println(buildProblem);
48 | System.out.println("Additional data: " + buildProblem.getAdditionalData());
49 | logger.logBuildProblem(buildProblem);
50 | }
51 |
52 | public void logBuildProblem(final String identity, final String type, final String description) {
53 | BuildProblemData buildProblem = BuildProblemData.createBuildProblem(StringUtils.cutLongBuildProblemIdentity(identity), type, description);
54 | logger.logBuildProblem(buildProblem);
55 | }
56 |
57 | public void activityStarted(String activityName) {
58 | logger.activityStarted(activityName, DefaultMessagesInfo.BLOCK_TYPE_MODULE);
59 | }
60 |
61 | public void activityFinished(String activityName) {
62 | logger.activityFinished(activityName, DefaultMessagesInfo.BLOCK_TYPE_MODULE);
63 | }
64 |
65 |
66 | public void logTestStarted(String testName) {
67 | logger.logTestStarted(testName);
68 | }
69 |
70 | public void logTestFailed(String testName, String typeError, String msg) {
71 | logger.logTestFailed(testName, typeError, msg);
72 | }
73 |
74 | public void logTestFinished(String testName) {
75 | logger.logTestFinished(testName);
76 | }
77 |
78 | public void testsGroupStarted(String testGroupName) {
79 | if (testGroupName != null && !testGroupName.isEmpty())
80 | logger.logSuiteStarted(testGroupName);
81 | }
82 | public void testsGroupFinished(String testGroupName) {
83 | if (testGroupName != null && !testGroupName.isEmpty())
84 | logger.logSuiteFinished(testGroupName);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/agent/src/perf_statistic/agent/metric_aggregation/counting/Item.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.agent.metric_aggregation.counting;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 | import perf_statistic.agent.common.BaseFileReader;
6 | import perf_statistic.agent.metric_aggregation.AggregationProperties;
7 | import perf_statistic.common.PerformanceMessageParser;
8 | import perf_statistic.common.StringUtils;
9 |
10 | import java.util.Arrays;
11 |
12 | public class Item {
13 | // protected final String startTime;
14 | private final long responseTime;
15 | private final String testName;
16 | private String responseCode = null;
17 | private boolean isSuccessful;
18 | private String testGroupName = StringUtils.EMPTY;
19 |
20 | // save only if check assertions and item is not successful
21 | protected String logLine;
22 |
23 | public Item(String line, AggregationProperties properties) throws IllegalItemFormatException {
24 | String[] values = PerformanceMessageParser.DELIMITER_PATTERN.split(line);
25 |
26 | if (values == null || values.length < 3) { //failureMessage may be empty
27 | throw new IllegalItemFormatException("", values);
28 | }
29 | // startTime = values[0];
30 | responseTime = Long.parseLong(values[1]);
31 | if (properties.isUsedTestGroups()) {
32 | String[] testNameParts = values[2].split(":");
33 | if (testNameParts.length < 2) {
34 | throw new IllegalItemFormatException("Test label must contains thread group name separated to test name by ':' ! Find: " + values[2]);
35 | }
36 | testGroupName = testNameParts[0].trim();
37 | testName = StringUtils.checkTestName(testNameParts[1].trim());
38 | } else {
39 | testName = StringUtils.checkTestName(values[2]);
40 | }
41 |
42 | boolean codes = properties.isCalculateResponseCodes();
43 | boolean assets = properties.isCheckAssertions();
44 |
45 | if (assets && !isSuccessful) {
46 | logLine = line;
47 | }
48 | if(codes && assets) {
49 | if (values.length < 5)
50 | throw new IllegalItemFormatException("\tresponseCode\tisSuccess", values);
51 | responseCode = values[3];
52 | isSuccessful = values[4].equals("1") || values[4].equalsIgnoreCase("true");
53 | } else if (codes) {
54 | if (values.length < 4)
55 | throw new IllegalItemFormatException("\tresponseCode", values);
56 | responseCode = values[3];
57 | } else if (assets) {
58 | if (values.length < 4)
59 | throw new IllegalItemFormatException("\tisSuccess", values);
60 | isSuccessful = values[3].equals("1") || values[3].equalsIgnoreCase("true");
61 | }
62 | }
63 |
64 | // public String getStartTime() {
65 | // return startTime;
66 | // }
67 |
68 | public long getResponseTime() {
69 | return responseTime;
70 | }
71 |
72 | public String getTestName() {
73 | return testName;
74 | }
75 |
76 | public String getResponseCode() {
77 | return responseCode;
78 | }
79 |
80 | public boolean isSuccessful() {
81 | return isSuccessful;
82 | }
83 |
84 | @NotNull
85 | public String getTestGroupName() {
86 | return testGroupName != null ? testGroupName : StringUtils.EMPTY;
87 | }
88 |
89 | @Nullable
90 | public String getLogLine() {
91 | return logLine;
92 | }
93 |
94 | public String toString(){
95 | return "_Item_: responseTime=[" + responseTime
96 | + "] testName=[" + testName
97 | + "] responseCode=[" + responseCode
98 | + "] isSuccessful=[" + isSuccessful
99 | + "] testGroupName=[" + testGroupName;
100 | }
101 |
102 | public static class IllegalItemFormatException extends BaseFileReader.FileFormatException {
103 | public IllegalItemFormatException(String message) {
104 | super(message);
105 | }
106 | public IllegalItemFormatException(String wrongFiledNames, String[] actualData) {
107 | super("Result item format must included asserted result. Format: startTime\tresponseTime\ttestName" + wrongFiledNames + "...\nFound" + Arrays.toString(actualData));
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/server/src/perf_statistic/server/remote_monitoring/RemotePerfMonTab.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.server.remote_monitoring;
2 |
3 | import jetbrains.buildServer.controllers.BuildDataExtensionUtil;
4 | import jetbrains.buildServer.serverSide.CustomDataStorage;
5 | import jetbrains.buildServer.serverSide.SBuild;
6 | import jetbrains.buildServer.serverSide.SBuildServer;
7 | import jetbrains.buildServer.serverSide.SBuildType;
8 | import jetbrains.buildServer.serverSide.artifacts.BuildArtifact;
9 | import jetbrains.buildServer.serverSide.artifacts.BuildArtifactsViewMode;
10 | import jetbrains.buildServer.web.openapi.PagePlaces;
11 | import jetbrains.buildServer.web.openapi.PlaceId;
12 | import jetbrains.buildServer.web.openapi.PluginDescriptor;
13 | import jetbrains.buildServer.web.openapi.SimpleCustomTab;
14 | import org.jetbrains.annotations.NotNull;
15 | import perf_statistic.common.PluginConstants;
16 | import perf_statistic.server.remote_monitoring.graph.Graph;
17 |
18 | import javax.servlet.http.HttpServletRequest;
19 | import java.io.File;
20 | import java.util.ArrayList;
21 | import java.util.Collection;
22 | import java.util.Map;
23 |
24 | /**
25 | * Remote performance monitoring tab, locates on the build page
26 | */
27 | public class RemotePerfMonTab extends SimpleCustomTab {
28 | private BuildArtifact monitoringLogFile;
29 |
30 | protected final SBuildServer myServer;
31 |
32 | public RemotePerfMonTab(@NotNull final PagePlaces pagePlaces, @NotNull final SBuildServer server, @NotNull final PluginDescriptor descriptor) {
33 | super(pagePlaces, PlaceId.BUILD_RESULTS_TAB, "jmeter", descriptor.getPluginResourcesPath("monitoring/remotePerfMon.jsp"), "RemotePerfMon");
34 | myServer = server;
35 |
36 | addJsFile(descriptor.getPluginResourcesPath("flot/excanvas.js"));
37 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.js"));
38 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.stack.js"));
39 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.crosshair.js"));
40 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.selection.js"));
41 |
42 | addJsFile(descriptor.getPluginResourcesPath("monitoring/remotePerfMon.format.js"));
43 | addJsFile(descriptor.getPluginResourcesPath("monitoring/remotePerfMon.plots.js"));
44 | addCssFile(descriptor.getPluginResourcesPath("monitoring/remotePerfMon.styles.css"));
45 | register();
46 | }
47 |
48 | @Override
49 | public boolean isAvailable(@NotNull final HttpServletRequest request) {
50 | final SBuild build = BuildDataExtensionUtil.retrieveBuild(request, myServer);
51 | if (build == null) {
52 | return false;
53 | }
54 | boolean hasFeature = build.getBuildType() != null && !build.getBuildType().getBuildFeaturesOfType(PluginConstants.FEATURE_TYPE_REMOTE_MONITORING).isEmpty();
55 |
56 | monitoringLogFile = build.getArtifacts(BuildArtifactsViewMode.VIEW_ALL).getArtifact(PluginConstants.MONITORING_RESULT_FILE);
57 | return hasFeature && monitoringLogFile != null && monitoringLogFile.isFile();
58 | }
59 |
60 | public void fillModel(@NotNull Map model, @NotNull HttpServletRequest request) {
61 | SBuild build = BuildDataExtensionUtil.retrieveBuild(request, myServer);
62 |
63 | Collection data = new ArrayList();
64 | if (monitoringLogFile != null && build != null) {
65 | RemotePerfMonLogDataProvider provider = new RemotePerfMonLogDataProvider();
66 | data = provider.getGraphs(new File(build.getArtifactsDirectory().getAbsolutePath() + File.separator + monitoringLogFile.getRelativePath()));
67 | }
68 |
69 | if (build != null && build.getBuildType() != null) {
70 | setState(data, build.getBuildType());
71 | }
72 | model.put("metrics", data);
73 | model.put("build", build);
74 | }
75 |
76 |
77 | /**
78 | * retrieve and set graph state (hidden/shown)
79 | * @param graphs
80 | * @param buildType
81 | */
82 | private void setState(Collection graphs, @NotNull SBuildType buildType) {
83 | CustomDataStorage stateStorage = buildType.getCustomDataStorage("teamcity.jmeter.graph.states");
84 | for (Graph graph : graphs) {
85 | String state = stateStorage.getValue(graph.getId());
86 | graph.setState(state == null ? "shown" : state);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/server/src/perf_statistic/server/FeaturePerformanceStatistic.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.server;
2 |
3 | import jetbrains.buildServer.serverSide.BuildFeature;
4 | import jetbrains.buildServer.serverSide.InvalidProperty;
5 | import jetbrains.buildServer.serverSide.PropertiesProcessor;
6 | import jetbrains.buildServer.web.openapi.PluginDescriptor;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 | import perf_statistic.common.PluginConstants;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Collection;
13 | import java.util.Map;
14 |
15 | /**
16 | * Created by Yuliya.Torhan on 1/16/14.
17 | */
18 | public class FeaturePerformanceStatistic extends BuildFeature {
19 | private final String myEditUrl;
20 |
21 | public FeaturePerformanceStatistic(@NotNull final PluginDescriptor descriptor) {
22 | myEditUrl = descriptor.getPluginResourcesPath("editFeaturePerformanceStatistic.jsp");
23 | }
24 |
25 | @NotNull
26 | @Override
27 | public String getType() {
28 | return PluginConstants.FEATURE_TYPE_AGGREGATION;
29 | }
30 |
31 | @NotNull
32 | @Override
33 | public String getDisplayName() {
34 | return "Performance Metrics Calculation";
35 | }
36 |
37 | @Nullable
38 | @Override
39 | public String getEditParametersUrl() {
40 | return myEditUrl;
41 | }
42 |
43 | @NotNull
44 | @Override
45 | public String describeParameters(@NotNull final Map params) {
46 | StringBuilder result = new StringBuilder();
47 | result.append("Log file to aggregate values=[").append(params.get(PluginConstants.PARAMS_AGGREGATE_FILE))
48 | .append("]; metrics=[ ");
49 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_METRIC_MIN)))
50 | result.append("min ");
51 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_METRIC_MAX)))
52 | result.append("max ");
53 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_METRIC_AVG)))
54 | result.append("avg ");
55 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_METRIC_LINE90)))
56 | result.append("line90");
57 | result.append(" ]");
58 |
59 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_HTTP_RESPONSE_CODE)))
60 | result.append(" + response codes");
61 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_CHECK_ASSERT)))
62 | result.append(" + assertions");
63 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_CALC_TOTAL)))
64 | result.append(" + total.");
65 |
66 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_TEST_GROUPS)))
67 | result.append(" Starts on different threads.");
68 | if ("true".equalsIgnoreCase(params.get(PluginConstants.PARAMS_REF_CHECK)))
69 | result.append(" Fail if check reference values not passed.");
70 | return result.toString();
71 | }
72 |
73 | public PropertiesProcessor getParametersProcessor() {
74 | return new PropertiesProcessor() {
75 | @Override
76 | public Collection process(Map params) {
77 | Collection invalidProperties = new ArrayList();
78 | if (params.get(PluginConstants.PARAMS_AGGREGATE_FILE) == null) {
79 | invalidProperties.add(new InvalidProperty(PluginConstants.PARAMS_AGGREGATE_FILE, "File with data to aggregate can't be empty!"));
80 | }
81 | if (Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_CHECK))) {
82 | if (Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_TYPE_BUILD_HISTORY)) && params.get(PluginConstants.PARAMS_REF_BUILD_COUNT) == null) {
83 | invalidProperties.add(new InvalidProperty(PluginConstants.PARAMS_REF_BUILD_COUNT, "Set count of build to calculate reference value!"));
84 | }
85 | if (Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_TYPE_FILE)) && params.get(PluginConstants.PARAMS_REF_DATA_FILE) == null) {
86 | invalidProperties.add(new InvalidProperty(PluginConstants.PARAMS_REF_DATA_FILE, "Set checkout file with references values!"));
87 | }
88 | }
89 | if (!Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_AVG)) && !Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_MAX))
90 | && !Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_MIN)) && !Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_LINE90))) {
91 | invalidProperties.add(new InvalidProperty("perfTest.metrics", "Please, choose at least one metric!"));
92 | }
93 | return invalidProperties;
94 | }
95 | };
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Tomcat.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/server/resources/flot/jquery.flot.time.min.js:
--------------------------------------------------------------------------------
1 | /* Pretty handling of time axes.
2 |
3 | Copyright (c) 2007-2013 IOLA and Ole Laursen.
4 | Licensed under the MIT license.
5 |
6 | Set axis.mode to "time" to enable. See the section "Time series data" in
7 | API.txt for details.
8 |
9 | */(function(e){function n(e,t){return t*Math.floor(e/t)}function r(e,t,n,r){if(typeof e.strftime=="function")return e.strftime(t);var i=function(e,t){return e=""+e,t=""+(t==null?"0":t),e.length==1?t+e:e},s=[],o=!1,u=e.getHours(),a=u<12;n==null&&(n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),r==null&&(r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]);var f;u>12?f=u-12:u==0?f=12:f=u;for(var l=0;l=u)break;var h=l[c][0],p=l[c][1];if(p=="year"){if(i.minTickSize!=null&&i.minTickSize[1]=="year")h=Math.floor(i.minTickSize[0]);else{var d=Math.pow(10,Math.floor(Math.log(e.delta/o.year)/Math.LN10)),v=e.delta/o.year/d;v<1.5?h=1:v<3?h=2:v<7.5?h=5:h=10,h*=d}h<1&&(h=1)}e.tickSize=i.tickSize||[h,p];var m=e.tickSize[0];p=e.tickSize[1];var g=m*o[p];p=="second"?r.setSeconds(n(r.getSeconds(),m)):p=="minute"?r.setMinutes(n(r.getMinutes(),m)):p=="hour"?r.setHours(n(r.getHours(),m)):p=="month"?r.setMonth(n(r.getMonth(),m)):p=="quarter"?r.setMonth(3*n(r.getMonth()/3,m)):p=="year"&&r.setFullYear(n(r.getFullYear(),m)),r.setMilliseconds(0),g>=o.minute&&r.setSeconds(0),g>=o.hour&&r.setMinutes(0),g>=o.day&&r.setHours(0),g>=o.day*4&&r.setDate(1),g>=o.month*2&&r.setMonth(n(r.getMonth(),3)),g>=o.quarter*2&&r.setMonth(n(r.getMonth(),6)),g>=o.year&&r.setMonth(0);var y=0,b=Number.NaN,w;do{w=b,b=r.getTime(),t.push(b);if(p=="month"||p=="quarter")if(m<1){r.setDate(1);var E=r.getTime();r.setMonth(r.getMonth()+(p=="quarter"?3:1));var S=r.getTime();r.setTime(b+y*o.hour+(S-E)*m),y=r.getHours(),r.setHours(0)}else r.setMonth(r.getMonth()+m*(p=="quarter"?3:1));else p=="year"?r.setFullYear(r.getFullYear()+m):r.setTime(b+g)}while(b {
12 | private static final Pattern non_word_pattern = Pattern.compile("\\W");
13 |
14 | enum PerformanceStatus {
15 | OK, CRITICAL_DECLINE, DECLINE
16 | }
17 | private final STestRun myTest;
18 | private final String myChartKey;
19 | private final String myTestName;
20 | private final String myGroupName;
21 |
22 | private String myWarning = "";
23 | private List myPerformanceProblems;
24 |
25 | private List> mySRTValues;
26 | private Map myRPSValues;
27 | private Map myLogLines;
28 |
29 | public PerformanceTestRun(STestRun test) {
30 | myTest = test;
31 | myChartKey = non_word_pattern.matcher(myTest.getTest().getName().getAsString()).replaceAll("");
32 | TestName name = myTest.getTest().getName();
33 | myTestName = name.getNameWithoutSuite();
34 | myGroupName = name.getSuite().replace(":", "");
35 | }
36 |
37 | public int getID() {
38 | return myTest.getTestRunId();
39 | }
40 |
41 | public String getTestName() {
42 | return myTestName;
43 | }
44 |
45 | public String getTestsGroupName() {
46 | return myGroupName;
47 | }
48 |
49 | public String getFullName() {
50 | return myTest.getTest().getName().getAsString();
51 | }
52 |
53 | public String getChartKey() {
54 | return myChartKey;
55 | }
56 |
57 | public STestRun getTestRun() {
58 | return myTest;
59 | }
60 |
61 | public String getTestStatus() {
62 | return myTest.getStatus().toString();
63 | }
64 |
65 | public String getPerformanceStatus() {
66 | if (myPerformanceProblems != null && !myPerformanceProblems.isEmpty())
67 | return PerformanceStatus.CRITICAL_DECLINE.toString();
68 | if (!myWarning.isEmpty())
69 | return PerformanceStatus.DECLINE.toString();
70 | return PerformanceStatus.OK.toString();
71 | }
72 |
73 | public Collection getPerformanceProblems() {
74 | return myPerformanceProblems;
75 | }
76 |
77 |
78 |
79 | public void setPerformanceProblems(List performanceProblems) {
80 | myPerformanceProblems = performanceProblems;
81 | }
82 |
83 | public String getWarning() {
84 | return myWarning;
85 | }
86 |
87 | public void setWarning(String warning) {
88 | myWarning = warning;
89 | }
90 |
91 | public List> getSRTValues() {
92 | return mySRTValues;
93 | }
94 |
95 | public List> getRPSValues() {
96 | if (myRPSValues != null) {
97 | List> values = new SortedList>(new Comparator>() {
98 | @Override
99 | public int compare(List o1, List o2) {
100 | return o2.get(0).equals(o1.get(0)) ? 0 : o1.get(0) < o2.get(0) ? -1 : 1;
101 | }
102 | });
103 | for (long time: myRPSValues.keySet()) {
104 | values.add(Arrays.asList(time, myRPSValues.get(time)));
105 | }
106 | return values;
107 | }
108 | return null;
109 | }
110 |
111 | public Map getLogLines() {
112 | return myLogLines;
113 | }
114 |
115 | public void addTimeValue(long time, long value) {
116 | if (mySRTValues == null) {
117 | mySRTValues = new SortedList>(new Comparator>() {
118 | @Override
119 | public int compare(List o1, List o2) {
120 | return o2.get(0).equals(o1.get(0)) ? 0 : o1.get(0) < o2.get(0) ? -1 : 1;
121 | }
122 | });
123 | }
124 | mySRTValues.add(Arrays.asList(time, value));
125 |
126 | if (myRPSValues == null) {
127 | myRPSValues = new HashMap();
128 | }
129 | time = time - time % 1000;
130 | Long count = myRPSValues.get(time);
131 | myRPSValues.put(time, count == null ? 1 : ++count);
132 | }
133 |
134 | public void addLogLine(long time, String[] lineParts) {
135 | if (myLogLines == null) {
136 | myLogLines = new HashMap();
137 | }
138 | myLogLines.put(time, lineParts);
139 | }
140 |
141 |
142 |
143 | @Override
144 | public int compareTo(PerformanceTestRun o) {
145 | return this.myGroupName.compareTo(((PerformanceTestRun) o).myGroupName);
146 | /* if (this.myGroupName == null && o.myGroupName == null) {
147 | if (this.myTestName.equals("Total")) return 1;
148 | if (o.myTestName.equals("Total")) return -1;
149 | if (o.myTestName.equals("Total")) return this.myTestName.compareTo(o.myTestName);
150 | }*/
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/common/src/perf_statistic/common/PerformanceMessageParser.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.common;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | public class PerformanceMessageParser {
6 | public static final String DELIMITER = "\t";
7 | public static final Pattern DELIMITER_PATTERN = Pattern.compile(DELIMITER);
8 |
9 | private static final String MESSAGE_START = "performance[";
10 | private static final String MESSAGE_END = "]";
11 | private static final String MESSAGE_KEY_METRIC = "metric=";
12 | private static final String MESSAGE_KEY_TEST_NAME = "test=";
13 | private static final String MESSAGE_KEY_CODE_LABEL = "code=";
14 | private static final String MESSAGE_KEY_VALUE = "value=";
15 | private static final String MESSAGE_KEY_TESTS_GROUP_NAME = "testsGroup=";
16 | private static final String MESSAGE_KEY_WARNING = "warning=";
17 | private static final String MESSAGE_KEY_WARNING_CURRENT_VALUE = "currValue=";
18 | private static final String MESSAGE_KEY_WARNING_VARIATION = "variation=";
19 |
20 |
21 | public static PerformanceMessage getPerformanceTestingMessage(String message) {
22 | if (message.startsWith(MESSAGE_START) && message.endsWith(MESSAGE_END)) {
23 | message = message.substring(12, message.length() - 1).trim();
24 | PerformanceMessage perfTestMessage = new PerformanceMessage();
25 | for (String attribute : DELIMITER_PATTERN.split(message)) {
26 | if (attribute.startsWith(MESSAGE_KEY_METRIC)) {
27 | perfTestMessage.setMetric(attribute.substring(7));
28 | continue;
29 | }
30 | if (attribute.startsWith(MESSAGE_KEY_TEST_NAME)) {
31 | perfTestMessage.setTestName(attribute.substring(5));
32 | continue;
33 | }
34 | if (attribute.startsWith(MESSAGE_KEY_CODE_LABEL)) {
35 | perfTestMessage.setCodeLabel(attribute.substring(5));
36 | continue;
37 | }
38 | if (attribute.startsWith(MESSAGE_KEY_VALUE)) {
39 | perfTestMessage.setValue(attribute.substring(6));
40 | continue;
41 | }
42 | if (attribute.startsWith(MESSAGE_KEY_TESTS_GROUP_NAME)) {
43 | perfTestMessage.setTestsGroupName(attribute.substring(11));
44 | }
45 | if (attribute.startsWith(MESSAGE_KEY_WARNING)) {
46 | perfTestMessage.setWarning(Boolean.parseBoolean(attribute.substring(8)));
47 | }
48 | if (attribute.startsWith(MESSAGE_KEY_WARNING_CURRENT_VALUE)) {
49 | perfTestMessage.setCurrValue(attribute.substring(10));
50 | }
51 | if (attribute.startsWith(MESSAGE_KEY_WARNING_VARIATION)) {
52 | perfTestMessage.setVariation(attribute.substring(10));
53 | }
54 | // todo: temporary to support old format; will be removed in future
55 | if (attribute.startsWith("label=")) {
56 | perfTestMessage.setTestName(attribute.substring(6));
57 | }
58 | }
59 | return perfTestMessage;
60 | }
61 | return null;
62 | }
63 |
64 | public static String createJMeterMessage(final String testGroup, final String testName, final String metric, final long value, final String code, final boolean warning) {
65 | StringBuilder message = new StringBuilder(MESSAGE_START);
66 | if (testGroup != null && !testGroup.isEmpty()) {
67 | message.append(MESSAGE_KEY_TESTS_GROUP_NAME).append(testGroup).append(DELIMITER);
68 | }
69 | message.append(MESSAGE_KEY_TEST_NAME).append(testName).append(DELIMITER)
70 | .append(MESSAGE_KEY_METRIC).append(metric).append(DELIMITER);
71 | if (code != null && !code.isEmpty()) {
72 | message.append(MESSAGE_KEY_CODE_LABEL).append(code).append(DELIMITER);
73 | }
74 |
75 | message.append(MESSAGE_KEY_WARNING).append(warning).append(DELIMITER);
76 | message.append(MESSAGE_KEY_VALUE).append(value).append(MESSAGE_END);
77 |
78 | return message.toString();
79 | }
80 |
81 | public static String createJMeterWarningMessage(final String testGroup, final String testName, final String metric, final long value, final String code, final long currValue, final double variation) {
82 | StringBuilder message = new StringBuilder(MESSAGE_START);
83 | if (testGroup != null && !testGroup.isEmpty()) {
84 | message.append(MESSAGE_KEY_TESTS_GROUP_NAME).append(testGroup).append(DELIMITER);
85 | }
86 | message.append(MESSAGE_KEY_TEST_NAME).append(testName).append(DELIMITER)
87 | .append(MESSAGE_KEY_METRIC).append(metric).append(DELIMITER);
88 | if (code != null && !code.isEmpty()) {
89 | message.append(MESSAGE_KEY_CODE_LABEL).append(code).append(DELIMITER);
90 | }
91 |
92 | message.append(MESSAGE_KEY_WARNING).append(true).append(DELIMITER);
93 | message.append(MESSAGE_KEY_WARNING_CURRENT_VALUE).append(currValue).append(DELIMITER);
94 | message.append(MESSAGE_KEY_WARNING_VARIATION).append(variation).append(DELIMITER);
95 | message.append(MESSAGE_KEY_VALUE).append(value).append(MESSAGE_END);
96 |
97 | return message.toString();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/server/src/perf_statistic/server/remote_monitoring/RemotePerfMonLogDataProvider.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.server.remote_monitoring;
2 |
3 | import com.intellij.util.containers.SortedList;
4 | import jetbrains.buildServer.log.Loggers;
5 | import jetbrains.buildServer.util.StringUtil;
6 | import org.jetbrains.annotations.NotNull;
7 | import perf_statistic.common.PerformanceMessageParser;
8 | import perf_statistic.common.PluginConstants;
9 | import perf_statistic.server.remote_monitoring.graph.Graph;
10 |
11 | import java.io.*;
12 | import java.util.*;
13 | import java.util.regex.Pattern;
14 |
15 | public class RemotePerfMonLogDataProvider {
16 | protected static Pattern delimiter = PerformanceMessageParser.DELIMITER_PATTERN;
17 |
18 | protected Map metrics;
19 |
20 | public RemotePerfMonLogDataProvider() {
21 | metrics = new HashMap();
22 | metrics.put("memory", new RemotePerfMonChart("memory", "Memory", "time", "byte", 2));
23 | metrics.put("cpu", new RemotePerfMonChart("cpu", "CPU", "time", "%", 0));
24 | metrics.put("disks", new RemotePerfMonChart("disks", "Disks", "time", "ops", 1));
25 | metrics.put("jmx_gc", new RemotePerfMonChart("jmx_gc","JMX: garbage collection", "time", "ms", 3));
26 | metrics.put("jmx_class_count", new RemotePerfMonChart("jmx_class_count", "JMX: class count", "time", "", 4));
27 | }
28 | protected void readLines(@NotNull final File file) {
29 | BufferedReader reader = null;
30 | try {
31 | reader = new BufferedReader(new FileReader(file));
32 | while (reader.ready()) {
33 | String[] items = delimiter.split(reader.readLine().trim());
34 | if (checkItem(items)) {
35 | processLine(items);
36 | }
37 | }
38 | } catch (FileNotFoundException e) {
39 | Loggers.STATS.error(PluginConstants.FEATURE_TYPE_REMOTE_MONITORING + " plugin error. File " + file.getAbsolutePath() + " not found!", e);
40 | } catch (IOException e) {
41 | Loggers.STATS.error(PluginConstants.FEATURE_TYPE_REMOTE_MONITORING + " plugin error. Error reading file " + file.getAbsolutePath(), e);
42 | } finally {
43 | if (reader != null) {
44 | try {
45 | reader.close();
46 | } catch (IOException e) {
47 | Loggers.STATS.error(PluginConstants.FEATURE_TYPE_REMOTE_MONITORING + " plugin error. Error closing file " + file.getAbsolutePath(), e);
48 | }
49 | }
50 | }
51 | }
52 |
53 | private boolean checkItem(String[] values) {
54 | if (values.length < 3) {
55 | Loggers.STATS.error(PluginConstants.FEATURE_TYPE_REMOTE_MONITORING + " plugin error. \nItem: timestamp\tresultValue\ttestName \n Found: " + Arrays.toString(values));
56 | return false;
57 | }
58 | return (values[0].matches("\\d+") && values[1].matches("[0-9]*\\.?[0-9]*([Ee][+-]?[0-9]+)?"));
59 | }
60 |
61 |
62 | public void processLine(String... itemValues) {
63 | long timeStamp = Long.parseLong(itemValues[0]);
64 | if (!StringUtil.isEmpty(itemValues[1])) {
65 | double value = Double.parseDouble(itemValues[1]);
66 | String label = itemValues[2];
67 |
68 | // TODO old format support: remove code after all older monitoring results will be removed;
69 | if (label.contains("labs.intellij.net")) {
70 | label = correctOldPerfMonFormat(label);
71 | value = value / 1000;
72 | }
73 |
74 | Graph metric = getMetric(label);
75 | metric.addValue(timeStamp, Math.round(value), label);
76 | }
77 | }
78 |
79 | private String correctOldPerfMonFormat(String label) {
80 | if (label != null) {
81 | label = label.toLowerCase();
82 | if (label.contains("memory")) {
83 | label = (label.contains("jmx") ? "jmx" : "") + label.substring(label.indexOf("memory"));
84 | }
85 | if (label.contains("cpu")) {
86 | label = label.substring(label.indexOf("cpu"));
87 | }
88 | if (label.contains("disks")) {
89 | label = label.substring(label.indexOf("disks"));
90 | }
91 | if (label.contains("gc-time")) {
92 | label = "jmx gc-time";
93 | }
94 | if (label.contains("class-count")) {
95 | label = "jmx class-count";
96 | }
97 | }
98 | return label;
99 | }
100 |
101 | private Graph getMetric(String label) {
102 | if (label != null && (label.contains("memory") || label.contains("swap"))) {
103 | return metrics.get("memory");
104 | }
105 | if (label != null && label.contains("cpu")) {
106 | return metrics.get("cpu");
107 | }
108 | if (label != null && label.contains("disks")) {
109 | return metrics.get("disks");
110 | }
111 | if (label != null && label.contains("gc-time")) {
112 | return metrics.get("jmx_gc");
113 | }
114 | if (label != null && label.contains("class-count")) {
115 | return metrics.get("jmx_class_count");
116 | }
117 | return RemotePerfMonChart.UNKNOWN_GRAPH;
118 | }
119 |
120 | public Collection getGraphs(@NotNull final File file) {
121 | readLines(file);
122 | List sortedMetricDescriptors = new SortedList(new Comparator() {
123 | @Override
124 | public int compare(Graph o1, Graph o2) {
125 | return o1.compareTo(o2);
126 | }
127 | });
128 | sortedMetricDescriptors.addAll(metrics.values());
129 | return sortedMetricDescriptors;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/server/resources/statistic/perfChartsCustom.js:
--------------------------------------------------------------------------------
1 | if (!window.$j) {
2 | window.$j = window.jQuery;
3 | }
4 |
5 | BS.PerfStat = {
6 | initPlot: function (container, data, yformat, label, logLines, logTitles) {
7 | container.css({
8 | width: 1400,
9 | height: 150
10 | });
11 |
12 | var settings = this.initSettings(yformat);
13 |
14 | var plot = window.$j.plot(container, data, settings);
15 | plot.unhighlightSerie = function (seriess) { };
16 | plot.showToolTip = function (seriess) { };
17 |
18 | function showTooltip(x, y, contents) {
19 | $j('' + contents + '
').css({
20 | position: 'absolute',
21 | display: 'none',
22 | top: y + 6,
23 | left: x + 12,
24 | border: '1px solid rgba(0, 0, 0, 0.2)',
25 | padding: '2px',
26 | 'background-color': '#ffffff',
27 | 'box-shadow': '0 5px 10px rgba(0, 0, 0, 0.2)'
28 | }).appendTo("body").fadeIn(200);
29 | }
30 |
31 | var previousPoint = null;
32 |
33 | container.unbind("plothover").bind("plothover", function (event, pos, item) {
34 | if (item) {
35 | if (previousPoint != item.dataIndex) {
36 | previousPoint = item.dataIndex;
37 | $j("#tooltip").remove();
38 | var value = item.datapoint[1];
39 | var msg = label + " " + value + " " + yformat + " ";
40 | if (logLines && logTitles && logLines[item.datapoint[0]]) {
41 | msg = msg + "
Log line details:";
42 | var parts = logLines[item.datapoint[0]];
43 | for (var i = 0; i < logTitles.length; i++) {
44 | if (i == 0) {
45 | msg = msg + "
" + logTitles[i] + ": " + BS.PerfStat.Format.formatTime(Number(parts[i])) + " (" + parts[i] + ")";
46 | } else {
47 | msg = msg + "
" + logTitles[i] + ": " + parts[i];
48 | }
49 | }
50 | }
51 | showTooltip(item.pageX, item.pageY, msg);
52 | }
53 | } else {
54 | $j("#tooltip").remove();
55 | previousPoint = null;
56 | }
57 | });
58 |
59 | container.unbind("plotselected").bind("plotselected", function (event, ranges) {
60 | if (ranges.xaxis) {
61 | var start = parseInt(ranges.xaxis.from.toFixed());
62 | var end = parseInt(ranges.xaxis.to.toFixed());
63 |
64 | plot = $j.plot(container, data,
65 | $j.extend(true, {xaxis: {min: start, max: end}}, settings));
66 | plot.unhighlightSerie = function (seriess) { };
67 | plot.showToolTip = function (seriess) { };
68 | } else {
69 | plot.clearSelection();
70 | }
71 | });
72 |
73 | container.unbind("plotunselected").bind("plotunselected", function () {
74 | plot = $j.plot(container, data, settings);
75 | plot.unhighlightSerie = function (seriess) { };
76 | plot.showToolTip = function (seriess) { };
77 | });
78 |
79 | container.unbind("plotclick");
80 | },
81 |
82 | initSettings: function (yformat) {
83 | return {
84 | series: {
85 | lines: { show: true },
86 | points: { show: true, radius: 1 }
87 | },
88 | grid: {
89 | hoverable: true,
90 | clickable: true
91 | },
92 | xaxis: {
93 | mode: 'time',
94 | tickFormatter: function (val) {
95 | return BS.PerfStat.Format.formatTime(val);
96 | }
97 | },
98 | yaxis: {
99 | tickFormatter: function (val) {
100 | return BS.PerfStat.Format.format(val, yformat, false, 0);
101 | },
102 | labelWidth: 50
103 | },
104 | legend: {
105 | show: false
106 | },
107 | selection: {
108 | mode: "x"
109 | },
110 | BSChart: {
111 | format: "duration",
112 | onHighlightSerie: function (series) {
113 | }
114 | }
115 | };
116 | }
117 | };
118 |
119 | BS.PerfStat.Format = {
120 | formatTime: function (value) {
121 | var date = new Date(value);
122 | return date.getHours() + ":" + this.toString2(date.getMinutes()) + ":" + this.toString2(date.getSeconds());
123 | },
124 |
125 | toString2: function (num) {
126 | if (num < 10) {
127 | return "0" + num;
128 | }
129 | return num;
130 | },
131 | // Y-axis and legend crosshair formatting
132 | bytes: "byte",
133 | percent: "%",
134 | ms: "ms",
135 | number: "",
136 |
137 | format: function(value, format, isBytes, precision) {
138 | if (isBytes) {
139 | return this._formatBytes(value, format);
140 | }
141 | return this._formatNumber(value, format, precision);
142 | },
143 | _formatBytes: function(bytes, format) {
144 | if (format == 'Gb')
145 | bytes = (bytes / 1073741824);
146 | if (format == 'Mb')
147 | bytes = (bytes / 1048576);
148 | if (format == 'Kb')
149 | bytes = (bytes / 1024);
150 | return bytes.toFixed(2) + ' ' + format;
151 | },
152 |
153 | _formatNumber: function(number, format, precision) {
154 | number = number.toFixed(precision);
155 | return number + ' ' + format;
156 | }
157 | };
158 |
--------------------------------------------------------------------------------
/server/src/perf_statistic/server/perf_test_charts/PerformanceStatisticTab.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.server.perf_test_charts;
2 |
3 | import jetbrains.buildServer.controllers.BuildDataExtensionUtil;
4 | import jetbrains.buildServer.serverSide.SBuild;
5 | import jetbrains.buildServer.serverSide.SBuildServer;
6 | import jetbrains.buildServer.serverSide.metadata.BuildMetadataEntry;
7 | import jetbrains.buildServer.serverSide.metadata.MetadataStorage;
8 | import jetbrains.buildServer.serverSide.statistics.ValueProviderRegistry;
9 | import jetbrains.buildServer.serverSide.statistics.build.BuildDataStorage;
10 | import jetbrains.buildServer.web.openapi.PagePlaces;
11 | import jetbrains.buildServer.web.openapi.PlaceId;
12 | import jetbrains.buildServer.web.openapi.PluginDescriptor;
13 | import jetbrains.buildServer.web.openapi.SimpleCustomTab;
14 | import jetbrains.buildServer.web.statistics.graph.BuildGraphHelper;
15 | import org.jetbrains.annotations.NotNull;
16 | import perf_statistic.common.PluginConstants;
17 | import perf_statistic.server.perf_tests.PerformanceBuildMetadataProvider;
18 | import perf_statistic.server.perf_tests.PerformanceTestProvider;
19 | import perf_statistic.server.perf_tests.PerformanceTestRun;
20 |
21 | import javax.servlet.http.HttpServletRequest;
22 | import java.util.Collection;
23 | import java.util.Iterator;
24 | import java.util.Map;
25 |
26 | public class PerformanceStatisticTab extends SimpleCustomTab {
27 | private static final String resourceURL = "statistic/tabPerfStat.jsp";
28 |
29 | private final BuildGraphHelper myGraphHelper;
30 |
31 | private final ValueProviderRegistry myRegistry;
32 | private final SBuildServer myServer;
33 | private final BuildDataStorage myStorage;
34 | private final MetadataStorage myMetadataStorage;
35 |
36 |
37 | private final PerformanceTestProvider myPerformanceTestHolder;
38 |
39 | public PerformanceStatisticTab(@NotNull final PagePlaces pagePlaces, @NotNull final SBuildServer server, final BuildDataStorage storage,
40 | @NotNull final ValueProviderRegistry valueProviderRegistry, @NotNull final BuildGraphHelper buildGraphHelper,
41 | @NotNull final PerformanceTestProvider testHolder, @NotNull final PluginDescriptor descriptor, @NotNull MetadataStorage metadataStorage) {
42 | super(pagePlaces, PlaceId.BUILD_RESULTS_TAB, "performanceTests", descriptor.getPluginResourcesPath(resourceURL), "Performance Statistics");
43 | myServer = server;
44 | myRegistry = valueProviderRegistry;
45 | myStorage = storage;
46 | myGraphHelper = buildGraphHelper;
47 | myPerformanceTestHolder = testHolder;
48 | myMetadataStorage = metadataStorage;
49 |
50 | addCssFile("/css/buildGraph.css");
51 |
52 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.js"));
53 | addJsFile(descriptor.getPluginResourcesPath("flot/excanvas.js"));
54 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.selection.js"));
55 | addJsFile(descriptor.getPluginResourcesPath("flot/jquery.flot.time.min.js"));
56 |
57 | addJsFile(descriptor.getPluginResourcesPath("statistic/perfChartsCustom.js"));
58 |
59 | register();
60 | }
61 |
62 | @Override
63 | public boolean isAvailable(@NotNull final HttpServletRequest request) {
64 | SBuild build = BuildDataExtensionUtil.retrieveBuild(request, myServer);
65 | return build != null && build.getBuildType() != null && !build.getBuildType().getBuildFeaturesOfType(PluginConstants.FEATURE_TYPE_AGGREGATION).isEmpty();
66 | }
67 |
68 | public void fillModel(@NotNull Map model, @NotNull HttpServletRequest request) {
69 | request.setAttribute("buildGraphHelper", myGraphHelper);
70 |
71 | SBuild build = BuildDataExtensionUtil.retrieveBuild(request, myServer);
72 | model.put("deselectedSeries", build.getBuildType().getCustomDataStorage(PluginConstants.STORAGE_ID_DEFAULT_DESELECTED_SERIES).getValues());
73 |
74 |
75 | Collection successTests = myPerformanceTestHolder.getSuccessTestRuns(build);
76 |
77 | for (Iterator it = myMetadataStorage.getBuildEntry(build.getBuildId(), PerformanceBuildMetadataProvider.PERFORMANCE_META_DATA_PROVIDER_ID); it.hasNext();) {
78 | BuildMetadataEntry entry = it.next();
79 | if (PerformanceBuildMetadataProvider.KEY_META_DATA_WARNINGS.equals(entry.getKey())) {
80 | Map warnings = entry.getMetadata();
81 | if (!warnings.isEmpty()) {
82 | for (PerformanceTestRun test : successTests) {
83 | String testID = test.getFullName();
84 | if (warnings.containsKey(testID)) {
85 | test.setWarning(warnings.get(testID));
86 | }
87 | }
88 | }
89 | }
90 | }
91 |
92 | model.put("performanceOKTests", successTests);
93 | model.put("performanceFailedTests", myPerformanceTestHolder.getFailedTestRuns(build));
94 |
95 | model.put("allTestNames", myPerformanceTestHolder.getAllTestNames(build));
96 | model.put("allTestGroups", myPerformanceTestHolder.getAllThreadGroups(build));
97 |
98 | model.put("build", build);
99 | model.put("statistic", build.getFullStatistics());
100 | model.put("isShowResponseCodes", build.getParametersProvider().get(PluginConstants.PARAMS_HTTP_RESPONSE_CODE) == null ? false : build.getParametersProvider().get(PluginConstants.PARAMS_HTTP_RESPONSE_CODE));
101 | model.put("isLogSaved", myPerformanceTestHolder.isLogAvailable(build));
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/server/src/perf_statistic/server/perf_test_charts/types/AbstractCompositeVT.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.server.perf_test_charts.types;
2 |
3 | import jetbrains.buildServer.serverSide.CustomDataStorage;
4 | import jetbrains.buildServer.serverSide.SBuild;
5 | import jetbrains.buildServer.serverSide.SBuildServer;
6 | import jetbrains.buildServer.serverSide.SBuildType;
7 | import jetbrains.buildServer.serverSide.statistics.BuildValueProvider;
8 | import jetbrains.buildServer.serverSide.statistics.ChartSettings;
9 | import jetbrains.buildServer.serverSide.statistics.ValueProviderRegistry;
10 | import jetbrains.buildServer.serverSide.statistics.build.*;
11 | import org.jetbrains.annotations.NotNull;
12 | import perf_statistic.common.PerformanceStatisticMetrics;
13 | import perf_statistic.common.PluginConstants;
14 |
15 | import java.util.*;
16 | import java.util.regex.Pattern;
17 |
18 | public abstract class AbstractCompositeVT extends CompositeVTB {
19 | protected static final Pattern comma_pattern = Pattern.compile(",");
20 |
21 | protected String[] subKeys;
22 | protected volatile String currentBuildTypeID;
23 |
24 | protected AbstractCompositeVT(BuildDataStorage buildDataStorage, ValueProviderRegistry valueProviderRegistry, SBuildServer server, String key) {
25 | super(buildDataStorage, valueProviderRegistry, server, key);
26 | }
27 |
28 | public abstract String getValueFormat();
29 | public abstract String getSeriesGenericName();
30 | public abstract String getSubKeysStorageKey();
31 | public abstract String getSubKey(String subKey);
32 |
33 |
34 | @Override
35 | public String[] getSubKeys() {
36 | if (subKeys != null) {
37 | String[] fullSubKeys = new String[subKeys.length];
38 | int i = 0;
39 | for(String subKey : subKeys) {
40 | fullSubKeys[i++] = getSubKey(subKey);
41 | }
42 | return fullSubKeys;
43 | }
44 | return new String[0];
45 | }
46 |
47 | @Override
48 | @NotNull
49 | public List getDataSet(@NotNull final ChartSettings _chartSettings) {
50 | if (_chartSettings instanceof BuildChartSettings) {
51 | if (subKeys == null) {
52 | BuildChartSettings settings = (BuildChartSettings) _chartSettings;
53 | updateKeys(settings.getBuildTypeId());
54 | setDefaultDeselectedKeys(settings);
55 | }
56 |
57 | return super.getDataSet(_chartSettings);
58 | }
59 | return Collections.emptyList();
60 | }
61 |
62 | protected void updateKeys(final String buildTypeID) {
63 | if (buildTypeID != null) {
64 | currentBuildTypeID = buildTypeID;
65 | SBuildType buildType = myServer.getProjectManager().findBuildTypeByExternalId(currentBuildTypeID);
66 | if (buildType != null) {
67 | CustomDataStorage storage = buildType.getCustomDataStorage(PluginConstants.STORAGE_ID_COMMON_JMETER);
68 | String value = storage.getValue(getSubKeysStorageKey());
69 | if (value != null) {
70 | subKeys = comma_pattern.split(value);
71 | }
72 | }
73 |
74 | }
75 | }
76 |
77 | @Override
78 | protected BuildValueProvider createValueProviderForSubkey(@NotNull final String subKey) {
79 | return new BuildValueTypeBase(myServer, myStorage, myValueProviderRegistry, subKey) {
80 | @NotNull
81 | public String getDescription(final ChartSettings chartSettings) {
82 | return subKey;
83 | }
84 | @NotNull
85 | public List getDataSet(@NotNull final ChartSettings chartSettings) {
86 | List values = myStorage.getDataSet(getKey(), (BuildChartSettings)chartSettings, getValueProcessor());
87 |
88 | for (ListIterator it = values.listIterator(); it.hasNext();) {
89 | BuildValue value = it.next();
90 | SBuild build = myServer.findBuildInstanceById(value.getBuildId());
91 | // clear data with removed builds or null values
92 | if (value.getBuildNumber() == null || value.getValue() == null || build == null) {
93 | it.remove();
94 | }
95 | }
96 | return values;
97 | }
98 | };
99 | }
100 |
101 | private void setDefaultDeselectedKeys(@NotNull final BuildChartSettings chartSettings){
102 | if (chartSettings.isResetToDefaultRequest() || !chartSettings.isUpdateRequest()) {
103 | SBuildType buildType = myServer.getProjectManager().findBuildTypeByExternalId(currentBuildTypeID);
104 | Set deselectedKeys = new HashSet();
105 |
106 | Map storage = buildType.getCustomDataStorage(PluginConstants.STORAGE_ID_DEFAULT_DESELECTED_SERIES).getValues();
107 |
108 | if (storage != null) {
109 | for (String key : storage.keySet()) {
110 | if ("true".equals(storage.get(key))) {
111 | PerformanceStatisticMetrics metric = PerformanceStatisticMetrics.getMetricByKey(key);
112 | if (metric != null) {
113 | deselectedKeys.add(metric.getTitle());
114 | deselectedKeys.add(metric.getReferenceTitle());
115 | }
116 | }
117 | }
118 | }
119 | chartSettings.setAll(BuildChartSettings.FILTER_S, deselectedKeys.toArray(new String[deselectedKeys.size()]));
120 | chartSettings.setSingle("_resetDefaults", ""); // hack, because default setting show all series
121 | }
122 | }
123 |
124 | @Override
125 | public boolean hasData(final ChartSettings buildChartSettings) {
126 | BuildChartSettings settings = (BuildChartSettings) buildChartSettings;
127 | updateKeys(settings.getBuildTypeId());
128 | setDefaultDeselectedKeys(settings);
129 | return subKeys != null && subKeys.length != 0;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/agent/src/perf_statistic/agent/metric_aggregation/AggregationProperties.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.agent.metric_aggregation;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import perf_statistic.common.PerformanceStatisticMetrics;
5 | import perf_statistic.common.PluginConstants;
6 |
7 | import java.io.File;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public class AggregationProperties {
14 | // Aggregate parameters
15 | private final String aggregateFile;
16 | private final boolean[] metrics = new boolean[5];
17 |
18 | private final boolean responseCodes;
19 | private final boolean assertions;
20 | private final boolean total;
21 |
22 | private final boolean usedTestGroups;
23 | private final boolean logResultsAsTests;
24 |
25 |
26 | // Check references values
27 | private final boolean checkReferences;
28 | private double criticalVariation;
29 | private double variation;
30 |
31 | private boolean isFileValues;
32 | private String referencesDataFile;
33 |
34 | private boolean isBuildHistoryValues;
35 | private int buildCount;
36 | private boolean[] refMetrics;
37 |
38 |
39 | public AggregationProperties(@NotNull Map params) {
40 | aggregateFile = params.get(PluginConstants.PARAMS_AGGREGATE_FILE);
41 |
42 | metrics[0] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_AVG));
43 | metrics[1] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_MAX));
44 | metrics[2] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_MIN));
45 | metrics[3] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_METRIC_LINE90));
46 | metrics[4] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_HTTP_RESPONSE_CODE));
47 |
48 | responseCodes = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_HTTP_RESPONSE_CODE));
49 | assertions = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_CHECK_ASSERT));
50 | total = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_CALC_TOTAL));
51 | usedTestGroups = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_TEST_GROUPS));
52 |
53 | logResultsAsTests = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_USED_TEST_FORMAT));
54 |
55 | checkReferences = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_CHECK));
56 | if (checkReferences) {
57 | String tmp = params.get(PluginConstants.PARAMS_VARIATION_CRITICAL);
58 | criticalVariation = tmp == null ? 0.05 : Double.parseDouble(tmp);
59 | tmp = params.get(PluginConstants.PARAMS_VARIATION_WARN);
60 | variation = tmp == null ? Double.NEGATIVE_INFINITY : Double.parseDouble(tmp);
61 |
62 | isFileValues = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_TYPE_FILE));
63 | isBuildHistoryValues = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_TYPE_BUILD_HISTORY));
64 | if (isFileValues) {
65 | referencesDataFile = params.get(PluginConstants.PARAMS_REF_DATA_FILE);
66 | }
67 | if (isBuildHistoryValues) {
68 | buildCount = Integer.parseInt(params.get(PluginConstants.PARAMS_REF_BUILD_COUNT));
69 | refMetrics = new boolean[3];
70 | refMetrics[0] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_METRIC_AVG));
71 | refMetrics[1] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_METRIC_LINE90));
72 | refMetrics[2] = Boolean.parseBoolean(params.get(PluginConstants.PARAMS_REF_METRIC_MAX));
73 | }
74 | }
75 | }
76 |
77 | //Aggregate parameter's getters
78 | @NotNull
79 | public String getAggregateDataFile(@NotNull final String workingDir) {
80 | if (aggregateFile == null) {
81 | throw new IllegalArgumentException("File for aggregation must not be null!");
82 | }
83 | return workingDir + File.separator + aggregateFile;
84 | }
85 | @NotNull
86 | public String getAggregateDataFile() {
87 | return aggregateFile;
88 | }
89 |
90 | public List getSelectedMetrics() {
91 | PerformanceStatisticMetrics[] values = PerformanceStatisticMetrics.values();
92 | if (metrics.length > values.length) {
93 | return Collections.emptyList(); // Can not remove
94 | }
95 | List metricList = new ArrayList();
96 | for(int i = 0 ; i < metrics.length; i++) {
97 | if (metrics[i])
98 | metricList.add(values[i]);
99 | }
100 | return metricList;
101 | }
102 |
103 | public boolean isCalculateResponseCodes() {
104 | return responseCodes;
105 | }
106 | public boolean isCalculateTotal() {
107 | return total;
108 | }
109 | public boolean isCheckAssertions() {
110 | return assertions;
111 | }
112 |
113 | public boolean isUsedTestGroups() {
114 | return usedTestGroups;
115 | }
116 |
117 | public boolean isLogResultsAsTests() {
118 | return logResultsAsTests;
119 | }
120 |
121 | public boolean isCheckReferences() {
122 | return checkReferences;
123 | }
124 |
125 | public boolean isFileValues() {
126 | return isFileValues;
127 | }
128 |
129 | public boolean isBuildHistoryValues() {
130 | return isBuildHistoryValues;
131 | }
132 |
133 | public int getBuildCount() {
134 | return buildCount;
135 | }
136 |
137 | public boolean isCountMaxReference() {
138 | return refMetrics[2];
139 | }
140 | public boolean isCountAverageReference() {
141 | return refMetrics[0];
142 | }
143 | public boolean isCount90LineReference() {
144 | return refMetrics[1];
145 | }
146 |
147 | public String getReferencesDataFile(String workingDir) {
148 | if (aggregateFile == null) {
149 | throw new IllegalArgumentException("File with references values must not be null!");
150 | }
151 | return workingDir + File.separator + referencesDataFile;
152 | }
153 |
154 | public double getCriticalVariation() {
155 | return criticalVariation;
156 | }
157 |
158 | public double getVariation() {
159 | return variation;
160 | }
161 |
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/server/resources/flot/jquery.flot.crosshair.js:
--------------------------------------------------------------------------------
1 | /*
2 | Flot plugin for showing crosshairs, thin lines, when the mouse hovers
3 | over the plot.
4 |
5 | crosshair: {
6 | mode: null or "x" or "y" or "xy"
7 | color: color
8 | lineWidth: number
9 | }
10 |
11 | Set the mode to one of "x", "y" or "xy". The "x" mode enables a
12 | vertical crosshair that lets you trace the values on the x axis, "y"
13 | enables a horizontal crosshair and "xy" enables them both. "color" is
14 | the color of the crosshair (default is "rgba(170, 0, 0, 0.80)"),
15 | "lineWidth" is the width of the drawn lines (default is 1).
16 |
17 | The plugin also adds four public methods:
18 |
19 | - setCrosshair(pos)
20 |
21 | Set the position of the crosshair. Note that this is cleared if
22 | the user moves the mouse. "pos" is in coordinates of the plot and
23 | should be on the form { x: xpos, y: ypos } (you can use x2/x3/...
24 | if you're using multiple axes), which is coincidentally the same
25 | format as what you get from a "plothover" event. If "pos" is null,
26 | the crosshair is cleared.
27 |
28 | - clearCrosshair()
29 |
30 | Clear the crosshair.
31 |
32 | - lockCrosshair(pos)
33 |
34 | Cause the crosshair to lock to the current location, no longer
35 | updating if the user moves the mouse. Optionally supply a position
36 | (passed on to setCrosshair()) to move it to.
37 |
38 | Example usage:
39 | var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } };
40 | $("#graph").bind("plothover", function (evt, position, item) {
41 | if (item) {
42 | // Lock the crosshair to the data point being hovered
43 | myFlot.lockCrosshair({ x: item.datapoint[0], y: item.datapoint[1] });
44 | }
45 | else {
46 | // Return normal crosshair operation
47 | myFlot.unlockCrosshair();
48 | }
49 | });
50 |
51 | - unlockCrosshair()
52 |
53 | Free the crosshair to move again after locking it.
54 | */
55 |
56 | (function ($) {
57 | var options = {
58 | crosshair: {
59 | mode: null, // one of null, "x", "y" or "xy",
60 | color: "rgba(170, 0, 0, 0.80)",
61 | lineWidth: 1
62 | }
63 | };
64 |
65 | function init(plot) {
66 | // position of crosshair in pixels
67 | var crosshair = { x: -1, y: -1, locked: false };
68 |
69 | plot.setCrosshair = function setCrosshair(pos) {
70 | if (!pos)
71 | crosshair.x = -1;
72 | else {
73 | var o = plot.p2c(pos);
74 | crosshair.x = Math.max(0, Math.min(o.left, plot.width()));
75 | crosshair.y = Math.max(0, Math.min(o.top, plot.height()));
76 | }
77 |
78 | plot.triggerRedrawOverlay();
79 | };
80 |
81 | plot.clearCrosshair = plot.setCrosshair; // passes null for pos
82 |
83 | plot.lockCrosshair = function lockCrosshair(pos) {
84 | if (pos)
85 | plot.setCrosshair(pos);
86 | crosshair.locked = true;
87 | }
88 |
89 | plot.unlockCrosshair = function unlockCrosshair() {
90 | crosshair.locked = false;
91 | }
92 |
93 | function onMouseOut(e) {
94 | if (crosshair.locked)
95 | return;
96 |
97 | if (crosshair.x != -1) {
98 | crosshair.x = -1;
99 | plot.triggerRedrawOverlay();
100 | }
101 | }
102 |
103 | function onMouseMove(e) {
104 | if (crosshair.locked)
105 | return;
106 |
107 | if (plot.getSelection && plot.getSelection()) {
108 | crosshair.x = -1; // hide the crosshair while selecting
109 | return;
110 | }
111 |
112 | var offset = plot.offset();
113 | crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));
114 | crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));
115 | plot.triggerRedrawOverlay();
116 | }
117 |
118 | plot.hooks.bindEvents.push(function (plot, eventHolder) {
119 | if (!plot.getOptions().crosshair.mode)
120 | return;
121 |
122 | eventHolder.mouseout(onMouseOut);
123 | eventHolder.mousemove(onMouseMove);
124 | });
125 |
126 | plot.hooks.drawOverlay.push(function (plot, ctx) {
127 | var c = plot.getOptions().crosshair;
128 | if (!c.mode)
129 | return;
130 |
131 | var plotOffset = plot.getPlotOffset();
132 |
133 | ctx.save();
134 | ctx.translate(plotOffset.left, plotOffset.top);
135 |
136 | if (crosshair.x != -1) {
137 | ctx.strokeStyle = c.color;
138 | ctx.lineWidth = c.lineWidth;
139 | ctx.lineJoin = "round";
140 |
141 | ctx.beginPath();
142 | if (c.mode.indexOf("x") != -1) {
143 | ctx.moveTo(crosshair.x, 0);
144 | ctx.lineTo(crosshair.x, plot.height());
145 | }
146 | if (c.mode.indexOf("y") != -1) {
147 | ctx.moveTo(0, crosshair.y);
148 | ctx.lineTo(plot.width(), crosshair.y);
149 | }
150 | ctx.stroke();
151 | }
152 | ctx.restore();
153 | });
154 |
155 | plot.hooks.shutdown.push(function (plot, eventHolder) {
156 | eventHolder.unbind("mouseout", onMouseOut);
157 | eventHolder.unbind("mousemove", onMouseMove);
158 | });
159 | }
160 |
161 | $.plot.plugins.push({
162 | init: init,
163 | options: options,
164 | name: 'crosshair',
165 | version: '1.0'
166 | });
167 | })(jQuery);
168 |
--------------------------------------------------------------------------------
/server/src/perf_statistic/server/perf_tests/PerformanceBuildMetadataProvider.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.server.perf_tests;
2 |
3 | import jetbrains.buildServer.serverSide.CustomDataStorage;
4 | import jetbrains.buildServer.serverSide.SBuild;
5 | import jetbrains.buildServer.serverSide.SBuildType;
6 | import jetbrains.buildServer.serverSide.buildLog.BuildLog;
7 | import jetbrains.buildServer.serverSide.buildLog.LogMessage;
8 | import jetbrains.buildServer.serverSide.metadata.BuildMetadataProvider;
9 | import jetbrains.buildServer.serverSide.metadata.MetadataStorageWriter;
10 | import jetbrains.buildServer.serverSide.statistics.build.BuildDataStorage;
11 | import org.jetbrains.annotations.NotNull;
12 | import perf_statistic.common.PerformanceMessage;
13 | import perf_statistic.common.PerformanceMessageParser;
14 | import perf_statistic.common.PerformanceStatisticMetrics;
15 | import perf_statistic.common.PluginConstants;
16 |
17 | import java.math.BigDecimal;
18 | import java.util.*;
19 | import java.util.regex.Pattern;
20 |
21 | public class PerformanceBuildMetadataProvider implements BuildMetadataProvider {
22 | private static final Pattern comma_pattern = Pattern.compile(",");
23 | private static final Pattern non_word_pattern = Pattern.compile("\\W");
24 |
25 | public static final String PERFORMANCE_META_DATA_PROVIDER_ID = "performanceMetaData";
26 |
27 | public static final String KEY_META_DATA_WARNINGS = "performance.warnings";
28 | private final BuildDataStorage myStorage;
29 |
30 |
31 | public PerformanceBuildMetadataProvider(final BuildDataStorage storage) {
32 | myStorage = storage;
33 | }
34 |
35 | @NotNull
36 | @Override
37 | public String getProviderId() {
38 | return PERFORMANCE_META_DATA_PROVIDER_ID;
39 | }
40 |
41 | @Override
42 | public void generateMedatadata(@NotNull SBuild build, @NotNull MetadataStorageWriter metadataStorageWriter) {
43 | SBuildType buildType = build.getBuildType();
44 | if (buildType == null || buildType.getBuildFeaturesOfType(PluginConstants.FEATURE_TYPE_AGGREGATION).isEmpty())
45 | return;
46 |
47 | Map warnings = new HashMap();
48 |
49 | List serviceMessages = getJMeterServiceMessages(build.getBuildLog());
50 | if (!serviceMessages.isEmpty()) {
51 | long buildId = build.getBuildId();
52 | String buildTypeId = build.getBuildTypeExternalId();
53 |
54 | CustomDataStorage sampleAliasStorage = buildType.getCustomDataStorage(PluginConstants.STORAGE_ID_TEST_ALIAS);
55 |
56 | Set metrics = new HashSet();
57 | Set codes = new HashSet();
58 | Set samples = new HashSet();
59 |
60 | for (PerformanceMessage message : serviceMessages) {
61 | String testsGroup = message.getTestsGroupName();
62 | String testName = message.getTestName();
63 | String metricValue = message.getMetric();
64 | String code = message.getCode();
65 | String value = message.getValue();
66 | boolean warning = message.isWarning();
67 |
68 | if (warning) {
69 | String key = !testsGroup.isEmpty() ? testsGroup + " : " + testName : testName;
70 | String warn = warnings.get(key);
71 | String msg = "Exceed variation: metric - " + PerformanceStatisticMetrics.getNonReferenceTitleByKey(metricValue)
72 | + "; reference value - " + value + "; current value - " + message.getCurrValue() + "; variation - " + message.getVariation();
73 | warnings.put(key, warn == null ? msg : warn + "
" + msg);
74 | }
75 |
76 | String alias;
77 | if (testsGroup.isEmpty()) {
78 | alias = non_word_pattern.matcher(testName).replaceAll("");
79 | updateStorageValue(sampleAliasStorage, alias, testName);
80 | } else {
81 | alias = non_word_pattern.matcher(testsGroup + testName).replaceAll("");
82 | updateStorageValue(sampleAliasStorage, alias, testsGroup + ": " + testName);
83 | }
84 |
85 | if (code != null && !code.isEmpty()) {
86 | alias += non_word_pattern.matcher(code).replaceAll("");
87 | }
88 | myStorage.publishValue(buildTypeId + '_' + metricValue + '_' + alias, buildId, new BigDecimal(value));
89 |
90 | if (metricValue.equals(PerformanceStatisticMetrics.RESPONSE_CODE.getKey())) {
91 | codes.add(code);
92 | } else {
93 | metrics.add(metricValue);
94 | samples.add(alias);
95 | }
96 | }
97 | // updateStorageValue(sampleOrderStorage, PerformanceStatisticMetrics.RESPONSE_CODE.getKey(), "0"); // response codes is the first
98 |
99 | CustomDataStorage commonStorage = buildType.getCustomDataStorage(PluginConstants.STORAGE_ID_COMMON_JMETER);
100 | updateStorageValue(commonStorage, PluginConstants.STORAGE_KEY_METRIC, metrics);
101 | updateStorageValue(commonStorage, PluginConstants.STORAGE_KEY_CODE, codes);
102 | updateStorageValue(commonStorage, PluginConstants.STORAGE_KEY_SAMPLES, samples);
103 | }
104 |
105 | if (!warnings.isEmpty()) {
106 | metadataStorageWriter.addParameters(KEY_META_DATA_WARNINGS, warnings);
107 | }
108 | }
109 |
110 | /**
111 | * parses build log, returns collection of service messages provided by jmeter agent runner
112 | *
113 | * @param log
114 | * @return
115 | */
116 | private List getJMeterServiceMessages(@NotNull BuildLog log) {
117 | List messages = new ArrayList();
118 | for (Iterator iterator = log.getMessagesIterator(); iterator.hasNext(); ) {
119 | PerformanceMessage message = PerformanceMessageParser.getPerformanceTestingMessage(iterator.next().getText().trim());
120 | if (message != null) {
121 | messages.add(message);
122 | }
123 | }
124 | return messages;
125 | }
126 |
127 | private void updateStorageValue(@NotNull CustomDataStorage storage, @NotNull String key, @NotNull String value) {
128 | String oldValue = storage.getValue(key);
129 | if (oldValue == null || !value.equals(oldValue)) {
130 | storage.putValue(key, value);
131 | storage.flush();
132 | }
133 | }
134 |
135 | private void updateStorageValue(@NotNull CustomDataStorage storage, @NotNull String key, @NotNull Collection values) {
136 | String parametersValues = storage.getValue(key);
137 | if (parametersValues != null) {
138 | Collections.addAll(values, comma_pattern.split(parametersValues));
139 | }
140 | StringBuilder builder = new StringBuilder();
141 | for (String value : values) {
142 | builder.append(value).append(',');
143 | }
144 | storage.putValue(key, builder.toString());
145 | storage.flush();
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/agent/src/perf_statistic/agent/remote_monitoring/RemoteMonitoring.java:
--------------------------------------------------------------------------------
1 | package perf_statistic.agent.remote_monitoring;
2 |
3 | import org.apache.log4j.Logger;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.io.*;
7 | import java.net.Socket;
8 | import java.util.Queue;
9 | import java.util.concurrent.ConcurrentLinkedQueue;
10 |
11 | public final class RemoteMonitoring {
12 | public static final Logger CLASS_LOGGER = Logger.getLogger(RemoteMonitoring.class);
13 |
14 | private static final String METRICS_COMMAND = "metrics:cpu:combined\tcpu:user\tcpu:system\tcpu:iowait\t"
15 | + "memory:used\t"
16 | + "disks:reads\tdisks:writes\t"
17 | + "swap:used\t"
18 | + "jmx:url=localhost\\:4711:gc-time\t"
19 | + "jmx:url=localhost\\:4711:class-count\t"
20 | // + "jmx:url=localhost\\:4711:memory-usage\t"
21 | // + "jmx:url=localhost\\:4711:memory-committed\t"
22 | + "jmx:url=localhost\\:4711:memorypool-usage\t"
23 | + "jmx:url=localhost\\:4711:memorypool-committed\t\n";
24 |
25 | private volatile boolean stopped;
26 | @NotNull private final Queue monitoringResults;
27 | @NotNull private final Thread getter;
28 | @NotNull private final Thread processor;
29 |
30 |
31 | public RemoteMonitoring(final RemoteMonitoringProperties properties, final String resultFile) {
32 | stopped = false;
33 | monitoringResults = new ConcurrentLinkedQueue();
34 |
35 | getter = new Thread(){
36 | private long interval = 0;
37 |
38 | private Socket socket = null;
39 | private BufferedWriter writer = null;
40 | private BufferedReader reader = null;
41 |
42 | @Override
43 | public void run() {
44 | try {
45 | socket = new Socket(properties.getRemoteMonitoringHost(), properties.getRemoteMonitoringPort());
46 | writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
47 | reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
48 |
49 | if (socket.isConnected()) {
50 | writer.write("test\n");
51 | writer.flush();
52 | String testResult = reader.readLine();
53 | if (testResult != null && "Yep".equals(testResult)) {
54 | setInterval(properties.getRemoteInterval());
55 | writer.write(METRICS_COMMAND);
56 | writer.flush();
57 |
58 | while (!stopped && socket.isConnected()) {
59 | processResults();
60 | }
61 | }
62 | writer.write("shutdown\n");
63 | }
64 | } catch (IOException e) {
65 | CLASS_LOGGER.error(e);
66 | } finally {
67 | if (reader != null) {
68 | try {
69 | writer.close();
70 | } catch (IOException e) {
71 | CLASS_LOGGER.error(e);
72 | }
73 | }
74 | if (writer != null) {
75 | try {
76 | writer.close();
77 | } catch (IOException e) {
78 | CLASS_LOGGER.error(e);
79 | }
80 | }
81 | if (socket != null && !socket.isClosed()) {
82 | try {
83 | socket.close();
84 | } catch (IOException e) {
85 | CLASS_LOGGER.error(e);
86 | }
87 | }
88 | }
89 | }
90 |
91 | private void setInterval(String strInterval) throws IOException {
92 | if (strInterval != null) {
93 | interval = Long.parseLong(strInterval) * 1000;
94 | writer.write("interval:" + strInterval + "\n");
95 | writer.flush();
96 | }
97 | }
98 |
99 | private void processResults() throws IOException {
100 | long start = System.currentTimeMillis();
101 | String line = reader.readLine();
102 | monitoringResults.add(line + start);
103 | long spend = System.currentTimeMillis() - start;
104 | if (spend < interval) {
105 | try {
106 | Thread.sleep(interval - spend);
107 | } catch (InterruptedException e) {
108 | CLASS_LOGGER.error(e);
109 | }
110 | }
111 | }
112 | };
113 |
114 | processor = new Thread(){
115 | private long interval = 1000;
116 | private long delay = properties.getRemoteClockDelay();
117 |
118 | @Override
119 | public void run() {
120 | if (properties.getRemoteInterval() != null) {
121 | interval = Long.parseLong(properties.getRemoteInterval()) * 1000;
122 | }
123 | BufferedWriter resultWriter = null;
124 | try {
125 | resultWriter = new BufferedWriter(new FileWriter(resultFile));
126 | while (true) {
127 | if (!monitoringResults.isEmpty()) {
128 | processLine(resultWriter, monitoringResults.poll());
129 | } else if (!stopped) {
130 | try {
131 | Thread.sleep(interval);
132 | } catch (InterruptedException e) {
133 | CLASS_LOGGER.error(e);
134 | }
135 | } else {
136 | break;
137 | }
138 | }
139 | } catch (IOException e) {
140 | CLASS_LOGGER.error(e);
141 | } finally {
142 | if (resultWriter != null) {
143 | try {
144 | resultWriter.close();
145 | } catch (IOException e) {
146 | CLASS_LOGGER.error(e);
147 | }
148 | }
149 | }
150 | }
151 |
152 | private void processLine(BufferedWriter writer, String line) throws IOException {
153 | String[] parts = line.split("\t");
154 | if (parts.length < 13) {
155 | return;
156 | }
157 | long time = Long.parseLong(parts[12]) + delay;
158 |
159 | writer.write(String.valueOf(time) + "\t" + parts[0] + "\tcpu\t\n");
160 | writer.write(String.valueOf(time) + "\t" + parts[1] + "\tcpu user\t\n");
161 | writer.write(String.valueOf(time) + "\t" + parts[2] + "\tcpu system\t\n");
162 | writer.write(String.valueOf(time) + "\t" + parts[3] + "\tcpu iowait\t\n");
163 | writer.write(String.valueOf(time) + "\t" + parts[4] + "\tmemory used\t\n");
164 | writer.write(String.valueOf(time) + "\t" + parts[5] + "\tdisks reads\t\n");
165 | writer.write(String.valueOf(time) + "\t" + parts[6] + "\tdisks writes\t\n");
166 | writer.write(String.valueOf(time) + "\t" + parts[7] + "\tswap used\t\n");
167 | writer.write(String.valueOf(time) + "\t" + parts[8] + "\tjmx gc-time\t\n");
168 | writer.write(String.valueOf(time) + "\t" + parts[9] + "\tjmx class-count\t\n");
169 | // writer.write(String.valueOf(time) + "\t" + parts[9] + "\tjmx memory-usage\t\n");
170 | // writer.write(String.valueOf(time) + "\t" + parts[10] + "\tjmx memory-committed\t\n");
171 | writer.write(String.valueOf(time) + "\t" + parts[10] + "\tjmx memorypool-usage\t\n");
172 | writer.write(String.valueOf(time) + "\t" + parts[11] + "\tjmx memorypool-committed\t\n");
173 | writer.flush();
174 | }
175 | };
176 |
177 | }
178 |
179 | public void start() {
180 | getter.start();
181 | processor.start();
182 | }
183 |
184 | public void stop() {
185 | stopped = true;
186 | try {
187 | getter.join();
188 | processor.join();
189 | } catch (InterruptedException e) {
190 | CLASS_LOGGER.error(e);
191 | }
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | Performance tests analysis plugin for TeamCity
2 | ==============================================
3 | This is plugin for TeamCity 8.0 that helps to organize simplest performance testing in CI.
4 | It has the ability to aggregate results from a log file, calculate metrics,
5 | compare results with reference values, monitor a remote machine with the tested application.
6 | Additionally, it allows viewing all the results of performance tests as charts.
7 |
8 | Last version: https://teamcity.jetbrains.com/repository/download/TeamCityPluginsByJetBrains_JMeterPlugin_Build/.lastSuccessful/jmeter.zip
9 |
10 | How it works
11 | ==============
12 | The plugin has two features:
13 | 1) Performance Metrics Calculation - a build feature to configure settings for calcuation performance metrics;
14 | 2) Performance Statistic - a tab with values of average metrics per test by builds;
15 |
16 | 3) Performance Remote Monitoring - a build feature to configure plugin settings for build configuration;
17 | 4) RemotePerfMon - a tab with monitoring results for the build.
18 |
19 |
20 | Performance Metrics Calculation
21 | ===========================================
22 | required - *
23 |
24 | Aggregation:
25 | -------------------------------------------
26 | File to aggregate results: *
27 | a relative path to the file with the raw test results to calculate average metrics;
28 | Aggregate metrics: *
29 | average, min, max, 90% line,
30 | response codes: it allows to calculated count of each code in test results - http code, or some other test result.
31 | (for example you can log some id for exception occurred during test run: OK, InternalError, NPE)
32 | note: response code NOT fail build,
33 | to DETECT errors or non-200 http codes and fail build in this case use assertions!
34 | Format settings:
35 | total - calculate total average values for all tests; Note: if thread groups are used, total will be calculated by groups
36 | assertions - fail the build if any assertion check fails,
37 | thread groups - you can group tests in thread groups(20,100 threads..), in this case, test name must have the specific format at the log
38 |
39 | used TeamCity tests format
40 | This option must be set if your tests are not created and run using Test framework, like TestNG, JUnit; and you aren't use service messages to log test result in build log.
41 |
42 | Check reference data (optionally):
43 | The build will be failed if the aggregated values exceed reference values considering variation.
44 | -------------------------------------------
45 | Get reference values from: *
46 | - file - set if you have a static reference values. In this case, set relative path to reference data;
47 | - builds - not supported yet
48 | Variation:
49 | the value of variation [0..1] in decimal format; default - 0.05 (5%);
50 |
51 |
52 |
53 |
54 |
55 |
56 | Performance Remote Monitoring:
57 | ===========================================
58 | allows monitoring some system and jvm statistics on the remote machine with the tested application.
59 | -------------------------------------------
60 | Build step to analyze: *
61 | the name of the build step which starts tests.
62 | Note: The plugin uses Server Agent(http://jmeter-plugins.org/wiki/PerfMonAgent/) to collect metric values on the remoted machine. So, the agent must be run on the remote machine before the build step begins.
63 |
64 | Remote machine: *
65 | host *, port * - settings for access to the running agent;
66 | clock delay - if tests will be performed from another machine(non-BuildAgent), set system clock delay between build agent and test machine to sync time of monitoring.
67 |
68 | Monitoring interval: * - in seconds, to collect metrics from agent;
69 |
70 | Monitored parameters, supported by plugin:
71 | - CPU system/user/all/iowait (all in percent)
72 | - Memory used (bytes)
73 | - Disk I/O reads/writes (ops)
74 | - JMX memory heap, pools commited/usage; gc time; class loaded;
75 |
76 |
77 |
78 | Formats of the file to aggregate results:
79 | ==========================================
80 | The first line must contain the titles of columns.
81 | Delimiter - \t
82 | Label is the same test/sample name;
83 | If thread groups are used; label must have format: :