From bd8b755e55c0359bcdaaa4f8aab33ab28d854203 Mon Sep 17 00:00:00 2001 From: Filip Drobnjakovic Date: Fri, 26 May 2023 15:35:39 +0200 Subject: [PATCH] Collectors from PA-RCA move. Signed-off-by: Filip Drobnjakovic --- build.gradle | 5 + .../commons/OSMetricsGeneratorFactory.java | 31 ++ .../commons/collectors/CachedStats.java | 42 ++ .../commons/collectors/DiskMetrics.java | 53 +++ .../commons/collectors/DisksCollector.java | 88 ++++ .../commons/collectors/GCInfoCollector.java | 91 ++++ .../collectors/HeapMetricsCollector.java | 177 ++++++++ .../commons/collectors/MetricStatus.java | 21 + .../collectors/MountedPartitionMetrics.java | 59 +++ .../MountedPartitionMetricsCollector.java | 82 ++++ .../collectors/NetInterfaceSummary.java | 74 +++ .../collectors/NetworkE2ECollector.java | 88 ++++ .../collectors/NetworkInterfaceCollector.java | 104 +++++ .../ScheduledMetricCollectorsExecutor.java | 165 +++++++ .../commons/collectors/TCPStatus.java | 84 ++++ .../commons/hwnet/Disks.java | 162 +++++++ .../commons/hwnet/MountedPartitions.java | 100 ++++ .../commons/hwnet/NetworkE2E.java | 212 +++++++++ .../commons/hwnet/NetworkInterface.java | 382 ++++++++++++++++ .../commons/jvm/GCMetrics.java | 133 ++++++ .../commons/jvm/GarbageCollectorInfo.java | 78 ++++ .../commons/jvm/HeapMetrics.java | 61 +++ .../commons/jvm/ThreadList.java | 426 ++++++++++++++++++ .../commons/metrics/MetricsConfiguration.java | 22 + .../CPUPagingActivityGenerator.java | 23 + .../DiskIOMetricsGenerator.java | 29 ++ .../DiskMetricsGenerator.java | 21 + .../metrics_generator/IPMetricsGenerator.java | 30 ++ .../MountedPartitionMetricsGenerator.java | 23 + .../metrics_generator/OSMetricsGenerator.java | 30 ++ .../SchedMetricsGenerator.java | 21 + .../TCPMetricsGenerator.java | 28 ++ .../LinuxCPUPagingActivityGenerator.java | 76 ++++ .../linux/LinuxDiskIOMetricsGenerator.java | 72 +++ .../linux/LinuxDiskMetricsGenerator.java | 51 +++ .../linux/LinuxIPMetricsGenerator.java | 93 ++++ ...LinuxMountedPartitionMetricsGenerator.java | 53 +++ .../linux/LinuxOSMetricsGenerator.java | 84 ++++ .../linux/LinuxSchedMetricsGenerator.java | 57 +++ .../linux/LinuxTCPMetricsGenerator.java | 61 +++ .../commons/os/OSGlobals.java | 94 ++++ .../commons/os/SchemaFileParser.java | 163 +++++++ .../commons/os/ThreadCPU.java | 222 +++++++++ .../commons/os/ThreadDiskIO.java | 168 +++++++ .../commons/os/ThreadSched.java | 143 ++++++ .../commons/util/JsonConverter.java | 123 +++++ .../util/JsonPathNotFoundException.java | 10 + .../commons/CommonsTestHelper.java | 24 + .../collectors/AbstractCollectorTest.java | 44 ++ .../collectors/DisksCollectorTest.java | 29 ++ .../collectors/GCInfoCollectorTest.java | 27 ++ .../HeapMetricsCollectorGCTypesTest.java | 58 +++ .../collectors/HeapMetricsCollectorTest.java | 45 ++ .../MountedPartitionMetricsCollectorTest.java | 36 ++ .../collectors/NetworkE2ECollectorTest.java | 45 ++ .../NetworkInterfaceCollectorTest.java | 32 ++ .../commons/jvm/GCMetricsTests.java | 24 + .../commons/jvm/HeapMetricsTests.java | 33 ++ .../commons/jvm/ThreadListTest.java | 39 ++ .../commons/jvm/ThreadListTests.java | 57 +++ .../commons/os/ThreadCPUTests.java | 25 + .../commons/os/ThreadDiskIOTests.java | 24 + .../commons/os/ThreadSchedTests.java | 24 + 63 files changed, 4981 insertions(+) create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/OSMetricsGeneratorFactory.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/CachedStats.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DiskMetrics.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollector.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollector.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollector.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MetricStatus.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetrics.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollector.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetInterfaceSummary.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollector.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollector.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/ScheduledMetricCollectorsExecutor.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/collectors/TCPStatus.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/Disks.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/MountedPartitions.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkE2E.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkInterface.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetrics.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GarbageCollectorInfo.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetrics.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadList.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/CPUPagingActivityGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskIOMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/IPMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/MountedPartitionMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/OSMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/SchedMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/TCPMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxCPUPagingActivityGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskIOMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxIPMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxMountedPartitionMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxOSMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxSchedMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxTCPMetricsGenerator.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/os/OSGlobals.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/os/SchemaFileParser.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPU.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIO.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadSched.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonConverter.java create mode 100644 src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonPathNotFoundException.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/CommonsTestHelper.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/AbstractCollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorGCTypesTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollectorTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetricsTests.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetricsTests.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTest.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTests.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIOTests.java create mode 100644 src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java diff --git a/build.gradle b/build.gradle index 16f0eef..36de934 100644 --- a/build.gradle +++ b/build.gradle @@ -124,6 +124,11 @@ configurations.each { compileJava { dependsOn spotlessApply + JavaVersion targetVersion = JavaVersion.toVersion(targetCompatibility); + if (targetVersion.isJava9Compatible()) { + options.compilerArgs += ["--add-exports", "jdk.attach/sun.tools.attach=ALL-UNNAMED"] + options.compilerArgs += ["--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED"] + } } test { diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/OSMetricsGeneratorFactory.java b/src/main/java/org/opensearch/performanceanalyzer/commons/OSMetricsGeneratorFactory.java new file mode 100644 index 0000000..3424b5b --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/OSMetricsGeneratorFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons; + + +import org.opensearch.performanceanalyzer.commons.config.ConfigStatus; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxOSMetricsGenerator; + +public class OSMetricsGeneratorFactory { + + private static final String OS_TYPE = System.getProperty("os.name"); + + public static OSMetricsGenerator getInstance() { + + if (isLinux()) { + return LinuxOSMetricsGenerator.getInstance(); + } else { + ConfigStatus.INSTANCE.setConfigurationInvalid(); + } + + return null; + } + + private static boolean isLinux() { + return OS_TYPE.toLowerCase().contains("linux"); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/CachedStats.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/CachedStats.java new file mode 100644 index 0000000..c6d20f3 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/CachedStats.java @@ -0,0 +1,42 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import java.util.*; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.ShardStatsValue; + +class CachedStats { + private static final Set CACHABLE_VALUES = + new HashSet<>( + Arrays.asList( + ShardStatsValue.INDEXING_THROTTLE_TIME.toString(), + ShardStatsValue.CACHE_QUERY_HIT.toString(), + ShardStatsValue.CACHE_QUERY_MISS.toString(), + ShardStatsValue.CACHE_FIELDDATA_EVICTION.toString(), + ShardStatsValue.CACHE_REQUEST_HIT.toString(), + ShardStatsValue.CACHE_REQUEST_MISS.toString(), + ShardStatsValue.CACHE_REQUEST_EVICTION.toString(), + ShardStatsValue.REFRESH_EVENT.toString(), + ShardStatsValue.REFRESH_TIME.toString(), + ShardStatsValue.FLUSH_EVENT.toString(), + ShardStatsValue.FLUSH_TIME.toString(), + ShardStatsValue.MERGE_EVENT.toString(), + ShardStatsValue.MERGE_TIME.toString())); + private Map cachedValues = new HashMap<>(); + + long getValue(String statsName) { + return cachedValues.getOrDefault(statsName, 0L); + } + + void putValue(String statsName, long value) { + cachedValues.put(statsName, value); + } + + static Set getCachableValues() { + return CACHABLE_VALUES; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DiskMetrics.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DiskMetrics.java new file mode 100644 index 0000000..4834b72 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DiskMetrics.java @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.DiskDimension; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.DiskValue; + +public class DiskMetrics extends MetricStatus { + public String name; + + public double utilization; // fraction, 0-1 + + public double await; // ms + + public double serviceRate; // MBps + + public DiskMetrics(String name, double utilization, double await, double serviceRate) { + super(); + this.name = name; + this.utilization = utilization; + this.await = await; + this.serviceRate = serviceRate; + } + + public DiskMetrics() { + super(); + } + + @JsonProperty(DiskDimension.Constants.NAME_VALUE) + public String getName() { + return name; + } + + @JsonProperty(DiskValue.Constants.UTIL_VALUE) + public double getUtilization() { + return utilization; + } + + @JsonProperty(DiskValue.Constants.WAIT_VALUE) + public double getAwait() { + return await; + } + + @JsonProperty(DiskValue.Constants.SRATE_VALUE) + public double getServiceRate() { + return serviceRate; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollector.java new file mode 100644 index 0000000..14fad0c --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollector.java @@ -0,0 +1,88 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import java.util.HashMap; +import java.util.Map; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsProcessor; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; +import org.opensearch.performanceanalyzer.commons.metrics_generator.DiskMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.stats.CommonStats; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +public class DisksCollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + + public DisksCollector(String name, int samplingIntervalMillis) { + super( + samplingIntervalMillis, + name, + WriterMetrics.DISKS_COLLECTOR_EXECUTION_TIME, + StatExceptionCode.DISK_METRICS_COLLECTOR_ERROR); + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + // throw exception if keys.length is not equal to 0 + if (keysPath.length != 0) { + throw new RuntimeException("keys length should be 0"); + } + + return PerformanceAnalyzerMetrics.generatePath( + startTime, PerformanceAnalyzerMetrics.sDisksPath); + } + + @Override + public void collectMetrics(long startTime) { + OSMetricsGenerator generator = OSMetricsGeneratorFactory.getInstance(); + if (generator == null) { + return; + } + long mCurrT = System.currentTimeMillis(); + DiskMetricsGenerator diskMetricsGenerator = generator.getDiskMetricsGenerator(); + diskMetricsGenerator.addSample(); + + saveMetricValues(getMetrics(diskMetricsGenerator), startTime); + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.DISKS_COLLECTOR_EXECUTION_TIME, + "", + System.currentTimeMillis() - mCurrT); + } + + private Map getMetricsMap(DiskMetricsGenerator diskMetricsGenerator) { + + Map map = new HashMap<>(); + + for (String disk : diskMetricsGenerator.getAllDisks()) { + DiskMetrics diskMetrics = new DiskMetrics(); + diskMetrics.name = disk; + diskMetrics.await = diskMetricsGenerator.getAwait(disk); + diskMetrics.serviceRate = diskMetricsGenerator.getServiceRate(disk); + diskMetrics.utilization = diskMetricsGenerator.getDiskUtilization(disk); + + map.put(disk, diskMetrics); + } + + return map; + } + + private String getMetrics(DiskMetricsGenerator diskMetricsGenerator) { + + Map map = getMetricsMap(diskMetricsGenerator); + value.setLength(0); + value.append(PerformanceAnalyzerMetrics.getJsonCurrentMilliSeconds()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + for (Map.Entry entry : map.entrySet()) { + value.append(entry.getValue().serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + } + return value.toString(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollector.java new file mode 100644 index 0000000..38c0f90 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollector.java @@ -0,0 +1,91 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.function.Supplier; +import org.opensearch.performanceanalyzer.commons.jvm.GarbageCollectorInfo; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.GCInfoDimension; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsProcessor; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; +import org.opensearch.performanceanalyzer.commons.stats.CommonStats; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +/** + * A collector that collects info about the current garbage collectors for various regions in the + * heap. + */ +public class GCInfoCollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + + private static final int EXPECTED_KEYS_PATH_LENGTH = 0; + + public GCInfoCollector(String name, int samplingIntervalMillis) { + super( + samplingIntervalMillis, + name, + WriterMetrics.GC_INFO_COLLECTOR_EXECUTION_TIME, + StatExceptionCode.GC_INFO_COLLECTOR_ERROR); + } + + @Override + public void collectMetrics(long startTime) { + long mCurrT = System.currentTimeMillis(); + // Zero the string builder + value.setLength(0); + + // first line is the timestamp + value.append(PerformanceAnalyzerMetrics.getJsonCurrentMilliSeconds()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + + for (Map.Entry> entry : + GarbageCollectorInfo.getGcSuppliers().entrySet()) { + value.append(new GCInfo(entry.getKey(), entry.getValue().get()).serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + } + + saveMetricValues(value.toString(), startTime); + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.GC_INFO_COLLECTOR_EXECUTION_TIME, + "", + System.currentTimeMillis() - mCurrT); + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + if (keysPath != null && keysPath.length != EXPECTED_KEYS_PATH_LENGTH) { + throw new RuntimeException("keys length should be " + EXPECTED_KEYS_PATH_LENGTH); + } + + return PerformanceAnalyzerMetrics.generatePath( + startTime, PerformanceAnalyzerMetrics.sGcInfoPath); + } + + public static class GCInfo extends MetricStatus { + private String memoryPool; + private String collectorName; + + public GCInfo() {} + + public GCInfo(final String memoryPool, final String collectorName) { + this.memoryPool = memoryPool; + this.collectorName = collectorName; + } + + @JsonProperty(GCInfoDimension.Constants.MEMORY_POOL_VALUE) + public String getMemoryPool() { + return memoryPool; + } + + @JsonProperty(GCInfoDimension.Constants.COLLECTOR_NAME_VALUE) + public String getCollectorName() { + return collectorName; + } + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollector.java new file mode 100644 index 0000000..37e506f --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollector.java @@ -0,0 +1,177 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.annotations.VisibleForTesting; +import java.lang.management.MemoryUsage; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.jvm.GCMetrics; +import org.opensearch.performanceanalyzer.commons.jvm.HeapMetrics; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.GCType; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.HeapDimension; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.HeapValue; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsProcessor; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; +import org.opensearch.performanceanalyzer.commons.stats.CommonStats; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +public class HeapMetricsCollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + private static final Logger LOG = LogManager.getLogger(HeapMetricsCollector.class); + private static final int KEYS_PATH_LENGTH = 0; + + public HeapMetricsCollector(String name, int samplingIntervalMillis) { + super( + samplingIntervalMillis, + name, + WriterMetrics.HEAP_METRICS_COLLECTOR_EXECUTION_TIME, + StatExceptionCode.HEAP_METRICS_COLLECTOR_ERROR); + } + + @Override + public void collectMetrics(long startTime) { + long mCurrT = System.currentTimeMillis(); + GCMetrics.runGCMetrics(); + + value.setLength(0); + value.append(PerformanceAnalyzerMetrics.getJsonCurrentMilliSeconds()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + value.append( + new HeapStatus( + GCType.TOT_YOUNG_GC.toString(), + GCMetrics.getTotYoungGCCollectionCount(), + GCMetrics.getTotYoungGCCollectionTime()) + .serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + + value.append( + new HeapStatus( + GCType.TOT_FULL_GC.toString(), + GCMetrics.getTotFullGCCollectionCount(), + GCMetrics.getTotFullGCCollectionTime()) + .serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + + for (Map.Entry> entry : + HeapMetrics.getMemoryUsageSuppliers().entrySet()) { + MemoryUsage memoryUsage = entry.getValue().get(); + + value.append( + new HeapStatus( + entry.getKey(), + memoryUsage.getCommitted(), + memoryUsage.getInit(), + memoryUsage.getMax(), + memoryUsage.getUsed()) + .serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + } + + saveMetricValues(value.toString(), startTime); + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.HEAP_METRICS_COLLECTOR_EXECUTION_TIME, + "", + System.currentTimeMillis() - mCurrT); + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + // throw exception if keys.length is not equal to 0 + if (keysPath.length != KEYS_PATH_LENGTH) { + throw new RuntimeException("keys length should be " + KEYS_PATH_LENGTH); + } + + return PerformanceAnalyzerMetrics.generatePath( + startTime, PerformanceAnalyzerMetrics.sHeapPath); + } + + public static class HeapStatus extends MetricStatus { + // GC type like survivor + private String type; + + // -2 means this metric is undefined for a memory pool. For example, + // The memory pool Eden has no collectionCount metric. + @VisibleForTesting static final long UNDEFINED = -2; + + // the total number of collections that have occurred + private long collectionCount = UNDEFINED; + + // the approximate accumulated collection elapsed time in milliseconds + private long collectionTime = UNDEFINED; + + // the amount of memory in bytes that is committed for the Java virtual machine to use + private long committed = UNDEFINED; + + // the amount of memory in bytes that the Java virtual machine initially requests from the + // operating system for memory management + private long init = UNDEFINED; + + // the maximum amount of memory in bytes that can be used for memory management + private long max = UNDEFINED; + + // the amount of used memory in bytes + private long used = UNDEFINED; + + // Allows for automatic JSON deserialization + public HeapStatus() {} + + public HeapStatus(String type, long collectionCount, long collectionTime) { + this.type = type; + this.collectionCount = collectionCount; + this.collectionTime = collectionTime; + } + + public HeapStatus(String type, long committed, long init, long max, long used) { + + this.type = type; + this.committed = committed; + this.init = init; + this.max = max; + this.used = used; + } + + @JsonProperty(HeapDimension.Constants.TYPE_VALUE) + public String getType() { + return type; + } + + @JsonProperty(HeapValue.Constants.COLLECTION_COUNT_VALUE) + public long getCollectionCount() { + return collectionCount; + } + + @JsonProperty(HeapValue.Constants.COLLECTION_TIME_VALUE) + public long getCollectionTime() { + return collectionTime; + } + + @JsonProperty(HeapValue.Constants.COMMITTED_VALUE) + public long getCommitted() { + return committed; + } + + @JsonProperty(HeapValue.Constants.INIT_VALUE) + public long getInit() { + return init; + } + + @JsonProperty(HeapValue.Constants.MAX_VALUE) + public long getMax() { + return max; + } + + @JsonProperty(HeapValue.Constants.USED_VALUE) + public long getUsed() { + return used; + } + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MetricStatus.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MetricStatus.java new file mode 100644 index 0000000..9662ca4 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MetricStatus.java @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.opensearch.performanceanalyzer.commons.util.JsonConverter; + +public class MetricStatus { + + /** + * converts any object to a JSON string and return that string + * + * @return A string containing a JSON representation of the object + */ + public String serialize() { + return JsonConverter.writeValueAsString(this); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetrics.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetrics.java new file mode 100644 index 0000000..f1fccac --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetrics.java @@ -0,0 +1,59 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.DevicePartitionDimension; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.DevicePartitionValue; + +public class MountedPartitionMetrics extends MetricStatus { + private String mountPoint; + private String devicePartition; + private long totalSpace; + private long freeSpace; + private long usableFreeSpace; + + public MountedPartitionMetrics() {} + + public MountedPartitionMetrics( + String devicePartition, + String mountPoint, + long totalSpace, + long freeSpace, + long usableFreeSpace) { + this.devicePartition = devicePartition; + this.mountPoint = mountPoint; + this.totalSpace = totalSpace; + this.freeSpace = freeSpace; + this.usableFreeSpace = usableFreeSpace; + } + + @JsonProperty(DevicePartitionDimension.Constants.MOUNT_POINT_VALUE) + public String getMountPoint() { + return mountPoint; + } + + @JsonProperty(DevicePartitionDimension.Constants.DEVICE_PARTITION_VALUE) + public String getDevicePartition() { + return devicePartition; + } + + @JsonProperty(DevicePartitionValue.Constants.TOTAL_SPACE_VALUE) + public long getTotalSpace() { + return totalSpace; + } + + @JsonProperty(DevicePartitionValue.Constants.FREE_SPACE_VALUE) + public long getFreeSpace() { + return freeSpace; + } + + @JsonProperty(DevicePartitionValue.Constants.USABLE_FREE_SPACE_VALUE) + public long getUsableFreeSpace() { + return usableFreeSpace; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollector.java new file mode 100644 index 0000000..d41c67b --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollector.java @@ -0,0 +1,82 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import java.util.Set; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsProcessor; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; +import org.opensearch.performanceanalyzer.commons.metrics_generator.MountedPartitionMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +public class MountedPartitionMetricsCollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + + private static final int EXPECTED_KEYS_PATH_LENGTH = 0; + + public MountedPartitionMetricsCollector(String name, int samplingIntervalMillis) { + super( + samplingIntervalMillis, + name, + WriterMetrics.MOUNTED_PARTITION_METRICS_COLLECTOR_EXECUTION_TIME, + StatExceptionCode.MOUNTED_PARTITION_METRICS_COLLECTOR_ERROR); + } + + @Override + public void collectMetrics(long startTime) { + OSMetricsGenerator generator = OSMetricsGeneratorFactory.getInstance(); + if (generator == null) { + return; + } + MountedPartitionMetricsGenerator mountedPartitionMetricsGenerator = + generator.getMountedPartitionMetricsGenerator(); + + mountedPartitionMetricsGenerator.addSample(); + + saveMetricValues(getMetrics(mountedPartitionMetricsGenerator), startTime); + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + if (keysPath != null && keysPath.length != EXPECTED_KEYS_PATH_LENGTH) { + throw new RuntimeException("keys length should be " + EXPECTED_KEYS_PATH_LENGTH); + } + + return PerformanceAnalyzerMetrics.generatePath( + startTime, PerformanceAnalyzerMetrics.sMountedPartitionMetricsPath); + } + + private String getMetrics( + final MountedPartitionMetricsGenerator mountedPartitionMetricsGenerator) { + // zero the string builder + value.setLength(0); + + // first line is the timestamp + value.append(PerformanceAnalyzerMetrics.getJsonCurrentMilliSeconds()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + + Set mountPoints = mountedPartitionMetricsGenerator.getAllMountPoints(); + for (String mountPoint : mountPoints) { + value.append( + new MountedPartitionMetrics( + mountedPartitionMetricsGenerator.getDevicePartition( + mountPoint), + mountPoint, + mountedPartitionMetricsGenerator.getTotalSpace( + mountPoint), + mountedPartitionMetricsGenerator.getFreeSpace( + mountPoint), + mountedPartitionMetricsGenerator.getUsableFreeSpace( + mountPoint)) + .serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + } + return value.toString(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetInterfaceSummary.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetInterfaceSummary.java new file mode 100644 index 0000000..adcfafb --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetInterfaceSummary.java @@ -0,0 +1,74 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.IPDimension; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.IPValue; + +// all metrics are per-time-unit +public class NetInterfaceSummary extends MetricStatus { + + public enum Direction { + in, + out; + } + + private Direction direction; + private double packetRate4; + private double dropRate4; + private double packetRate6; + private double dropRate6; + private double bps; + + public NetInterfaceSummary() {} + + public NetInterfaceSummary( + Direction direction, + double packetRate4, + double dropRate4, + double packetRate6, + double dropRate6, + double bps) { + this.direction = direction; + this.packetRate4 = packetRate4; + this.dropRate4 = dropRate4; + this.packetRate6 = packetRate6; + this.dropRate6 = dropRate6; + this.bps = bps; + } + + @JsonProperty(IPDimension.Constants.DIRECTION_VALUE) + public Direction getDirection() { + return direction; + } + + @JsonProperty(IPValue.Constants.PACKET_RATE4_VALUE) + public double getPacketRate4() { + return packetRate4; + } + + @JsonProperty(IPValue.Constants.DROP_RATE4_VALUE) + public double getDropRate4() { + return dropRate4; + } + + @JsonProperty(IPValue.Constants.PACKET_RATE6_VALUE) + public double getPacketRate6() { + return packetRate6; + } + + @JsonProperty(IPValue.Constants.DROP_RATE6_VALUE) + public double getDropRate6() { + return dropRate6; + } + + @JsonProperty(IPValue.Constants.THROUGHPUT_VALUE) + public double getBps() { + return bps; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollector.java new file mode 100644 index 0000000..198704a --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollector.java @@ -0,0 +1,88 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import java.util.HashMap; +import java.util.Map; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsProcessor; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.TCPMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +public class NetworkE2ECollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + + public NetworkE2ECollector(String name, int samplingIntervalMillis) { + super( + samplingIntervalMillis, + name, + WriterMetrics.NETWORK_E2E_COLLECTOR_EXECUTION_TIME, + StatExceptionCode.NETWORK_COLLECTION_ERROR); + } + + @Override + public void collectMetrics(long startTime) { + OSMetricsGenerator generator = OSMetricsGeneratorFactory.getInstance(); + if (generator == null) { + return; + } + TCPMetricsGenerator tcpMetricsGenerator = generator.getTCPMetricsGenerator(); + tcpMetricsGenerator.addSample(); + saveMetricValues(getMetrics(tcpMetricsGenerator), startTime); + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + // throw exception if keys.length is not equal to 0 + if (keysPath.length != 0) { + throw new RuntimeException("keys length should be 0"); + } + + return PerformanceAnalyzerMetrics.generatePath( + startTime, PerformanceAnalyzerMetrics.sTCPPath); + } + + private Map getMetricsMap(TCPMetricsGenerator tcpMetricsGenerator) { + Map map = new HashMap<>(); + + for (String dest : tcpMetricsGenerator.getAllDestionationIps()) { + TCPStatus tcpStatus = + new TCPStatus( + dest, + tcpMetricsGenerator.getNumberOfFlows(dest), + tcpMetricsGenerator.getTransmitQueueSize(dest), + tcpMetricsGenerator.getReceiveQueueSize(dest), + tcpMetricsGenerator.getCurrentLost(dest), + tcpMetricsGenerator.getSendCongestionWindow(dest), + tcpMetricsGenerator.getSlowStartThreshold(dest)); + + map.put(dest, tcpStatus); + } + + return map; + } + + private String getMetrics(TCPMetricsGenerator tcpMetricsGenerator) { + + Map map = getMetricsMap(tcpMetricsGenerator); + value.setLength(0); + + // first line is the timestamp + value.append(PerformanceAnalyzerMetrics.getJsonCurrentMilliSeconds()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + + for (TCPStatus tcpStatus : map.values()) { + value.append(tcpStatus.serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + } + + return value.toString(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollector.java new file mode 100644 index 0000000..ffd1167 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollector.java @@ -0,0 +1,104 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsProcessor; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; +import org.opensearch.performanceanalyzer.commons.metrics_generator.IPMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.stats.CommonStats; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +public class NetworkInterfaceCollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + private static final int sTimeInterval = + MetricsConfiguration.CONFIG_MAP.get(NetworkInterfaceCollector.class).samplingInterval; + private static final Logger LOG = LogManager.getLogger(NetworkInterfaceCollector.class); + + public NetworkInterfaceCollector(String name, int samplingIntervalMillis) { + super( + samplingIntervalMillis, + name, + WriterMetrics.NETWORK_INTERFACE_COLLECTOR_EXECUTION_TIME, + StatExceptionCode.RCA_NETWORK_ERROR); + } + + @Override + public void collectMetrics(long startTime) { + OSMetricsGenerator generator = OSMetricsGeneratorFactory.getInstance(); + if (generator == null) { + return; + } + + long mCurrT = System.currentTimeMillis(); + IPMetricsGenerator IPMetricsGenerator = generator.getIPMetricsGenerator(); + IPMetricsGenerator.addSample(); + saveMetricValues( + getMetrics(IPMetricsGenerator) + PerformanceAnalyzerMetrics.sMetricNewLineDelimitor, + startTime); + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.NETWORK_INTERFACE_COLLECTOR_EXECUTION_TIME, + "", + System.currentTimeMillis() - mCurrT); + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + // throw exception if keys.length is not equal to 0 + if (keysPath.length != 0) { + throw new RuntimeException("keys length should be 0"); + } + + return PerformanceAnalyzerMetrics.generatePath( + startTime, PerformanceAnalyzerMetrics.sIPPath); + } + + private String getMetrics(IPMetricsGenerator IPMetricsGenerator) { + + value.setLength(0); + value.append(PerformanceAnalyzerMetrics.getJsonCurrentMilliSeconds()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + + try { + NetInterfaceSummary inNetwork = + new NetInterfaceSummary( + NetInterfaceSummary.Direction.in, + IPMetricsGenerator.getInPacketRate4(), + IPMetricsGenerator.getInDropRate4(), + IPMetricsGenerator.getInPacketRate6(), + IPMetricsGenerator.getInDropRate6(), + IPMetricsGenerator.getInBps()); + + NetInterfaceSummary outNetwork = + new NetInterfaceSummary( + NetInterfaceSummary.Direction.out, + IPMetricsGenerator.getOutPacketRate4(), + IPMetricsGenerator.getOutDropRate4(), + IPMetricsGenerator.getOutPacketRate6(), + IPMetricsGenerator.getOutDropRate6(), + IPMetricsGenerator.getOutBps()); + + value.append(inNetwork.serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + value.append(outNetwork.serialize()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + } catch (Exception e) { + LOG.debug( + "Exception in NetworkInterfaceCollector: {} with ExceptionCode: {}", + () -> e.toString(), + () -> StatExceptionCode.NETWORK_COLLECTION_ERROR.toString()); + StatsCollector.instance().logException(StatExceptionCode.NETWORK_COLLECTION_ERROR); + } + + return value.toString(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/ScheduledMetricCollectorsExecutor.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/ScheduledMetricCollectorsExecutor.java new file mode 100644 index 0000000..fecf255 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/ScheduledMetricCollectorsExecutor.java @@ -0,0 +1,165 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.stats.CommonStats; +import org.opensearch.performanceanalyzer.commons.stats.metrics.WriterMetrics; + +public class ScheduledMetricCollectorsExecutor extends Thread { + private static final Logger LOG = LogManager.getLogger(ScheduledMetricCollectorsExecutor.class); + private final int collectorThreadCount; + private static final int DEFAULT_COLLECTOR_THREAD_COUNT = 5; + private static final int COLLECTOR_THREAD_KEEPALIVE_SECS = 1000; + private final boolean checkFeatureDisabledFlag; + private boolean paEnabled = false; + private boolean threadContentionMonitoringEnabled = false; + private int minTimeIntervalToSleep = Integer.MAX_VALUE; + private Map metricsCollectors; + + public static final String COLLECTOR_THREAD_POOL_NAME = "pa-collectors-th"; + + private ThreadPoolExecutor metricsCollectorsTP; + + public ScheduledMetricCollectorsExecutor( + int collectorThreadCount, boolean checkFeatureDisabledFlag) { + metricsCollectors = new HashMap<>(); + metricsCollectorsTP = null; + this.collectorThreadCount = collectorThreadCount; + this.checkFeatureDisabledFlag = checkFeatureDisabledFlag; + } + + public ScheduledMetricCollectorsExecutor() { + this(DEFAULT_COLLECTOR_THREAD_COUNT, true); + } + + public synchronized void setEnabled(final boolean enabled) { + paEnabled = enabled; + } + + public synchronized boolean getEnabled() { + return paEnabled; + } + + public synchronized void setThreadContentionMonitoringEnabled(final boolean enabled) { + metricsCollectors + .keySet() + .forEach(collector -> collector.setThreadContentionMonitoringEnabled(enabled)); + threadContentionMonitoringEnabled = enabled; + } + + private synchronized boolean getThreadContentionMonitoringEnabled() { + return threadContentionMonitoringEnabled; + } + + public void addScheduledMetricCollector(PerformanceAnalyzerMetricsCollector task) { + task.setThreadContentionMonitoringEnabled(getThreadContentionMonitoringEnabled()); + metricsCollectors.put(task, System.currentTimeMillis() + task.getTimeInterval()); + if (task.getTimeInterval() < minTimeIntervalToSleep) { + minTimeIntervalToSleep = task.getTimeInterval(); + } + } + + public void run() { + Thread.currentThread().setName(this.getClass().getSimpleName()); + if (metricsCollectorsTP == null) { + ThreadFactory taskThreadFactory = + new ThreadFactoryBuilder() + .setNameFormat(COLLECTOR_THREAD_POOL_NAME) + .setDaemon(true) + .build(); + metricsCollectorsTP = + new ThreadPoolExecutor( + collectorThreadCount, + collectorThreadCount, + COLLECTOR_THREAD_KEEPALIVE_SECS, + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(metricsCollectors.size()), + taskThreadFactory); + } + + long prevStartTimestamp = System.currentTimeMillis(); + + while (true) { + try { + long millisToSleep = + minTimeIntervalToSleep - System.currentTimeMillis() + prevStartTimestamp; + if (millisToSleep > 0) { + Thread.sleep(millisToSleep); + } + } catch (Exception ex) { + LOG.error("Exception in Thread Sleep", ex); + } + + prevStartTimestamp = System.currentTimeMillis(); + + if (getEnabled()) { + long currentTime = System.currentTimeMillis(); + + for (Map.Entry entry : + metricsCollectors.entrySet()) { + if (entry.getValue() <= currentTime) { + PerformanceAnalyzerMetricsCollector collector = entry.getKey(); + if (collector.getState() + == PerformanceAnalyzerMetricsCollector.State.MUTED) { + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.COLLECTORS_MUTED, + collector.getCollectorName(), + 1); + continue; + } + metricsCollectors.put( + collector, entry.getValue() + collector.getTimeInterval()); + if (!collector.inProgress()) { + collector.setStartTime(currentTime); + metricsCollectorsTP.execute(collector); + } else { + /** + * Always run StatsCollector; we rely on StatsCollector for framework + * service metrics + */ + if (collector + .getCollectorName() + .equals(StatsCollector.COLLECTOR_NAME)) { + LOG.info( + " {} is still in progress; StatsCollector is critical for framework service metrics", + StatsCollector.COLLECTOR_NAME); + return; + } + if (collector.getState() + == PerformanceAnalyzerMetricsCollector.State.HEALTHY) { + collector.setState(PerformanceAnalyzerMetricsCollector.State.SLOW); + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.COLLECTORS_SLOW, + collector.getCollectorName(), + 1); + } else if (collector.getState() + == PerformanceAnalyzerMetricsCollector.State.SLOW) { + collector.setState(PerformanceAnalyzerMetricsCollector.State.MUTED); + } + LOG.info( + "Collector {} is still in progress, so skipping this Interval", + collector.getCollectorName()); + CommonStats.WRITER_METRICS_AGGREGATOR.updateStat( + WriterMetrics.COLLECTORS_SKIPPED, + collector.getCollectorName(), + 1); + } + } + } + } + } + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/TCPStatus.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/TCPStatus.java new file mode 100644 index 0000000..a221d54 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/TCPStatus.java @@ -0,0 +1,84 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.TCPDimension; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.TCPValue; + +public class TCPStatus extends MetricStatus { + + private String dest; + + private int numFlows; + + private double txQ; + + private double rxQ; + + private double curLost; + + private double sndCWND; + + // make this field private so that Jackson uses getter method name + private double ssThresh; + + public TCPStatus() {} + + public TCPStatus( + String dest, + int numFlows, + double txQ, + double rxQ, + double curLost, + double sndCWND, + double sSThresh) { + super(); + this.dest = dest; + this.numFlows = numFlows; + this.txQ = txQ; + this.rxQ = rxQ; + this.curLost = curLost; + this.sndCWND = sndCWND; + this.ssThresh = sSThresh; + } + + @JsonProperty(TCPDimension.Constants.DEST_VALUE) + public String getDest() { + return dest; + } + + @JsonProperty(TCPValue.Constants.NUM_FLOWS_VALUE) + public int getNumFlows() { + return numFlows; + } + + @JsonProperty(TCPValue.Constants.TXQ_VALUE) + public double getTxQ() { + return txQ; + } + + @JsonProperty(TCPValue.Constants.RXQ_VALUE) + public double getRxQ() { + return rxQ; + } + + @JsonProperty(TCPValue.Constants.CUR_LOST_VALUE) + public double getCurLost() { + return curLost; + } + + @JsonProperty(TCPValue.Constants.SEND_CWND_VALUE) + public double getSndCWND() { + return sndCWND; + } + + @JsonProperty(TCPValue.Constants.SSTHRESH_VALUE) + public double getSsThresh() { + return ssThresh; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/Disks.java b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/Disks.java new file mode 100644 index 0000000..73fc46f --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/Disks.java @@ -0,0 +1,162 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.hwnet; + + +import java.io.File; +import java.util.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.collectors.DiskMetrics; +import org.opensearch.performanceanalyzer.commons.metrics_generator.DiskMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxDiskMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.os.SchemaFileParser; +import org.opensearch.performanceanalyzer.commons.util.Util; + +public class Disks { + private static Map> diskKVMap = new HashMap<>(); + private static Map> olddiskKVMap = new HashMap<>(); + private static long kvTimestamp = 0; + private static long oldkvTimestamp = 0; + private static Set diskList = new HashSet<>(); + private static final Logger LOG = LogManager.getLogger(Disks.class); + private static LinuxDiskMetricsGenerator linuxDiskMetricsHandler = + new LinuxDiskMetricsGenerator(); + + private static String statKeys[] = { + "majno", // 1 + "minno", + "name", + "rdone", + "rmerged", + "rsectors", + "rtime", + "wdone", + "wmerged", + "wsectors", // 10 + "wtime", + "inprogressIO", + "IOtime", + "weightedIOtime" + }; + + private static SchemaFileParser.FieldTypes statTypes[] = { + SchemaFileParser.FieldTypes.INT, // 1 + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.STRING, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, // 10 + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG + }; + + static { + Util.invokePrivileged(() -> listDisks()); + oldkvTimestamp = System.currentTimeMillis(); + kvTimestamp = oldkvTimestamp; + } + + private static StringBuilder value = new StringBuilder(); + + private static void listDisks() { + try { + File file = new File("/sys/block"); + File[] files = file.listFiles(); + if (files != null) { + for (File dfile : files) { + if (dfile != null && !dfile.getCanonicalPath().contains("/virtual/")) { + diskList.add(dfile.getName()); + } + } + } + } catch (Exception e) { + LOG.debug("Exception in calling listDisks with details: {}", () -> e.toString()); + } + } + + public static DiskMetricsGenerator getDiskMetricsHandler() { + return linuxDiskMetricsHandler; + } + + public static void addSample() { + olddiskKVMap.clear(); + olddiskKVMap.putAll(diskKVMap); + diskKVMap.clear(); + + SchemaFileParser parser = new SchemaFileParser("/proc/diskstats", statKeys, statTypes); + List> sampleList = parser.parseMultiple(); + + for (Map sample : sampleList) { + String diskname = (String) (sample.get("name")); + if (!diskList.contains(diskname)) { + diskKVMap.put(diskname, sample); + } + } + + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + + calculateDiskMetrics(); + } + + private static void calculateDiskMetrics() { + + linuxDiskMetricsHandler.setDiskMetricsMap(getMetricsMap()); + } + + public static Map getMetricsMap() { + Map map = new HashMap<>(); + if (kvTimestamp > oldkvTimestamp) { + for (Map.Entry> entry : diskKVMap.entrySet()) { + String disk = entry.getKey(); + Map m = entry.getValue(); + Map mold = olddiskKVMap.get(disk); + if (mold != null) { + DiskMetrics dm = new DiskMetrics(); + dm.name = (String) m.get("name"); + double rwdeltatime = + 1.0 + * ((long) m.get("rtime") + + (long) m.get("wtime") + - (long) mold.get("rtime") + - (long) mold.get("wtime")); + double rwdeltaiops = + 1.0 + * ((long) m.get("rdone") + + (long) m.get("wdone") + - (long) mold.get("rdone") + - (long) mold.get("wdone")); + double rwdeltasectors = + 1.0 + * ((long) m.get("rsectors") + + (long) m.get("wsectors") + - (long) mold.get("rsectors") + - (long) mold.get("wsectors")); + + dm.utilization = rwdeltatime / (kvTimestamp - oldkvTimestamp); + dm.await = (rwdeltaiops > 0) ? rwdeltatime / rwdeltaiops : 0; + dm.serviceRate = + (rwdeltatime > 0) ? rwdeltasectors * 512 * 1.0e-3 / rwdeltatime : 0; + + map.put(disk, dm); + } + } + } + return map; + } + + public static void runOnce() { + addSample(); + System.out.println("disks: " + getMetricsMap()); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/MountedPartitions.java b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/MountedPartitions.java new file mode 100644 index 0000000..a51b8df --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/MountedPartitions.java @@ -0,0 +1,100 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.hwnet; + + +import com.google.common.collect.ImmutableSet; +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.opensearch.performanceanalyzer.commons.collectors.MountedPartitionMetrics; +import org.opensearch.performanceanalyzer.commons.metrics_generator.MountedPartitionMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxMountedPartitionMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.os.SchemaFileParser; +import org.opensearch.performanceanalyzer.commons.os.SchemaFileParser.FieldTypes; + +public class MountedPartitions { + + private static final String CGROUP = "cgroup"; + private static final String PROC = "proc"; + private static final String SYSFS = "sysfs"; + private static final String DEV_PTS = "devpts"; + private static final String DEV_TMPFS = "devtmpfs"; + private static final String PATH_SYS = "/sys/"; + private static final String PATH_ETC = "/etc/"; + private static final String KEY_DEVICE_PARTITION = "devicePartition"; + private static final String KEY_MOUNT_POINT = "mountPoint"; + private static final String KEY_FILE_SYSTEM_TYPE = "fileSystemType"; + private static final String KEY_MOUNT_OPTIONS = "mountOptions"; + private static final String KEY_DUMP = "dump"; + private static final String KEY_PASS = "pass"; + private static final String NONE = "none"; + private static final LinuxMountedPartitionMetricsGenerator + linuxMountedPartitionMetricsGenerator; + private static final Map mountPointFileMap; + private static final Set virtualSysPartitionSet; + private static final Set ignoredMountPoints; + private static final String[] schemaKeys = { + KEY_DEVICE_PARTITION, + KEY_MOUNT_POINT, + KEY_FILE_SYSTEM_TYPE, + KEY_MOUNT_OPTIONS, + KEY_DUMP, + KEY_PASS + }; + private static final FieldTypes[] schemaKeyTypes = { + FieldTypes.STRING, // devicePartition + FieldTypes.STRING, // mountPoint + FieldTypes.STRING, // fileSystemType + FieldTypes.STRING, // mountOptions + FieldTypes.INT, // dump + FieldTypes.INT // pass + }; + + static { + linuxMountedPartitionMetricsGenerator = new LinuxMountedPartitionMetricsGenerator(); + mountPointFileMap = new HashMap<>(); + virtualSysPartitionSet = ImmutableSet.of(CGROUP, PROC, SYSFS, DEV_PTS, DEV_TMPFS, NONE); + ignoredMountPoints = ImmutableSet.of(PATH_SYS, PATH_ETC); + } + + public static void addSample() { + SchemaFileParser parser = new SchemaFileParser("/proc/mounts", schemaKeys, schemaKeyTypes); + List> parsedMaps = + parser.parseMultiple().stream() + .filter( + map -> { + String devicePartition = (String) map.get(KEY_DEVICE_PARTITION); + String mountPoint = (String) map.get(KEY_MOUNT_POINT); + + return !(virtualSysPartitionSet.contains(devicePartition) + || !devicePartition.startsWith("/dev/") + || ignoredMountPoints.contains(mountPoint)); + }) + .collect(Collectors.toList()); + for (Map mountInfo : parsedMaps) { + String devicePartition = (String) mountInfo.get(KEY_DEVICE_PARTITION); + String mountPoint = (String) mountInfo.get(KEY_MOUNT_POINT); + + long totalSpace = + mountPointFileMap.computeIfAbsent(mountPoint, File::new).getTotalSpace(); + long freeSpace = mountPointFileMap.get(mountPoint).getFreeSpace(); + long usableFreeSpace = mountPointFileMap.get(mountPoint).getUsableSpace(); + MountedPartitionMetrics metrics = + new MountedPartitionMetrics( + devicePartition, mountPoint, totalSpace, freeSpace, usableFreeSpace); + + linuxMountedPartitionMetricsGenerator.addSupplier(mountPoint, metrics); + } + } + + public static MountedPartitionMetricsGenerator getLinuxMountedPartitionMetricsGenerator() { + return linuxMountedPartitionMetricsGenerator; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkE2E.java b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkE2E.java new file mode 100644 index 0000000..b16f194 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkE2E.java @@ -0,0 +1,212 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.hwnet; + + +import com.google.common.annotations.VisibleForTesting; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.collectors.StatsCollector; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxTCPMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.os.OSGlobals; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; + +public class NetworkE2E { + /* Data sources: + /proc/net/tcp, /proc/net/tcp6 and /proc/pid/fd/* + intersection of these gives a list of flows + owned by the process. net/tcp gives metrics + (by src-dest pair) around queues, retx's + and TCP sndwnd. + */ + + private static final Logger LOG = LogManager.getLogger(NetworkE2E.class); + private static String pid = OSGlobals.getPid(); + + static class TCPFlowMetrics { + String destIP; + + long txQueue; + long rxQueue; + long currentLost; + long sendCWND; + long SSThresh; + } + + static class destTCPFlowMetrics { + long txQueueTot; + long rxQueueTot; + long currentLostTot; + long sendCWNDTot; + long SSThreshTot; + int numFlows; + + destTCPFlowMetrics(TCPFlowMetrics m) { + txQueueTot = m.txQueue; + rxQueueTot = m.rxQueue; + currentLostTot = m.currentLost; + sendCWNDTot = m.sendCWND; + SSThreshTot = m.SSThresh; + numFlows = 1; + } + } + + private static Set inodeSocketList = new HashSet<>(); + private static Map inodeFlowMetricsMap = new HashMap<>(); + private static Map destnodeFlowMetricsMap = new HashMap<>(); + private static LinuxTCPMetricsGenerator linuxTCPMetricsHandler = new LinuxTCPMetricsGenerator(); + + private static StringBuilder value = new StringBuilder(); + + static void listSockets() { + File self = new File("/proc/" + pid + "/fd"); + File[] filesList = self.listFiles(); + if (filesList == null) { + return; + } + for (File f : filesList) { + // no check for file, as this dir is all files/symlinks + String target = null; + try { + Path targetp = Files.readSymbolicLink(Paths.get(f.getCanonicalPath())); + target = targetp.toString(); + } catch (Exception e) { + continue; + } + if (target.contains("socket:")) { + target = target.split("socket:\\[")[1]; + target = target.split("\\]")[0]; + inodeSocketList.add(target); + } + } + } + + private static void generateMap(String line, String ver) { + String[] toks = line.trim().split("\\s+"); + if (!inodeSocketList.contains(toks[9])) { // inode + return; + } + TCPFlowMetrics m = new TCPFlowMetrics(); + m.destIP = toks[2].split(":")[0]; + m.txQueue = Long.decode("0x" + toks[4].split(":")[0]); + m.rxQueue = Long.decode("0x" + toks[4].split(":")[1]); + m.currentLost = Long.decode("0x" + toks[6]); + if (toks.length > 16) { + m.sendCWND = Long.parseLong(toks[15]); + m.SSThresh = Long.parseLong(toks[16]); + } else { + m.sendCWND = -1; + m.SSThresh = -1; + } + inodeFlowMetricsMap.put(toks[9], m); + } + + private static void mapTCPMetrics(String ver) { + int ln = 0; + try (FileReader fileReader = new FileReader(new File(ver)); + BufferedReader bufferedReader = new BufferedReader(fileReader)) { + String line = null; + while ((line = bufferedReader.readLine()) != null) { + if (ln != 0) { // first line is keys + generateMap(line, ver); + } + ln++; + } + } catch (Exception e) { + LOG.debug( + "Error in mapTCPMetrics: {} with ExceptionCode: {}", + () -> e, + () -> StatExceptionCode.NETWORK_COLLECTION_ERROR.toString()); + StatsCollector.instance().logException(StatExceptionCode.NETWORK_COLLECTION_ERROR); + } + } + + private static void mapTCPMetrics() { + mapTCPMetrics("/proc/net/tcp"); + mapTCPMetrics("/proc/net/tcp6"); + } + + private static void clearAll() { + inodeSocketList.clear(); + inodeFlowMetricsMap.clear(); + destnodeFlowMetricsMap.clear(); + } + + private static void computeSummary() { + for (Map.Entry entry : inodeFlowMetricsMap.entrySet()) { + TCPFlowMetrics m = entry.getValue(); + destTCPFlowMetrics exist = destnodeFlowMetricsMap.get(m.destIP); + if (exist == null) { + destnodeFlowMetricsMap.put(m.destIP, new destTCPFlowMetrics(m)); + } else { + // check for "-1"s and add to total only if it is not -1 + exist.numFlows++; + exist.txQueueTot += (m.txQueue != -1 ? m.txQueue : 0); + exist.rxQueueTot += (m.rxQueue != -1 ? m.rxQueue : 0); + exist.currentLostTot += (m.currentLost != -1 ? m.currentLost : 0); + exist.sendCWNDTot += (m.sendCWND != -1 ? m.sendCWND : 0); + exist.SSThreshTot += (m.SSThresh != -1 ? m.SSThresh : 0); + } + } + + calculateTCPMetrics(); + } + + protected static void calculateTCPMetrics() { + + Map localMap = new HashMap<>(); + for (Map.Entry entry : destnodeFlowMetricsMap.entrySet()) { + destTCPFlowMetrics m = entry.getValue(); + + double[] metrics = new double[6]; + metrics[0] = m.numFlows; + metrics[1] = m.txQueueTot * 1.0 / m.numFlows; + metrics[2] = m.rxQueueTot * 1.0 / m.numFlows; + metrics[3] = m.currentLostTot * 1.0 / m.numFlows; + metrics[4] = m.sendCWNDTot * 1.0 / m.numFlows; + metrics[5] = m.SSThreshTot * 1.0 / m.numFlows; + + localMap.put(entry.getKey(), metrics); + } + + linuxTCPMetricsHandler.setTCPMetrics(localMap); + } + + public static LinuxTCPMetricsGenerator getTCPMetricsHandler() { + + return linuxTCPMetricsHandler; + } + + public static void addSample() { + clearAll(); + listSockets(); + mapTCPMetrics(); + computeSummary(); + } + + public static void runOnce() { + clearAll(); + listSockets(); + mapTCPMetrics(); + computeSummary(); + } + + @VisibleForTesting + protected static void setDestnodeFlowMetricsMap( + Map destnodeFlowMetricsMap) { + NetworkE2E.destnodeFlowMetricsMap = destnodeFlowMetricsMap; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkInterface.java b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkInterface.java new file mode 100644 index 0000000..d23d626 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/hwnet/NetworkInterface.java @@ -0,0 +1,382 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.hwnet; + + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.collectors.NetInterfaceSummary; +import org.opensearch.performanceanalyzer.commons.collectors.StatsCollector; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxIPMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; + +public class NetworkInterface { + private static final Logger LOG = LogManager.getLogger(NetworkInterface.class); + + /* Data sources: + /proc/net/snmp, /prov/net/snmp6, /proc/net/dev + measures tcp and ip-layer pathologies. + SNMP fields of interest (see RFCs 2011 and 1213): + - [ip6]inReceives: total including errors + - [ip6]inDelivers: sent to next layer (including ICMP) + - [ip6]outRequests: sent from previous layer + - [ip6]outDiscards + [ip6]outNoRoutes: sender-side drops + */ + + static class NetInterfaceMetrics { + Map PHYmetrics = new HashMap<>(); + Map IPmetrics = new HashMap<>(); + // these three are currently unused; + // leaving them commented for now. + /*Map TCPmetrics = + new HashMap<>(); + Map UDPmetrics = + new HashMap<>(); + Map ICMPmetrics = + new HashMap<>();*/ + + public void clearAll() { + PHYmetrics.clear(); + IPmetrics.clear(); + /*TCPmetrics.clear(); + UDPmetrics.clear(); + ICMPmetrics.clear();*/ + } + + public void putAll(NetInterfaceMetrics m) { + PHYmetrics.putAll(m.PHYmetrics); + IPmetrics.putAll(m.IPmetrics); + /*TCPmetrics.putAll(m.TCPmetrics); + UDPmetrics.putAll(m.UDPmetrics); + ICMPmetrics.putAll(m.ICMPmetrics);*/ + } + } + + private static NetInterfaceMetrics currentMetrics = new NetInterfaceMetrics(); + private static NetInterfaceMetrics oldMetrics = new NetInterfaceMetrics(); + private static Map currentMetrics6 = new HashMap<>(); + private static Map oldMetrics6 = new HashMap<>(); + private static long kvTimestamp = 0; + private static long oldkvTimestamp = 0; + + private static StringBuilder ret = new StringBuilder(); + + private static String[] IPkeys = null; + // static private String[] TCPkeys = null; + // static private String[] UDPkeys = null; + // static private String[] ICMPkeys = null; + + private static LinuxIPMetricsGenerator linuxIPMetricsGenerator = new LinuxIPMetricsGenerator(); + + private static final Splitter STRING_PATTERN_SPLITTER = Splitter.on(Pattern.compile("[ \\t]+")); + + static { + addSampleHelper(); + } + + public static LinuxIPMetricsGenerator getLinuxIPMetricsGenerator() { + return linuxIPMetricsGenerator; + } + + protected static void calculateNetworkMetrics() { + + if (kvTimestamp <= oldkvTimestamp) { + linuxIPMetricsGenerator.setInNetworkInterfaceSummary(null); + linuxIPMetricsGenerator.setOutNetworkInterfaceSummary(null); + return; + } + + Map curphy = currentMetrics.PHYmetrics; + Map curipv4 = currentMetrics.IPmetrics; + Map oldphy = oldMetrics.PHYmetrics; + Map oldipv4 = oldMetrics.IPmetrics; + + long nin = curipv4.get("InReceives") - oldipv4.get("InReceives"); + long nout = curipv4.get("OutRequests") - oldipv4.get("OutRequests"); + long delivin = curipv4.get("InDelivers") - oldipv4.get("InDelivers"); + long dropout = + curipv4.get("OutDiscards") + + curipv4.get("OutNoRoutes") + - oldipv4.get("OutDiscards") + - oldipv4.get("OutNoRoutes"); + long nin6 = currentMetrics6.get("Ip6InReceives") - oldMetrics6.get("Ip6InReceives"); + long nout6 = currentMetrics6.get("Ip6OutRequests") - oldMetrics6.get("Ip6OutRequests"); + long delivin6 = currentMetrics6.get("Ip6InDelivers") - oldMetrics6.get("Ip6InDelivers"); + long dropout6 = + currentMetrics6.get("Ip6OutDiscards") + + currentMetrics6.get("Ip6OutNoRoutes") + - oldMetrics6.get("Ip6OutDiscards") + - oldMetrics6.get("Ip6OutNoRoutes"); + + long timeDelta = kvTimestamp - oldkvTimestamp; + double inbps = 8 * 1.0e3 * (curphy.get("inbytes") - oldphy.get("inbytes")) / timeDelta; + double outbps = 8 * 1.0e3 * (curphy.get("outbytes") - oldphy.get("outbytes")) / timeDelta; + double inPacketRate4 = 1.0e3 * (nin) / timeDelta; + double outPacketRate4 = 1.0e3 * (nout) / timeDelta; + double inDropRate4 = 1.0e3 * (nin - delivin) / timeDelta; + double outDropRate4 = 1.0e3 * (dropout) / timeDelta; + double inPacketRate6 = 1.0e3 * (nin6) / timeDelta; + double outPacketRate6 = 1.0e3 * (nout6) / timeDelta; + double inDropRate6 = 1.0e3 * (nin6 - delivin6) / timeDelta; + double outDropRate6 = 1.0e3 * (dropout6) / timeDelta; + + NetInterfaceSummary inNetwork = + new NetInterfaceSummary( + NetInterfaceSummary.Direction.in, + inPacketRate4, + inDropRate4, + inPacketRate6, + inDropRate6, + inbps); + + NetInterfaceSummary outNetwork = + new NetInterfaceSummary( + NetInterfaceSummary.Direction.out, + outPacketRate4, + outDropRate4, + outPacketRate6, + outDropRate6, + outbps); + + linuxIPMetricsGenerator.setInNetworkInterfaceSummary(inNetwork); + linuxIPMetricsGenerator.setOutNetworkInterfaceSummary(outNetwork); + } + + private static void getKeys(String line) { + if (IPkeys != null) { + // { && TCPkeys != null && + // UDPkeys != null && ICMPkeys != null) { + return; + } + if (line.startsWith("Ip:")) { + IPkeys = line.split("\\s+"); + } /*else if (line.startsWith("Icmp:")) { + ICMPkeys = line.split("\\s+"); + } else if (line.startsWith("Tcp:")) { + TCPkeys = line.split("\\s+"); + } else if (line.startsWith("Udp:")) { + UDPkeys = line.split("\\s+"); + }*/ + } + + private static void generateMap(String line) { + Map map = null; + String[] keys = null; + if (line.startsWith("Ip:")) { + map = currentMetrics.IPmetrics; + keys = IPkeys; + } /*else if (line.startsWith("Icmp:")) { + map = currentMetrics.ICMPmetrics; + keys = ICMPkeys; + } else if (line.startsWith("Tcp:")) { + map = currentMetrics.TCPmetrics; + keys = TCPkeys; + } else if (line.startsWith("Udp:")) { + map = currentMetrics.UDPmetrics; + keys = UDPkeys; + }*/ + if (keys != null) { + generateMap(line, keys, map); + } + } + + private static void generateMap(String line, String[] keys, Map map) { + String[] values = line.split("\\s+"); + int count = values.length; + map.put(keys[0], 0L); + for (int i = 1; i < count; i++) { + map.put(keys[i], Long.parseLong(values[i])); + } + } + + private static void addSample4() { + int ln = 0; + + oldMetrics.clearAll(); + oldMetrics.putAll(currentMetrics); + currentMetrics.clearAll(); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + + try (FileReader fileReader = new FileReader(new File("/proc/net/snmp")); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { + String line = null; + while ((line = bufferedReader.readLine()) != null) { + if (ln % 2 == 0) { // keys + getKeys(line); + } else { + generateMap(line); + } + ln++; + } + } catch (Exception e) { + LOG.debug( + "Exception in calling addSample4 with details: {} with ExceptionCode: {}", + () -> e.toString(), + () -> StatExceptionCode.NETWORK_COLLECTION_ERROR.toString()); + StatsCollector.instance().logException(StatExceptionCode.NETWORK_COLLECTION_ERROR); + } + } + + private static void addSample6() { + oldMetrics6.clear(); + oldMetrics6.putAll(currentMetrics6); + currentMetrics6.clear(); + + try (FileReader fileReader = new FileReader(new File("/proc/net/snmp6")); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { + String line = null; + while ((line = bufferedReader.readLine()) != null) { + List toks = STRING_PATTERN_SPLITTER.splitToList(line); + if (toks.size() > 1) { + currentMetrics6.put(toks.get(0), Long.parseLong(toks.get(1))); + } + } + } catch (Exception e) { + LOG.debug( + "Exception in calling addSample6 with details: {} with ExceptionCode: {}", + () -> e.toString(), + () -> StatExceptionCode.NETWORK_COLLECTION_ERROR.toString()); + StatsCollector.instance().logException(StatExceptionCode.NETWORK_COLLECTION_ERROR); + } + } + + // this assumes that addSample4() is called + private static void addDeviceStats() { + try (FileReader fileReader = new FileReader(new File("/proc/net/dev")); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { + String line = null; + long intotbytes = 0; + long outtotbytes = 0; + long intotpackets = 0; + long outtotpackets = 0; + while ((line = bufferedReader.readLine()) != null) { + if (line.contains("Receive") || line.contains("packets")) { + continue; + } + String[] toks = line.trim().split(" +"); + intotbytes += Long.parseLong(toks[1]); + intotpackets += Long.parseLong(toks[2]); + outtotbytes += Long.parseLong(toks[9]); + outtotpackets += Long.parseLong(toks[10]); + } + currentMetrics.PHYmetrics.put("inbytes", intotbytes); + currentMetrics.PHYmetrics.put("inpackets", intotpackets); + currentMetrics.PHYmetrics.put("outbytes", outtotbytes); + currentMetrics.PHYmetrics.put("outpackets", outtotpackets); + } catch (Exception e) { + LOG.debug( + "Exception in calling addDeviceStats with details: {} with ExceptionCode: {}", + () -> e.toString(), + () -> StatExceptionCode.NETWORK_COLLECTION_ERROR.toString()); + StatsCollector.instance().logException(StatExceptionCode.NETWORK_COLLECTION_ERROR); + } + } + + public static void addSample() { + addSampleHelper(); + calculateNetworkMetrics(); + } + + private static synchronized void addSampleHelper() { + addSample4(); + addSample6(); + addDeviceStats(); + } + + public static void runOnce() { + addSample(); + } + + @VisibleForTesting + Map getCurrentPhyMetric() { + return currentMetrics.PHYmetrics; + } + + @VisibleForTesting + Map getCurrentIpMetric() { + return currentMetrics.IPmetrics; + } + + @VisibleForTesting + Map getOldPhyMetric() { + return oldMetrics.PHYmetrics; + } + + @VisibleForTesting + Map getOldIpMetric() { + return oldMetrics.IPmetrics; + } + + @VisibleForTesting + Map getCurrentMetrics6() { + return currentMetrics6; + } + + @VisibleForTesting + Map getOldMetrics6() { + return oldMetrics6; + } + + @VisibleForTesting + void putCurrentPhyMetric(String key, Long value) { + currentMetrics.PHYmetrics.put(key, value); + } + + @VisibleForTesting + void putCurrentIpMetric(String key, Long value) { + currentMetrics.IPmetrics.put(key, value); + } + + @VisibleForTesting + void putOldPhyMetric(String key, Long value) { + oldMetrics.PHYmetrics.put(key, value); + } + + @VisibleForTesting + void putOldIpMetric(String key, Long value) { + oldMetrics.IPmetrics.put(key, value); + } + + @VisibleForTesting + void putCurrentMetrics6(String key, Long value) { + currentMetrics6.put(key, value); + } + + @VisibleForTesting + void putOldMetrics6(String key, Long value) { + oldMetrics6.put(key, value); + } + + @VisibleForTesting + static void setKvTimestamp(long value) { + NetworkInterface.kvTimestamp = value; + } + + @VisibleForTesting + static void setOldkvTimestamp(long oldkvTimestamp) { + NetworkInterface.oldkvTimestamp = oldkvTimestamp; + } + + @VisibleForTesting + static long getKvTimestamp() { + return kvTimestamp; + } + + @VisibleForTesting + static long getOldkvTimestamp() { + return oldkvTimestamp; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetrics.java b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetrics.java new file mode 100644 index 0000000..496101b --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetrics.java @@ -0,0 +1,133 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class GCMetrics { + private static GarbageCollectorMXBean fullGC = null; + private static GarbageCollectorMXBean youngGC = null; + + private static long totYoungGCCollectionCount = 0; + private static long totYoungGCCollectionTime = 0; + private static long totFullGCCollectionCount = 0; + private static long totFullGCCollectionTime = 0; + + private static long lastYoungGCCollectionCount = 0; + private static long lastYoungGCCollectionTime = 0; + private static long lastFullGCCollectionCount = 0; + private static long lastFullGCCollectionTime = 0; + private static final Logger LOGGER = LogManager.getLogger(GCMetrics.class); + + static { + for (GarbageCollectorMXBean item : ManagementFactory.getGarbageCollectorMXBeans()) { + if ("ConcurrentMarkSweep".equals(item.getName()) + || "MarkSweepCompact".equals(item.getName()) + || "PS MarkSweep".equals(item.getName()) + || "G1 Old Generation".equals(item.getName()) + || "Garbage collection optimized for short pausetimes Old Collector" + .equals(item.getName()) + || "Garbage collection optimized for throughput Old Collector" + .equals(item.getName()) + || "Garbage collection optimized for deterministic pausetimes Old Collector" + .equals(item.getName())) { + fullGC = item; + } else if ("ParNew".equals(item.getName()) + || "Copy".equals(item.getName()) + || "PS Scavenge".equals(item.getName()) + || "G1 Young Generation".equals(item.getName()) + || "Garbage collection optimized for short pausetimes Young Collector" + .equals(item.getName()) + || "Garbage collection optimized for throughput Young Collector" + .equals(item.getName()) + || "Garbage collection optimized for deterministic pausetimes Young Collector" + .equals(item.getName())) { + youngGC = item; + } else { + LOGGER.error("MX bean missing: {}", () -> item.getName()); + } + } + } + + public static long getTotYoungGCCollectionCount() { + return totYoungGCCollectionCount; + } + + public static long getTotYoungGCCollectionTime() { + return totYoungGCCollectionTime; + } + + public static long getTotFullGCCollectionCount() { + return totFullGCCollectionCount; + } + + public static long getTotFullGCCollectionTime() { + return totFullGCCollectionTime; + } + + private static long getYoungGCCollectionCount() { + if (youngGC == null) { + return 0; + } + return youngGC.getCollectionCount(); + } + + private static long getYoungGCCollectionTime() { + if (youngGC == null) { + return 0; + } + return youngGC.getCollectionTime(); + } + + private static long getFullGCCollectionCount() { + if (fullGC == null) { + return 0; + } + return fullGC.getCollectionCount(); + } + + private static long getFullGCCollectionTime() { + if (fullGC == null) { + return 0; + } + return fullGC.getCollectionTime(); + } + + public static void runGCMetrics() { + long YoungGCCollectionCount = getYoungGCCollectionCount(); + long YoungGCCollectionTime = getYoungGCCollectionTime(); + long FullGCCollectionCount = getFullGCCollectionCount(); + long FullGCCollectionTime = getFullGCCollectionTime(); + + totYoungGCCollectionCount = YoungGCCollectionCount - lastYoungGCCollectionCount; + totYoungGCCollectionTime = YoungGCCollectionTime - lastYoungGCCollectionTime; + totFullGCCollectionCount = FullGCCollectionCount - lastFullGCCollectionCount; + totFullGCCollectionTime = FullGCCollectionTime - lastFullGCCollectionTime; + + lastYoungGCCollectionCount = YoungGCCollectionCount; + lastYoungGCCollectionTime = YoungGCCollectionTime; + lastFullGCCollectionCount = FullGCCollectionCount; + lastFullGCCollectionTime = FullGCCollectionTime; + } + + static void printGCMetrics() { + if (lastYoungGCCollectionCount >= 0) { + System.out.println( + "GC:: yC:" + + getTotYoungGCCollectionCount() + + " yT:" + + getTotYoungGCCollectionTime() + + " oC:" + + getTotFullGCCollectionCount() + + " oT:" + + getTotFullGCCollectionTime()); + } + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GarbageCollectorInfo.java b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GarbageCollectorInfo.java new file mode 100644 index 0000000..3809235 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/GarbageCollectorInfo.java @@ -0,0 +1,78 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class GarbageCollectorInfo { + + private static final Map> gcSuppliers; + private static final ImmutableMap memoryPoolMap; + + private static final String SURVIVOR = "Survivor"; + private static final String EDEN = "Eden"; + private static final String OLD_GEN = "OldGen"; + private static final String PERM_GEN = "PermGen"; + + static { + gcSuppliers = new HashMap<>(); + memoryPoolMap = + ImmutableMap.builder() + // Perm gen region names as read by different collectors. + .put("CMS Perm Gen", PERM_GEN) + .put("Perm Gen", PERM_GEN) + .put("PS Perm Gen", PERM_GEN) + .put("G1 Perm Gen", PERM_GEN) + .put("Metaspace", PERM_GEN) + // Old gen region names as read by different collectors. + .put("CMS Old Gen", OLD_GEN) + .put("Tenured Gen", OLD_GEN) + .put("PS Old Gen", OLD_GEN) + .put("G1 Old Gen", OLD_GEN) + // Young gen region names as read by different collectors. + .put("Par Eden Space", EDEN) + .put("Eden Space", EDEN) + .put("PS Eden Space", EDEN) + .put("G1 Eden", EDEN) + .put("G1 Eden Space", EDEN) + // Survivor space as read by different collectors. + .put("Par Survivor Space", SURVIVOR) + .put("Survivor Space", SURVIVOR) + .put("PS Survivor Space", SURVIVOR) + .put("G1 Survivor", SURVIVOR) + .put("G1 Survivor Space", SURVIVOR) + .build(); + + List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + + for (GarbageCollectorMXBean gcBean : gcBeans) { + String[] memoryPools = gcBean.getMemoryPoolNames(); + if (memoryPools != null && memoryPools.length > 0) { + for (String memoryPool : memoryPools) { + String genericMemoryPool = memoryPoolMap.getOrDefault(memoryPool, memoryPool); + gcSuppliers.putIfAbsent(genericMemoryPool, gcBean::getName); + } + } + } + } + + public static Map> getGcSuppliers() { + return gcSuppliers; + } + + @VisibleForTesting + public static ImmutableMap getMemoryPoolMap() { + return memoryPoolMap; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetrics.java b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetrics.java new file mode 100644 index 0000000..e90cfd3 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetrics.java @@ -0,0 +1,61 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class HeapMetrics { + private static final Map> memoryUsageSuppliers; + + static { + memoryUsageSuppliers = new HashMap<>(); + MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + if (memoryMXBean != null) { + memoryUsageSuppliers.put("Heap", () -> memoryMXBean.getHeapMemoryUsage()); + memoryUsageSuppliers.put("NonHeap", () -> memoryMXBean.getNonHeapMemoryUsage()); + } + + List list = ManagementFactory.getMemoryPoolMXBeans(); + for (MemoryPoolMXBean item : list) { + if ("CMS Perm Gen".equals(item.getName()) + || "Perm Gen".equals(item.getName()) + || "PS Perm Gen".equals(item.getName()) + || "G1 Perm Gen".equals(item.getName()) + || "Metaspace".equals(item.getName())) { + memoryUsageSuppliers.put("PermGen", () -> item.getUsage()); + } else if ("CMS Old Gen".equals(item.getName()) + || "Tenured Gen".equals(item.getName()) + || "PS Old Gen".equals(item.getName()) + || "G1 Old Gen".equals(item.getName())) { + memoryUsageSuppliers.put("OldGen", () -> item.getUsage()); + } else if ("Par Eden Space".equals(item.getName()) + || "Eden Space".equals(item.getName()) + || "PS Eden Space".equals(item.getName()) + || "G1 Eden".equals(item.getName()) + || "G1 Eden Space".equals(item.getName())) { + memoryUsageSuppliers.put("Eden", () -> item.getUsage()); + } else if ("Par Survivor Space".equals(item.getName()) + || "Survivor Space".equals(item.getName()) + || "PS Survivor Space".equals(item.getName()) + || "G1 Survivor".equals(item.getName()) + || "G1 Survivor Space".equals(item.getName())) { + memoryUsageSuppliers.put("Survivor", () -> item.getUsage()); + } + } + } + + public static Map> getMemoryUsageSuppliers() { + return memoryUsageSuppliers; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadList.java b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadList.java new file mode 100644 index 0000000..be32d32 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadList.java @@ -0,0 +1,426 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import com.sun.tools.attach.VirtualMachine; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; +import org.opensearch.performanceanalyzer.commons.util.Util; +import sun.tools.attach.HotSpotVirtualMachine; + +/** Traverses and prints the stack traces for all Java threads in the remote VM */ +public class ThreadList { + private static final Map jTidNameMap = new ConcurrentHashMap<>(); + private static final Map nativeTidMap = new ConcurrentHashMap<>(); + private static final Map oldNativeTidMap = new ConcurrentHashMap<>(); + private static final Map jTidMap = new ConcurrentHashMap<>(); + private static final Map nameMap = new ConcurrentHashMap<>(); + private static final String pid = OSMetricsGeneratorFactory.getInstance().getPid(); + static final Logger LOGGER = LogManager.getLogger(ThreadList.class); + static final int samplingInterval = + MetricsConfiguration.CONFIG_MAP.get(ThreadList.class).samplingInterval; + + // This value controls how often we do the thread dump. + private static final long minRunInterval = samplingInterval; + private static final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + private static final Pattern linePattern = Pattern.compile("\"([^\"]*)\""); + private static long lastRunTime = 0; + + private static Lock vmAttachLock = new ReentrantLock(); + + public static class ThreadState { + public long javaTid; + public long nativeTid; + public long heapUsage; + public String threadName; + public String tState; + public Thread.State state; + public long blockedCount; + public long blockedTime; + public long waitedCount; + public long waitedTime; + + public double heapAllocRate; + public double avgBlockedTime; + public double avgWaitedTime; + + ThreadState() { + javaTid = -1; + nativeTid = -1; + heapUsage = -1; + heapAllocRate = 0; + blockedCount = 0; + blockedTime = 0; + waitedCount = 0; + waitedTime = 0; + avgBlockedTime = 0; + avgWaitedTime = 0; + threadName = ""; + tState = ""; + } + + @Override + public String toString() { + return new StringBuilder() + .append("javatid:") + .append(javaTid) + .append(" nativetid:") + .append(nativeTid) + .append(" name:") + .append(threadName) + .append(" state:") + .append(tState) + .append("(") + .append(state) + .append(")") + .append(" heaprate: ") + .append(heapAllocRate) + .append(" bTime: ") + .append(avgBlockedTime) + .append(":") + .append(blockedCount) + .append(" wTime: ") + .append(avgWaitedTime) + .append(":") + .append(waitedCount) + .toString(); + } + } + + /** + * This is called from OSMetricsCollector#collectMetrics. So this is not called in the critical + * path of OpenSearch request handling. Even for the collector thread, we do a timed wait to + * acquire this lock and move on if we could not get it. + * + * @return A hashmap of threadId to threadState. + * @param threadContentionMonitoringEnabled + */ + public static Map getNativeTidMap( + boolean threadContentionMonitoringEnabled) { + if (threadBean.isThreadContentionMonitoringSupported()) { + threadBean.setThreadContentionMonitoringEnabled(threadContentionMonitoringEnabled); + } + if (vmAttachLock.tryLock()) { + try { + // Thread dumps are expensive and therefore we make sure that at least + // minRunInterval milliseconds have elapsed between two attempts. + if (System.currentTimeMillis() > lastRunTime + minRunInterval) { + runThreadDump(pid, new String[0]); + } + } finally { + vmAttachLock.unlock(); + } + } + // - sending a copy so that if runThreadDump next iteration clears it; caller still has the + // state at the call time + // - not too expensive as this is only being called from Scheduled Collectors (only once in + // few seconds) + return new HashMap<>(nativeTidMap); + } + + /** + * This method is called from the critical bulk and search paths which PA intercepts. This + * method used to try to do a thread dump if it could not find the information about the thread + * in question. The thread dump is an expensive operation and can stall see + * VirtualMachineImpl#VirtualMachineImpl() for jdk-11 u06. We don't want the OpenSearch threads + * to pay the price. We skip this iteration and then hopefully in the next call to + * getNativeTidMap(), the OSMetricsCollector#collectMetrics will fill the jTidMap. This + * transfers the responsibility from the OpenSearch threads to the PA collector threads. + * + * @param threadId The threadId of the current thread. + * @return If we have successfully captured the ThreadState, then we emit it or Null otherwise. + */ + public static ThreadState getThreadState(long threadId) { + return jTidMap.get(threadId); + } + + // Attach to pid and perform a thread dump + private static void runAttachDump(String pid, String[] args) { + + VirtualMachine vm = null; + try { + vm = VirtualMachine.attach(pid); + } catch (Exception ex) { + // If the thread dump failed then we clean up the old map. So, next time when the + // collection + // happens as it would after a bootup. + oldNativeTidMap.clear(); + return; + } + + try (InputStream in = ((HotSpotVirtualMachine) vm).remoteDataDump(args); ) { + createMap(in); + } catch (Exception ex) { + oldNativeTidMap.clear(); + } + + try { + vm.detach(); + } catch (Exception ex) { + LOGGER.error("VM detaching failed", ex); + } + } + + public static void parseAllThreadInfos(ThreadInfo[] infos) { + for (ThreadInfo info : infos) { + try { + parseThreadInfo(info); + } catch (Exception ex) { + LOGGER.error("Parsing thread info failed", ex); + // CommonStats.ERRORS_AND_EXCEPTIONS_AGGREGATOR.updateStat( + // StatExceptionCode.JVM_THREAD_ID_NO_LONGER_EXISTS, "", 1); + } + } + } + + public static ThreadInfo[] getAllThreadInfos() { + long[] ids = threadBean.getAllThreadIds(); + return threadBean.getThreadInfo(ids); + } + + // ThreadMXBean-based info for tid, name and allocs + private static void runMXDump() { + ThreadInfo[] infos = getAllThreadInfos(); + parseAllThreadInfos(infos); + ThreadHistory.cleanup(); + } + + private static void parseThreadInfo(final ThreadInfo info) { + long id = info.getThreadId(); + String name = info.getThreadName(); + Thread.State state = info.getThreadState(); + + // following captures cumulative allocated bytes + TLAB used bytes + // and it is cumulative + long mem = ((com.sun.management.ThreadMXBean) threadBean).getThreadAllocatedBytes(id); + + ThreadState t = jTidMap.get(id); + if (t == null) { + return; + } + t.heapUsage = mem; + t.state = state; + t.blockedCount = info.getBlockedCount(); + t.blockedTime = info.getBlockedTime(); + t.waitedCount = info.getWaitedCount(); + t.waitedTime = info.getWaitedTime(); + ThreadHistory.addBlocked( + t.nativeTid, (state == Thread.State.BLOCKED) ? samplingInterval : 0); + ThreadHistory.addWaited( + t.nativeTid, + (state == Thread.State.WAITING || state == Thread.State.TIMED_WAITING) + ? samplingInterval + : 0); + + long curRunTime = System.currentTimeMillis(); + ThreadState oldt = oldNativeTidMap.get(t.nativeTid); + if (curRunTime > lastRunTime && oldt != null) { + t.heapAllocRate = + Math.max(t.heapUsage - oldt.heapUsage, 0) * 1.0e3 / (curRunTime - lastRunTime); + if (t.blockedTime != -1 && t.blockedCount > oldt.blockedCount) { + t.avgBlockedTime = + 1.0e-3 + * (t.blockedTime - oldt.blockedTime) + / (t.blockedCount - oldt.blockedCount); + } else if (t.blockedCount == oldt.blockedCount && t.blockedTime > oldt.blockedTime) { + t.avgBlockedTime = + 1.0e-3 * (t.blockedTime - oldt.blockedTime + oldt.avgBlockedTime); + } else { + CircularLongArray arr = ThreadHistory.blockedTidHistoryMap.get(t.nativeTid); + // NOTE: this is an upper bound + if (arr != null) { + t.avgBlockedTime = 1.0 * arr.getAvgValue() / samplingInterval; + } + } + if (t.waitedTime != -1 && t.waitedCount > oldt.waitedCount) { + t.avgWaitedTime = + 1.0e-3 + * (t.waitedTime - oldt.waitedTime) + / (t.waitedCount - oldt.waitedCount); + } else if (t.waitedCount == oldt.waitedCount && t.waitedTime > oldt.waitedTime) { + t.avgWaitedTime = 1.0e-3 * (t.waitedTime - oldt.waitedTime + oldt.avgWaitedTime); + } else { + CircularLongArray arr = ThreadHistory.waitedTidHistoryMap.get(t.nativeTid); + // NOTE: this is an upper bound + if (arr != null) { + t.avgWaitedTime = 1.0 * arr.getAvgValue() / samplingInterval; + } + } + } + jTidNameMap.put(id, name); + } + + static void runThreadDump(String pid, String[] args) { + String currentThreadName = Thread.currentThread().getName(); + + jTidNameMap.clear(); + oldNativeTidMap.putAll(nativeTidMap); + nativeTidMap.clear(); + jTidMap.clear(); + nameMap.clear(); + + // TODO: make this map update atomic + Util.invokePrivileged(() -> runAttachDump(pid, args)); + // oldNativeTidMap gets cleared if the attach Fails, so that the + // metrics collection starts as it would after a restart. + if (!oldNativeTidMap.isEmpty()) { + runMXDump(); + } + lastRunTime = System.currentTimeMillis(); + } + + private static void parseLine(String line) { + String[] tokens = line.split(" os_prio=[0-9]* "); + ThreadState t = new ThreadState(); + t.javaTid = -1; + + Matcher m = linePattern.matcher(tokens[0]); + if (!m.find()) { + t.threadName = tokens[0]; + } else { + t.threadName = m.group(1); + if (!tokens[0].equals("\"" + t.threadName + "\"")) { + t.javaTid = + Long.parseLong( + tokens[0] + .split(Pattern.quote("\"" + t.threadName + "\" "))[1] + .split(" ")[0] + .split("#")[1]); + } + } + + tokens = tokens[1].split(" "); + for (String token : tokens) { + String[] keyValuePare = token.split("="); + if (keyValuePare.length < 2) { + continue; + } + if (t.javaTid == -1 && keyValuePare[0].equals("tid")) { + t.javaTid = Long.decode(keyValuePare[1]); + } + if (keyValuePare[0].equals("nid")) { + t.nativeTid = Long.decode(keyValuePare[1]); + } + } + t.tState = tokens[2]; // TODO: stuff like "in Object.wait()" + nativeTidMap.put(t.nativeTid, t); + jTidMap.put(t.javaTid, t); + nameMap.put(t.threadName, t); // XXX: we assume no collisions + } + + private static void createMap(InputStream in) throws Exception { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String line = null; + while ((line = br.readLine()) != null) { + if (line.contains("tid=")) { + parseLine(line); + } + } + } + + // currently stores thread states to track locking periods + static class ThreadHistory { + public static Map blockedTidHistoryMap = new HashMap<>(); + public static Map waitedTidHistoryMap = new HashMap<>(); + private static final int HISTORY_SIZE = 60; // 60 * samplingInterval + + public static void addBlocked(long tid, long value) { + add(tid, value, blockedTidHistoryMap); + } + + public static void addWaited(long tid, long value) { + add(tid, value, waitedTidHistoryMap); + } + + public static void cleanup() { + long curTime = System.currentTimeMillis(); + cleanUp(curTime, blockedTidHistoryMap); + cleanUp(curTime, waitedTidHistoryMap); + } + + private static void add(long tid, long value, Map tidHistoryMap) { + CircularLongArray arr = tidHistoryMap.get(tid); + if (arr == null) { + arr = new CircularLongArray(HISTORY_SIZE); + arr.add(value); + tidHistoryMap.put(tid, arr); + } else { + arr.add(value); + } + } + + private static void cleanUp(long curTime, Map tidHistoryMap) { + for (Iterator> it = + tidHistoryMap.entrySet().iterator(); + it.hasNext(); ) { + Map.Entry me = it.next(); + CircularLongArray arr = me.getValue(); + // delete items updated older than 300s + if (curTime - arr.lastWriteTimestamp > HISTORY_SIZE * samplingInterval * 1.0e3) { + it.remove(); + } + } + } + } + + // models a fixed-capacity queue that is append-only + // not thread-safe + static class CircularLongArray { + ArrayList list = null; + public long lastWriteTimestamp; + private long totalValue; + private int startidx; + private int capacity; + + CircularLongArray(int capacity) { + list = new ArrayList<>(capacity); + this.capacity = capacity; + totalValue = 0; + startidx = 0; + lastWriteTimestamp = 0; + } + + public boolean add(long e) { + lastWriteTimestamp = System.currentTimeMillis(); + if (list.size() < capacity) { + // can only happen if startidx == 0 + if (startidx != 0) { + return false; + } else { + totalValue += e; + return list.add(e); + } + } + totalValue -= list.get(startidx); + totalValue += e; + list.set(startidx, e); + startidx = (startidx + 1) % capacity; + return true; + } + + public double getAvgValue() { + return list.size() == 0 ? 0 : 1.0 * totalValue / list.size(); + } + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/MetricsConfiguration.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/MetricsConfiguration.java index 8629e47..ac119a9 100644 --- a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/MetricsConfiguration.java +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/MetricsConfiguration.java @@ -8,6 +8,14 @@ import java.util.HashMap; import java.util.Map; +import org.opensearch.performanceanalyzer.commons.collectors.*; +import org.opensearch.performanceanalyzer.commons.jvm.GCMetrics; +import org.opensearch.performanceanalyzer.commons.jvm.HeapMetrics; +import org.opensearch.performanceanalyzer.commons.jvm.ThreadList; +import org.opensearch.performanceanalyzer.commons.os.OSGlobals; +import org.opensearch.performanceanalyzer.commons.os.ThreadCPU; +import org.opensearch.performanceanalyzer.commons.os.ThreadDiskIO; +import org.opensearch.performanceanalyzer.commons.os.ThreadSched; public class MetricsConfiguration { public static final int SAMPLING_INTERVAL = 5000; @@ -33,5 +41,19 @@ public MetricConfig(int samplingInterval, int rotationInterval) { static { cdefault = new MetricConfig(SAMPLING_INTERVAL, 0); CONFIG_MAP.put(PerformanceAnalyzerMetrics.class, new MetricConfig(0, ROTATION_INTERVAL)); + CONFIG_MAP.put(ThreadCPU.class, cdefault); + CONFIG_MAP.put(ThreadDiskIO.class, cdefault); + CONFIG_MAP.put(ThreadSched.class, cdefault); + CONFIG_MAP.put(ThreadList.class, cdefault); + CONFIG_MAP.put(GCMetrics.class, cdefault); + CONFIG_MAP.put(HeapMetrics.class, cdefault); + CONFIG_MAP.put(NetworkE2ECollector.class, cdefault); + CONFIG_MAP.put(NetworkInterfaceCollector.class, cdefault); + CONFIG_MAP.put(OSGlobals.class, cdefault); + CONFIG_MAP.put(StatsCollector.class, new MetricConfig(STATS_ROTATION_INTERVAL, 0)); + CONFIG_MAP.put(DisksCollector.class, cdefault); + CONFIG_MAP.put(HeapMetricsCollector.class, cdefault); + CONFIG_MAP.put(GCInfoCollector.class, cdefault); + CONFIG_MAP.put(MountedPartitionMetricsCollector.class, cdefault); } } diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/CPUPagingActivityGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/CPUPagingActivityGenerator.java new file mode 100644 index 0000000..d8a4bf6 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/CPUPagingActivityGenerator.java @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + +public interface CPUPagingActivityGenerator { + + // This method will be called before all following get methods + // to make sure that all information exists for a thread id + boolean hasPagingActivity(String threadId); + + double getCPUUtilization(String threadId); + + double getMajorFault(String threadId); + + double getMinorFault(String threadId); + + double getResidentSetSize(String threadId); + + void addSample(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskIOMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskIOMetricsGenerator.java new file mode 100644 index 0000000..d2a213c --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskIOMetricsGenerator.java @@ -0,0 +1,29 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + +public interface DiskIOMetricsGenerator { + + // This method will be called before all following get methods + // to make sure that all information exists for a thread id + boolean hasDiskIOMetrics(String threadId); + + // these metrics include page cache activity; + // only explicit syscalls: NO mmaps (majflts include mmaps) + double getAvgReadThroughputBps(String threadId); + + double getAvgWriteThroughputBps(String threadId); + + double getAvgTotalThroughputBps(String threadId); + + double getAvgReadSyscallRate(String threadId); + + double getAvgWriteSyscallRate(String threadId); + + double getAvgTotalSyscallRate(String threadId); + + void addSample(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskMetricsGenerator.java new file mode 100644 index 0000000..81c0eee --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskMetricsGenerator.java @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + + +import java.util.Set; + +public interface DiskMetricsGenerator { + Set getAllDisks(); + + double getDiskUtilization(String disk); + + double getAwait(String disk); + + double getServiceRate(String disk); + + void addSample(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/IPMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/IPMetricsGenerator.java new file mode 100644 index 0000000..212733e --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/IPMetricsGenerator.java @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + +public interface IPMetricsGenerator { + double getInPacketRate4(); + + double getOutPacketRate4(); + + double getInDropRate4(); + + double getOutDropRate4(); + + double getInPacketRate6(); + + double getOutPacketRate6(); + + double getInDropRate6(); + + double getOutDropRate6(); + + double getInBps(); + + double getOutBps(); + + void addSample(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/MountedPartitionMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/MountedPartitionMetricsGenerator.java new file mode 100644 index 0000000..81b859e --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/MountedPartitionMetricsGenerator.java @@ -0,0 +1,23 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + + +import java.util.Set; + +public interface MountedPartitionMetricsGenerator { + void addSample(); + + Set getAllMountPoints(); + + String getDevicePartition(String mountPoint); + + long getTotalSpace(String mountPoint); + + long getFreeSpace(String mountPoint); + + long getUsableFreeSpace(String mountPoint); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/OSMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/OSMetricsGenerator.java new file mode 100644 index 0000000..a1aa080 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/OSMetricsGenerator.java @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + + +import java.util.Set; + +public interface OSMetricsGenerator { + + String getPid(); + + CPUPagingActivityGenerator getPagingActivityGenerator(); + + SchedMetricsGenerator getSchedMetricsGenerator(); + + Set getAllThreadIds(); + + DiskIOMetricsGenerator getDiskIOMetricsGenerator(); + + TCPMetricsGenerator getTCPMetricsGenerator(); + + IPMetricsGenerator getIPMetricsGenerator(); + + DiskMetricsGenerator getDiskMetricsGenerator(); + + MountedPartitionMetricsGenerator getMountedPartitionMetricsGenerator(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/SchedMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/SchedMetricsGenerator.java new file mode 100644 index 0000000..cc6f4ec --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/SchedMetricsGenerator.java @@ -0,0 +1,21 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + +public interface SchedMetricsGenerator { + + // This method will be called before all following get methods + // to make sure that all information exists for a thread id + boolean hasSchedMetrics(String threadId); + + double getAvgRuntime(String threadId); + + double getAvgWaittime(String threadId); + + double getContextSwitchRate(String threadId); + + void addSample(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/TCPMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/TCPMetricsGenerator.java new file mode 100644 index 0000000..4bdb250 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/TCPMetricsGenerator.java @@ -0,0 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator; + + +import java.util.Set; + +public interface TCPMetricsGenerator { + + Set getAllDestionationIps(); + + int getNumberOfFlows(String ip); + + double getTransmitQueueSize(String ip); + + double getReceiveQueueSize(String ip); + + double getCurrentLost(String ip); + + double getSendCongestionWindow(String ip); + + double getSlowStartThreshold(String ip); + + void addSample(); +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxCPUPagingActivityGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxCPUPagingActivityGenerator.java new file mode 100644 index 0000000..f775d58 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxCPUPagingActivityGenerator.java @@ -0,0 +1,76 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.opensearch.performanceanalyzer.commons.metrics_generator.CPUPagingActivityGenerator; +import org.opensearch.performanceanalyzer.commons.os.ThreadCPU; + +public class LinuxCPUPagingActivityGenerator implements CPUPagingActivityGenerator { + + private Map cpu; + private Map pagingActivities; + + public LinuxCPUPagingActivityGenerator() { + cpu = new HashMap<>(); + pagingActivities = new HashMap<>(); + } + + @Override + public double getCPUUtilization(final String threadId) { + + return cpu.getOrDefault(threadId, 0.0); + } + + @Override + public double getMajorFault(final String threadId) { + + return pagingActivities.get(threadId)[0]; + } + + @Override + public double getMinorFault(final String threadId) { + + return pagingActivities.get(threadId)[1]; + } + + @Override + public double getResidentSetSize(final String threadId) { + + return pagingActivities.get(threadId)[2]; + } + + @Override + public boolean hasPagingActivity(final String threadId) { + + return pagingActivities.containsKey(threadId); + } + + @Override + public void addSample() { + + cpu.clear(); + pagingActivities.clear(); + ThreadCPU.INSTANCE.addSample(); + } + + public void setCPUUtilization(final String threadId, final Double cpuUtilization) { + + cpu.put(threadId, cpuUtilization); + } + + public Set getAllThreadIds() { + + return cpu.keySet(); + } + + public void setPagingActivities(final String threadId, final Double[] activityes) { + pagingActivities.put(threadId, activityes); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskIOMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskIOMetricsGenerator.java new file mode 100644 index 0000000..e509888 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskIOMetricsGenerator.java @@ -0,0 +1,72 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import java.util.HashMap; +import java.util.Map; +import org.opensearch.performanceanalyzer.commons.metrics_generator.DiskIOMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.os.ThreadDiskIO; + +public class LinuxDiskIOMetricsGenerator implements DiskIOMetricsGenerator { + + private Map diskIOMetricsMap; + + public LinuxDiskIOMetricsGenerator() { + diskIOMetricsMap = new HashMap<>(); + } + + @Override + public double getAvgReadThroughputBps(final String threadId) { + + return diskIOMetricsMap.get(threadId).avgReadThroughputBps; + } + + @Override + public double getAvgReadSyscallRate(final String threadId) { + + return diskIOMetricsMap.get(threadId).avgReadSyscallRate; + } + + @Override + public double getAvgWriteThroughputBps(final String threadId) { + + return diskIOMetricsMap.get(threadId).avgWriteThroughputBps; + } + + @Override + public double getAvgWriteSyscallRate(final String threadId) { + + return diskIOMetricsMap.get(threadId).avgWriteSyscallRate; + } + + @Override + public double getAvgTotalThroughputBps(final String threadId) { + + return diskIOMetricsMap.get(threadId).avgTotalThroughputBps; + } + + @Override + public double getAvgTotalSyscallRate(final String threadId) { + + return diskIOMetricsMap.get(threadId).avgTotalSyscallRate; + } + + @Override + public boolean hasDiskIOMetrics(final String threadId) { + + return diskIOMetricsMap.containsKey(threadId); + } + + @Override + public void addSample() { + ThreadDiskIO.addSample(); + } + + public void setDiskIOMetrics(final String threadId, final ThreadDiskIO.IOMetrics ioMetrics) { + diskIOMetricsMap.put(threadId, ioMetrics); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskMetricsGenerator.java new file mode 100644 index 0000000..33daaa6 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskMetricsGenerator.java @@ -0,0 +1,51 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import java.util.Map; +import java.util.Set; +import org.opensearch.performanceanalyzer.commons.collectors.DiskMetrics; +import org.opensearch.performanceanalyzer.commons.hwnet.Disks; +import org.opensearch.performanceanalyzer.commons.metrics_generator.DiskMetricsGenerator; + +public class LinuxDiskMetricsGenerator implements DiskMetricsGenerator { + + private Map diskMetricsMap; + + @Override + public Set getAllDisks() { + return diskMetricsMap.keySet(); + } + + @Override + public double getDiskUtilization(final String disk) { + + return diskMetricsMap.get(disk).utilization; + } + + @Override + public double getAwait(final String disk) { + + return diskMetricsMap.get(disk).await; + } + + @Override + public double getServiceRate(final String disk) { + + return diskMetricsMap.get(disk).serviceRate; + } + + @Override + public void addSample() { + Disks.addSample(); + } + + public void setDiskMetricsMap(final Map map) { + + diskMetricsMap = map; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxIPMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxIPMetricsGenerator.java new file mode 100644 index 0000000..4f26d62 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxIPMetricsGenerator.java @@ -0,0 +1,93 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import org.opensearch.performanceanalyzer.commons.collectors.NetInterfaceSummary; +import org.opensearch.performanceanalyzer.commons.hwnet.NetworkInterface; +import org.opensearch.performanceanalyzer.commons.metrics_generator.IPMetricsGenerator; + +public class LinuxIPMetricsGenerator implements IPMetricsGenerator { + + private NetInterfaceSummary inNetInterfaceSummary; + private NetInterfaceSummary outNetInterfaceSummary; + + @Override + public double getInPacketRate4() { + + return inNetInterfaceSummary.getPacketRate4(); + } + + @Override + public double getOutPacketRate4() { + + return outNetInterfaceSummary.getPacketRate4(); + } + + @Override + public double getInDropRate4() { + + return inNetInterfaceSummary.getDropRate4(); + } + + @Override + public double getOutDropRate4() { + + return outNetInterfaceSummary.getDropRate4(); + } + + @Override + public double getInPacketRate6() { + + return inNetInterfaceSummary.getPacketRate6(); + } + + @Override + public double getOutPacketRate6() { + + return outNetInterfaceSummary.getPacketRate6(); + } + + @Override + public double getInDropRate6() { + + return inNetInterfaceSummary.getDropRate6(); + } + + @Override + public double getOutDropRate6() { + + return outNetInterfaceSummary.getDropRate6(); + } + + @Override + public double getInBps() { + + return inNetInterfaceSummary.getBps(); + } + + @Override + public double getOutBps() { + + return outNetInterfaceSummary.getBps(); + } + + @Override + public void addSample() { + + NetworkInterface.addSample(); + } + + public void setInNetworkInterfaceSummary(final NetInterfaceSummary netInterfaceSummary) { + + this.inNetInterfaceSummary = netInterfaceSummary; + } + + public void setOutNetworkInterfaceSummary(final NetInterfaceSummary netInterfaceSummary) { + + this.outNetInterfaceSummary = netInterfaceSummary; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxMountedPartitionMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxMountedPartitionMetricsGenerator.java new file mode 100644 index 0000000..b3cb4c2 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxMountedPartitionMetricsGenerator.java @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import com.google.common.collect.ImmutableSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.opensearch.performanceanalyzer.commons.collectors.MountedPartitionMetrics; +import org.opensearch.performanceanalyzer.commons.hwnet.MountedPartitions; +import org.opensearch.performanceanalyzer.commons.metrics_generator.MountedPartitionMetricsGenerator; + +public class LinuxMountedPartitionMetricsGenerator implements MountedPartitionMetricsGenerator { + private static final Map suppliers = new HashMap<>(); + + @Override + public void addSample() { + MountedPartitions.addSample(); + } + + @Override + public Set getAllMountPoints() { + return ImmutableSet.copyOf(suppliers.keySet()); + } + + public void addSupplier(final String mountPoint, final MountedPartitionMetrics supplier) { + suppliers.put(mountPoint, supplier); + } + + @Override + public String getDevicePartition(final String mountPoint) { + return suppliers.get(mountPoint).getDevicePartition(); + } + + @Override + public long getTotalSpace(final String mountPoint) { + return suppliers.get(mountPoint).getTotalSpace(); + } + + @Override + public long getFreeSpace(final String mountPoint) { + return suppliers.get(mountPoint).getFreeSpace(); + } + + @Override + public long getUsableFreeSpace(final String mountPoint) { + return suppliers.get(mountPoint).getUsableFreeSpace(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxOSMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxOSMetricsGenerator.java new file mode 100644 index 0000000..8d4b7a3 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxOSMetricsGenerator.java @@ -0,0 +1,84 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import java.util.Set; +import org.opensearch.performanceanalyzer.commons.hwnet.Disks; +import org.opensearch.performanceanalyzer.commons.hwnet.MountedPartitions; +import org.opensearch.performanceanalyzer.commons.hwnet.NetworkE2E; +import org.opensearch.performanceanalyzer.commons.hwnet.NetworkInterface; +import org.opensearch.performanceanalyzer.commons.metrics_generator.*; +import org.opensearch.performanceanalyzer.commons.os.OSGlobals; +import org.opensearch.performanceanalyzer.commons.os.ThreadCPU; +import org.opensearch.performanceanalyzer.commons.os.ThreadDiskIO; +import org.opensearch.performanceanalyzer.commons.os.ThreadSched; + +public class LinuxOSMetricsGenerator implements OSMetricsGenerator { + + private static OSMetricsGenerator osMetricsGenerator; + + static { + osMetricsGenerator = new LinuxOSMetricsGenerator(); + } + + public static OSMetricsGenerator getInstance() { + + return osMetricsGenerator; + } + + @Override + public String getPid() { + + return OSGlobals.getPid(); + } + + @Override + public CPUPagingActivityGenerator getPagingActivityGenerator() { + + return ThreadCPU.INSTANCE.getCPUPagingActivity(); + } + + @Override + public Set getAllThreadIds() { + return ThreadCPU.INSTANCE.getCPUPagingActivity().getAllThreadIds(); + } + + @Override + public DiskIOMetricsGenerator getDiskIOMetricsGenerator() { + + return ThreadDiskIO.getIOUtilization(); + } + + @Override + public SchedMetricsGenerator getSchedMetricsGenerator() { + + return ThreadSched.INSTANCE.getSchedLatency(); + } + + @Override + public TCPMetricsGenerator getTCPMetricsGenerator() { + + return NetworkE2E.getTCPMetricsHandler(); + } + + @Override + public IPMetricsGenerator getIPMetricsGenerator() { + + return NetworkInterface.getLinuxIPMetricsGenerator(); + } + + @Override + public DiskMetricsGenerator getDiskMetricsGenerator() { + + return Disks.getDiskMetricsHandler(); + } + + @Override + public MountedPartitionMetricsGenerator getMountedPartitionMetricsGenerator() { + return MountedPartitions.getLinuxMountedPartitionMetricsGenerator(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxSchedMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxSchedMetricsGenerator.java new file mode 100644 index 0000000..2704274 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxSchedMetricsGenerator.java @@ -0,0 +1,57 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import java.util.HashMap; +import java.util.Map; +import org.opensearch.performanceanalyzer.commons.metrics_generator.SchedMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.os.ThreadSched; + +public class LinuxSchedMetricsGenerator implements SchedMetricsGenerator { + + private final Map schedMetricsMap; + + public LinuxSchedMetricsGenerator() { + schedMetricsMap = new HashMap<>(); + } + + @Override + public double getAvgRuntime(final String threadId) { + + return schedMetricsMap.get(threadId).avgRuntime; + } + + @Override + public double getAvgWaittime(final String threadId) { + + return schedMetricsMap.get(threadId).avgWaittime; + } + + @Override + public double getContextSwitchRate(final String threadId) { + + return schedMetricsMap.get(threadId).contextSwitchRate; + } + + @Override + public boolean hasSchedMetrics(final String threadId) { + + return schedMetricsMap.containsKey(threadId); + } + + @Override + public void addSample() { + + schedMetricsMap.clear(); + ThreadSched.INSTANCE.addSample(); + } + + public void setSchedMetric(final String threadId, final ThreadSched.SchedMetrics schedMetrics) { + + schedMetricsMap.put(threadId, schedMetrics); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxTCPMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxTCPMetricsGenerator.java new file mode 100644 index 0000000..e2dd562 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxTCPMetricsGenerator.java @@ -0,0 +1,61 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + + +import java.util.Map; +import java.util.Set; +import org.opensearch.performanceanalyzer.commons.hwnet.NetworkE2E; +import org.opensearch.performanceanalyzer.commons.metrics_generator.TCPMetricsGenerator; + +public class LinuxTCPMetricsGenerator implements TCPMetricsGenerator { + + private Map map; + + @Override + public Set getAllDestionationIps() { + return map.keySet(); + } + + @Override + public int getNumberOfFlows(final String ip) { + return (int) map.get(ip)[0]; + } + + @Override + public double getTransmitQueueSize(String ip) { + return map.get(ip)[1]; + } + + @Override + public double getReceiveQueueSize(String ip) { + return map.get(ip)[2]; + } + + @Override + public double getCurrentLost(String ip) { + return map.get(ip)[3]; + } + + @Override + public double getSendCongestionWindow(String ip) { + return map.get(ip)[4]; + } + + @Override + public double getSlowStartThreshold(String ip) { + return map.get(ip)[5]; + } + + @Override + public void addSample() { + NetworkE2E.addSample(); + } + + public void setTCPMetrics(final Map metrics) { + map = metrics; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/os/OSGlobals.java b/src/main/java/org/opensearch/performanceanalyzer/commons/os/OSGlobals.java new file mode 100644 index 0000000..e977306 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/OSGlobals.java @@ -0,0 +1,94 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.opensearch.performanceanalyzer.commons.config.ConfigStatus; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class OSGlobals { + private static long scClkTck; + private static String pid; + private static final String CLK_TCK_SYS_PROPERTY_NAME = "clk.tck"; + + private static final Logger LOGGER = LogManager.getLogger(OSGlobals.class); + private static final long REFRESH_INTERVAL_MS = + MetricsConfiguration.CONFIG_MAP.get(OSGlobals.class).samplingInterval; + private static List tids = new ArrayList<>(); + private static long lastUpdated = -1; + + static { + try { + pid = new File("/proc/self").getCanonicalFile().getName(); + getScClkTckFromConfig(); + enumTids(); + lastUpdated = System.currentTimeMillis(); + } catch (Exception e) { + LOGGER.error( + (Supplier) + () -> + new ParameterizedMessage( + "Error in static initialization of OSGlobals with exception: {}", + e.toString()), + e); + } + } + + public static String getPid() { + return pid; + } + + public static long getScClkTck() { + return scClkTck; + } + + private static void getScClkTckFromConfig() throws Exception { + try { + scClkTck = Long.parseUnsignedLong(System.getProperty(CLK_TCK_SYS_PROPERTY_NAME)); + } catch (Exception e) { + LOGGER.error( + (Supplier) + () -> + new ParameterizedMessage( + "Error in reading/parsing clk.tck value: {}", + e.toString()), + e); + ConfigStatus.INSTANCE.setConfigurationInvalid(); + } + } + + private static void enumTids() { + tids.clear(); + tids.add(pid); + + File self = new File("/proc/self/task"); + File[] filesList = self.listFiles(); + if (filesList != null) { + for (File f : filesList) { + if (f.isDirectory()) { + String tid = f.getName(); + tids.add(tid); + } + } + } + } + + static synchronized List getTids() { + long curtime = System.currentTimeMillis(); + if (curtime - lastUpdated > REFRESH_INTERVAL_MS) { + enumTids(); + lastUpdated = curtime; + } + return new ArrayList<>(tids); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/os/SchemaFileParser.java b/src/main/java/org/opensearch/performanceanalyzer/commons/os/SchemaFileParser.java new file mode 100644 index 0000000..3b4f463 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/SchemaFileParser.java @@ -0,0 +1,163 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; + +public class SchemaFileParser { + private static final Logger LOGGER = LogManager.getLogger(SchemaFileParser.class); + + public enum FieldTypes { + INT, + STRING, + CHAR, + ULONG, + DOUBLE; + } + + private String filename = null; + private String[] keys = null; + private FieldTypes[] types = null; + private boolean preProcess = false; + + public SchemaFileParser(String file, String[] keys, FieldTypes[] types) { + this.filename = file; + this.keys = keys.clone(); + this.types = types.clone(); + } + + // - from java 11 onwards, there is thread name in /proc/pid/task/tid/stat, which has spaces in + // it + // - And threadname has "()" around it. Introduced a preprocess step to combine all of them + public SchemaFileParser(String file, String[] keys, FieldTypes[] types, boolean preProcess) { + this.filename = file; + this.keys = keys.clone(); + this.types = types.clone(); + this.preProcess = preProcess; + } + + private Object getTypedValue(String value, FieldTypes type) { + switch (type) { + case CHAR: + return value.charAt(0); + case INT: + return Integer.valueOf(value); + case STRING: + return value; + case ULONG: + return Long.parseUnsignedLong(value); + case DOUBLE: + return Double.valueOf(value); + default: + return null; + } + } + + private void generateMap(String content, Map map) { + String[] splitvalues = content.trim().split(" +"); + String[] values = preProcess(splitvalues); + if (values.length < types.length) { + LOGGER.debug( + "Content Values tokens {} length is less than types {} length with ExceptionCode: {}", + () -> Arrays.toString(values), + () -> Arrays.toString(types), + () -> StatExceptionCode.SCHEMA_PARSER_ERROR.toString()); + } + int lim = Math.min(values.length, types.length); + for (int idx = 0; idx < lim; idx++) { + map.put(keys[idx], getTypedValue(values[idx], types[idx])); + } + } + + private String[] preProcess(String[] tokens) { + if (preProcess) { + List processedTokens = new ArrayList<>(); + StringBuffer tmp = new StringBuffer(); + boolean beingProcessed = false; + for (int idx = 0; idx < tokens.length; idx++) { + if (beingProcessed) { + tmp.append(tokens[idx]); + if (tokens[idx].endsWith(")")) { + beingProcessed = false; + processedTokens.add(tmp.toString()); + tmp.setLength(0); + } + } else if (tokens[idx].startsWith("(")) { + if (tokens[idx].endsWith(")")) { + processedTokens.add(tokens[idx]); + } else { + beingProcessed = true; + tmp.append(tokens[idx]); + } + } else { + processedTokens.add(tokens[idx]); + } + } + return processedTokens.toArray(new String[processedTokens.size()]); + } else { + return tokens; + } + } + + /* + to be used for parsing the outputs that contains single line + */ + public Map parse() { + Map map = new HashMap<>(); + try (FileReader fileReader = new FileReader(new File(filename)); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { + String line = bufferedReader.readLine(); + if (line == null) { + return map; + } + generateMap(line, map); + } catch (FileNotFoundException e) { + LOGGER.debug("FileNotFound in parse with exception: {}", () -> e.toString()); + } catch (Exception e) { + LOGGER.debug( + "Error in parse with exception: {} with ExceptionCode: {}", + () -> e.toString(), + () -> StatExceptionCode.SCHEMA_PARSER_ERROR.toString()); + } + return map; + } + + /* + to be used for parsing the outputs that contains multiple lines + */ + public List> parseMultiple() { + List> mapList = new ArrayList<>(); + try (FileReader fileReader = new FileReader(new File(filename)); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { + String line; + while ((line = bufferedReader.readLine()) != null) { + Map map = new HashMap<>(); + generateMap(line, map); + mapList.add(map); + } + } catch (FileNotFoundException e) { + LOGGER.debug("FileNotFound in parse with exception: {}", () -> e.toString()); + } catch (Exception e) { + LOGGER.debug( + "Error in parseMultiple with exception: {} with ExceptionCode: {}", + () -> e.toString(), + () -> StatExceptionCode.SCHEMA_PARSER_ERROR.toString()); + } + return mapList; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPU.java b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPU.java new file mode 100644 index 0000000..d5ddefc --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPU.java @@ -0,0 +1,222 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxCPUPagingActivityGenerator; + +public final class ThreadCPU { + private static final Logger LOGGER = LogManager.getLogger(ThreadCPU.class); + public static final ThreadCPU INSTANCE = new ThreadCPU(); + private long scClkTck = 0; + private String pid = null; + private List tids = null; + private Map> tidKVMap = new HashMap<>(); + private Map> oldtidKVMap = new HashMap<>(); + private long kvTimestamp = 0; + private long oldkvTimestamp = 0; + private LinuxCPUPagingActivityGenerator cpuPagingActivityMap = + new LinuxCPUPagingActivityGenerator(); + + // these two arrays map 1-1 + private static String[] statKeys = { + "pid", + "comm", + "state", + "ppid", + "pgrp", + "session", + "ttynr", + "tpgid", + "flags", + "minflt", + "cminflt", + "majflt", + "cmajflt", + "utime", + "stime", + "cutime", + "cstime", + "prio", + "nice", + "nthreads", + "itrealvalue", + "starttime", + "vsize", + "rss", + "rsslim", + "startcode", + "endcode", + "startstack", + "kstkesp", + "kstkeip", + "signal", + "blocked", + "sigignore", + "sigcatch", + "wchan", + "nswap", + "cnswap", + "exitsig", + "cpu", + "rtprio", + "schedpolicy", + "bio_ticks", + "vmtime", + "cvmtime" + // more that we ignore + }; + + private static SchemaFileParser.FieldTypes[] statTypes = { + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.STRING, + SchemaFileParser.FieldTypes.CHAR, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.ULONG, // 10 + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, // 20 + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, // 30 + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, // 40 + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT, + SchemaFileParser.FieldTypes.INT + }; + + private ThreadCPU() { + try { + pid = OSGlobals.getPid(); + scClkTck = OSGlobals.getScClkTck(); + tids = OSGlobals.getTids(); + } catch (Exception e) { + LOGGER.error( + (Supplier) + () -> + new ParameterizedMessage( + "Error In Initializing ThreadCPU: {}", e.toString()), + e); + } + } + + public synchronized void addSample() { + tids = OSGlobals.getTids(); + + oldtidKVMap.clear(); + oldtidKVMap.putAll(tidKVMap); + + tidKVMap.clear(); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + for (String tid : tids) { + Map sample = + // (new SchemaFileParser("/proc/"+tid+"/stat", + (new SchemaFileParser( + "/proc/" + pid + "/task/" + tid + "/stat", + statKeys, + statTypes, + true)) + .parse(); + tidKVMap.put(tid, sample); + } + + calculateCPUDetails(); + calculatePagingActivity(); + } + + private void calculateCPUDetails() { + if (oldkvTimestamp == kvTimestamp) { + return; + } + + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + Map oldv = oldtidKVMap.get(entry.getKey()); + if (v != null && oldv != null) { + if (!v.containsKey("utime") || !oldv.containsKey("utime")) { + continue; + } + long diff = + ((long) (v.getOrDefault("utime", 0L)) + - (long) (oldv.getOrDefault("utime", 0L))) + + ((long) (v.getOrDefault("stime", 0L)) + - (long) (oldv.getOrDefault("stime", 0L))); + double util = (1.0e3 * diff / scClkTck) / (kvTimestamp - oldkvTimestamp); + cpuPagingActivityMap.setCPUUtilization(entry.getKey(), util); + } + } + } + + /** Note: major faults include mmap()'ed accesses */ + private void calculatePagingActivity() { + if (oldkvTimestamp == kvTimestamp) { + return; + } + + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + Map oldv = oldtidKVMap.get(entry.getKey()); + if (v != null && oldv != null) { + if (!v.containsKey("majflt") || !oldv.containsKey("majflt")) { + continue; + } + double majdiff = + ((long) (v.getOrDefault("majflt", 0L)) + - (long) (oldv.getOrDefault("majflt", 0L))); + majdiff /= 1.0e-3 * (kvTimestamp - oldkvTimestamp); + double mindiff = + ((long) (v.getOrDefault("minflt", 0L)) + - (long) (oldv.getOrDefault("minflt", 0L))); + mindiff /= 1.0e-3 * (kvTimestamp - oldkvTimestamp); + + Double[] fltarr = {majdiff, mindiff, (double) ((long) v.getOrDefault("rss", 0L))}; + cpuPagingActivityMap.setPagingActivities(entry.getKey(), fltarr); + } + } + } + + public LinuxCPUPagingActivityGenerator getCPUPagingActivity() { + + return cpuPagingActivityMap; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIO.java b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIO.java new file mode 100644 index 0000000..df254b0 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIO.java @@ -0,0 +1,168 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxDiskIOMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; + +public class ThreadDiskIO { + private static String pid = OSGlobals.getPid(); + private static List tids = null; + private static final Logger LOGGER = LogManager.getLogger(ThreadDiskIO.class); + + private static Map> tidKVMap = new HashMap<>(); + private static Map> oldtidKVMap = new HashMap<>(); + private static long kvTimestamp = 0; + private static long oldkvTimestamp = 0; + + public static class IOMetrics { + public double avgReadThroughputBps; + public double avgWriteThroughputBps; + public double avgTotalThroughputBps; + + public double avgReadSyscallRate; + public double avgWriteSyscallRate; + public double avgTotalSyscallRate; + + public double avgPageCacheReadThroughputBps; + public double avgPageCacheWriteThroughputBps; + public double avgPageCacheTotalThroughputBps; + + @SuppressWarnings("checkstyle:parameternumber") + IOMetrics( + double avgReadThroughputBps, + double avgReadSyscallRate, + double avgWriteThroughputBps, + double avgWriteSyscallRate, + double avgTotalThroughputBps, + double avgTotalSyscallRate, + double avgPageCacheReadThroughputBps, + double avgPageCacheWriteThroughputBps, + double avgPageCacheTotalThroughputBps) { + this.avgReadThroughputBps = avgReadThroughputBps; + this.avgWriteThroughputBps = avgWriteThroughputBps; + this.avgTotalThroughputBps = avgTotalThroughputBps; + this.avgReadSyscallRate = avgReadSyscallRate; + this.avgWriteSyscallRate = avgWriteSyscallRate; + this.avgTotalSyscallRate = avgTotalSyscallRate; + this.avgPageCacheReadThroughputBps = avgPageCacheReadThroughputBps; + this.avgPageCacheWriteThroughputBps = avgPageCacheWriteThroughputBps; + this.avgPageCacheTotalThroughputBps = avgPageCacheTotalThroughputBps; + } + + public String toString() { + return new StringBuilder() + .append("rBps:") + .append(avgReadThroughputBps) + .append(" wBps:") + .append(avgWriteThroughputBps) + .append(" totBps:") + .append(avgTotalThroughputBps) + .append(" rSysc:") + .append(avgReadSyscallRate) + .append(" wSysc:") + .append(avgWriteSyscallRate) + .append(" totSysc:") + .append(avgTotalSyscallRate) + .append(" rPcBps:") + .append(avgPageCacheReadThroughputBps) + .append(" wPcBps:") + .append(avgPageCacheWriteThroughputBps) + .append(" totPcBps:") + .append(avgPageCacheTotalThroughputBps) + .toString(); + } + } + + private static void addSampleTid(String tid) { + try (FileReader fileReader = + new FileReader(new File("/proc/" + pid + "/task/" + tid + "/io")); + BufferedReader bufferedReader = new BufferedReader(fileReader); ) { + String line = null; + Map kvmap = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null) { + String[] toks = line.split("[: ]+"); + String key = toks[0]; + long val = Long.parseLong(toks[1]); + kvmap.put(key, val); + } + tidKVMap.put(tid, kvmap); + } catch (FileNotFoundException e) { + LOGGER.debug("FileNotFound in parse with exception: {}", () -> e.toString()); + } catch (Exception e) { + LOGGER.debug( + "Error In addSample Tid for: {} with error: {} with ExceptionCode: {}", + () -> tid, + () -> e.toString(), + () -> StatExceptionCode.THREAD_IO_ERROR.toString()); + } + } + + public static synchronized void addSample() { + tids = OSGlobals.getTids(); + oldtidKVMap.clear(); + oldtidKVMap.putAll(tidKVMap); + + tidKVMap.clear(); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + for (String tid : tids) { + addSampleTid(tid); + } + } + + public static synchronized LinuxDiskIOMetricsGenerator getIOUtilization() { + + LinuxDiskIOMetricsGenerator linuxDiskIOMetricsHandler = new LinuxDiskIOMetricsGenerator(); + if (oldkvTimestamp == kvTimestamp) { + return linuxDiskIOMetricsHandler; + } + + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + Map oldv = oldtidKVMap.get(entry.getKey()); + if (v != null && oldv != null) { + double duration = 1.0e-3 * (kvTimestamp - oldkvTimestamp); + double readBytes = v.get("read_bytes") - oldv.get("read_bytes"); + double writeBytes = v.get("write_bytes") - oldv.get("write_bytes"); + double readSyscalls = v.get("syscr") - oldv.get("syscr"); + double writeSyscalls = v.get("syscw") - oldv.get("syscw"); + double readPcBytes = v.get("rchar") - oldv.get("rchar") - readBytes; + double writePcBytes = v.get("wchar") - oldv.get("wchar") - writeBytes; + readBytes /= duration; + readSyscalls /= duration; + writeBytes /= duration; + writeSyscalls /= duration; + readPcBytes /= duration; + writePcBytes /= duration; + + linuxDiskIOMetricsHandler.setDiskIOMetrics( + entry.getKey(), + new IOMetrics( + readBytes, + readSyscalls, + writeBytes, + writeSyscalls, + readBytes + writeBytes, + readSyscalls + writeSyscalls, + readPcBytes, + writePcBytes, + readPcBytes + writePcBytes)); + } + } + return linuxDiskIOMetricsHandler; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadSched.java b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadSched.java new file mode 100644 index 0000000..55af97c --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadSched.java @@ -0,0 +1,143 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.util.Supplier; +import org.opensearch.performanceanalyzer.commons.metrics_generator.SchedMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxSchedMetricsGenerator; + +public final class ThreadSched { + private static final Logger LOGGER = LogManager.getLogger(ThreadSched.class); + public static final ThreadSched INSTANCE = new ThreadSched(); + private String pid = null; + private List tids = null; + private Map> tidKVMap = new HashMap<>(); + private Map> oldtidKVMap = new HashMap<>(); + private long kvTimestamp = 0; + private long oldkvTimestamp = 0; + + public static class SchedMetrics { + public final double avgRuntime; + public final double avgWaittime; + public final double contextSwitchRate; // both voluntary and involuntary + + SchedMetrics(double avgRuntime, double avgWaittime, double contextSwitchRate) { + this.avgRuntime = avgRuntime; + this.avgWaittime = avgWaittime; + this.contextSwitchRate = contextSwitchRate; + } + + @Override + public String toString() { + return new StringBuilder() + .append("avgruntime: ") + .append(avgRuntime) + .append(" avgwaittime: ") + .append(avgWaittime) + .append(" ctxrate: ") + .append(contextSwitchRate) + .toString(); + } + } + + private LinuxSchedMetricsGenerator schedLatencyMap = new LinuxSchedMetricsGenerator(); + + private static String[] schedKeys = {"runticks", "waitticks", "totctxsws"}; + + private static SchemaFileParser.FieldTypes[] schedTypes = { + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG, + SchemaFileParser.FieldTypes.ULONG + }; + + private ThreadSched() { + try { + pid = OSGlobals.getPid(); + tids = OSGlobals.getTids(); + } catch (Exception e) { + LOGGER.error( + (Supplier) + () -> + new ParameterizedMessage( + "Error In Initializing ThreadCPU: {}", e.toString()), + e); + } + } + + public synchronized void addSample() { + tids = OSGlobals.getTids(); + + oldtidKVMap.clear(); + oldtidKVMap.putAll(tidKVMap); + + tidKVMap.clear(); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + for (String tid : tids) { + Map sample = + (new SchemaFileParser( + "/proc/" + pid + "/task/" + tid + "/schedstat", + schedKeys, + schedTypes)) + .parse(); + tidKVMap.put(tid, sample); + } + + calculateSchedLatency(); + } + + private void calculateSchedLatency() { + if (oldkvTimestamp == kvTimestamp) { + return; + } + + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + Map oldv = oldtidKVMap.get(entry.getKey()); + if (v != null && oldv != null) { + if (!v.containsKey("totctxsws") || !oldv.containsKey("totctxsws")) { + continue; + } + long ctxdiff = + (long) v.getOrDefault("totctxsws", 0L) + - (long) oldv.getOrDefault("totctxsws", 0L); + double avgRuntime = + 1.0e-9 + * ((long) v.getOrDefault("runticks", 0L) + - (long) oldv.getOrDefault("runticks", 0L)); + double avgWaittime = + 1.0e-9 + * ((long) v.getOrDefault("waitticks", 0L) + - (long) oldv.getOrDefault("waitticks", 0L)); + if (ctxdiff == 0) { + avgRuntime = 0; + avgWaittime = 0; + } else { + avgRuntime /= 1.0 * ctxdiff; + avgWaittime /= 1.0 * ctxdiff; + } + double contextSwitchRate = ctxdiff; + contextSwitchRate /= 1.0e-3 * (kvTimestamp - oldkvTimestamp); + + schedLatencyMap.setSchedMetric( + entry.getKey(), + new SchedMetrics(avgRuntime, avgWaittime, contextSwitchRate)); + } + } + } + + public synchronized SchedMetricsGenerator getSchedLatency() { + + return schedLatencyMap; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonConverter.java b/src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonConverter.java new file mode 100644 index 0000000..7626374 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonConverter.java @@ -0,0 +1,123 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.util; + + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.performanceanalyzer.commons.collectors.StatsCollector; +import org.opensearch.performanceanalyzer.commons.stats.metrics.StatExceptionCode; + +public class JsonConverter { + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private static final Logger LOG = LogManager.getLogger(JsonConverter.class); + + /** + * We can miss writing a metric if exception is thrown. + * + * @param value a Java object + * @return the converted string from the input Java object + */ + public static String writeValueAsString(Object value) { + try { + return MAPPER.writeValueAsString(value); + } catch (JsonGenerationException e) { + LOG.warn("Json generation error " + e.getMessage()); + throw new IllegalArgumentException(e); + } catch (JsonMappingException e) { + LOG.warn("Json Mapping Error: " + e.getMessage()); + throw new IllegalArgumentException(e); + } catch (IOException e) { + LOG.warn("IO error: " + e.getMessage()); + throw new IllegalArgumentException(e); + } + } + + public static Map createMapFrom(String json) { + + try { + if (json.trim().length() != 0) { + return MAPPER.readValue(json, new TypeReference>() {}); + } + } catch (IOException e) { + LOG.debug( + "IO error: {} for json {} with ExceptionCode: {}", + () -> e.toString(), + () -> json, + () -> StatExceptionCode.JSON_PARSER_ERROR.toString()); + StatsCollector.instance().logException(StatExceptionCode.JSON_PARSER_ERROR); + } + return Collections.emptyMap(); + } + + /** + * Search a Jackson JsonNode inside a JSON string matching the input path expression + * + * @param jsonString an encoded JSON string + * @param paths path fragments + * @return the matching Jackson JsonNode or null in case of no match. + * @throws IOException if underlying input contains invalid content of type JsonParser supports + * @throws JsonProcessingException if underlying input contains invalid content of type + * JsonParser supports + * @throws IOException if underlying input contains invalid content of type JsonParser supports + */ + public static JsonNode getChildNode(String jsonString, String... paths) + throws JsonProcessingException, IOException { + JsonNode rootNode = MAPPER.readTree(jsonString); + return getChildNode(rootNode, paths); + } + + /** + * Search a Jackson JsonNode inside a Jackson JsonNode matching the input path expression + * + * @param jsonNode a Jackson JsonNode + * @param paths path fragments + * @return the matching Jackson JsonNode or null in case of no match. + */ + public static JsonNode getChildNode(JsonNode jsonNode, String... paths) { + for (int i = 0; i < paths.length; i++) { + String path = paths[i]; + if (!jsonNode.has(path)) { + return null; + } + + jsonNode = jsonNode.get(path); + } + + return jsonNode; + } + + /** + * Search a long number inside a JSON string matching the input path expression + * + * @param jsonString an encoded JSON string + * @param paths path fragments + * @return the matching long number or null in case of no match. + * @throws JsonPathNotFoundException thrown if the input path is invalid + * @throws IOException thrown if underlying input contains invalid content of type JsonParser + * supports + * @throws JsonProcessingException thrown if underlying input contains invalid content of type + * JsonParser supports + */ + public static long getLongValue(String jsonString, String... paths) + throws JsonPathNotFoundException, JsonProcessingException, IOException { + JsonNode jsonNode = getChildNode(jsonString, paths); + if (jsonNode != null) { + return jsonNode.longValue(); + } + throw new JsonPathNotFoundException(); + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonPathNotFoundException.java b/src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonPathNotFoundException.java new file mode 100644 index 0000000..3075618 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/util/JsonPathNotFoundException.java @@ -0,0 +1,10 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.util; + +public class JsonPathNotFoundException extends Exception { + public JsonPathNotFoundException() {} +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/CommonsTestHelper.java b/src/test/java/org/opensearch/performanceanalyzer/commons/CommonsTestHelper.java new file mode 100644 index 0000000..25d2e30 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/CommonsTestHelper.java @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons; + + +import org.opensearch.performanceanalyzer.commons.stats.CommonStats; +import org.opensearch.performanceanalyzer.commons.stats.measurements.MeasurementSet; + +public class CommonsTestHelper { + public static boolean verify(MeasurementSet measurementSet) throws InterruptedException { + final int MAX_TIME_TO_WAIT_MILLIS = 10_000; + int waited_for_millis = 0; + while (waited_for_millis++ < MAX_TIME_TO_WAIT_MILLIS) { + if (CommonStats.RCA_STATS_REPORTER.isMeasurementCollected(measurementSet)) { + return true; + } + Thread.sleep(1); + } + return false; + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/AbstractCollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/AbstractCollectorTest.java new file mode 100644 index 0000000..a98645b --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/AbstractCollectorTest.java @@ -0,0 +1,44 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Instant; +import org.junit.Test; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; + +/** A base class that others can extend to validate their Metrics Collectors behavior */ +public abstract class AbstractCollectorTest { + protected static final ObjectMapper mapper = new ObjectMapper(); + private PerformanceAnalyzerMetricsCollector uut; + + // Implementors should call this in a setup() function to set their collector + public void setUut(PerformanceAnalyzerMetricsCollector collector) { + this.uut = collector; + } + + // This is the only line that implementors need to modify + public abstract void validateMetric(String metric) throws Exception; + + @Test + public void validateMetrics() throws Exception { + uut.collectMetrics(Instant.now().toEpochMilli()); + String metricString = uut.getValue().toString(); + // chop off current time json + int end = metricString.indexOf(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + metricString = metricString.substring(end + 1); + while (!metricString.isEmpty()) { + end = metricString.indexOf(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + if (end == -1) { + break; + } + String metric = metricString.substring(0, end); + validateMetric(metric); + metricString = metricString.substring(end + 1); + } + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollectorTest.java new file mode 100644 index 0000000..e59e3a3 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/DisksCollectorTest.java @@ -0,0 +1,29 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.junit.Assert; +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class DisksCollectorTest extends AbstractCollectorTest { + @Before + public void setup() { + int interval = MetricsConfiguration.CONFIG_MAP.get(DisksCollector.class).samplingInterval; + setUut(new DisksCollector("DiskCollector", interval)); + } + + @Override + public void validateMetric(String metric) throws Exception { + DiskMetrics diskMetrics = mapper.readValue(metric, DiskMetrics.class); + // TODO implement further validation of the MetricStatus + Assert.assertFalse(diskMetrics.getName().isEmpty()); + Assert.assertTrue(diskMetrics.getUtilization() >= 0 && diskMetrics.getUtilization() <= 1); + Assert.assertTrue(diskMetrics.getAwait() >= 0); + Assert.assertTrue(diskMetrics.getServiceRate() >= 0); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollectorTest.java new file mode 100644 index 0000000..0e11f8e --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/GCInfoCollectorTest.java @@ -0,0 +1,27 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.junit.Assert; +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class GCInfoCollectorTest extends AbstractCollectorTest { + @Before + public void setup() { + int interval = MetricsConfiguration.CONFIG_MAP.get(GCInfoCollector.class).samplingInterval; + setUut(new GCInfoCollector("GCInfoCollector", interval)); + } + + @Override + public void validateMetric(String metric) throws Exception { + GCInfoCollector.GCInfo info = mapper.readValue(metric, GCInfoCollector.GCInfo.class); + // TODO implement further validation of the MetricStatus + Assert.assertFalse(info.getCollectorName().isEmpty()); + Assert.assertFalse(info.getMemoryPool().isEmpty()); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorGCTypesTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorGCTypesTest.java new file mode 100644 index 0000000..5e3c8b6 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorGCTypesTest.java @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableSet; +import java.time.Instant; +import java.util.HashSet; +import java.util.Set; +import org.junit.Assert; +import org.junit.Test; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; +import org.opensearch.performanceanalyzer.commons.metrics.PerformanceAnalyzerMetrics; + +public class HeapMetricsCollectorGCTypesTest { + private static final ObjectMapper mapper = new ObjectMapper(); + private static final HeapMetricsCollector uut = + new HeapMetricsCollector( + "HeapMetricsCollector", + MetricsConfiguration.CONFIG_MAP.get(HeapMetricsCollector.class) + .samplingInterval); + private static final Set gcTypes = + ImmutableSet.of( + AllMetrics.GCType.HEAP.toString(), + AllMetrics.GCType.NON_HEAP.toString(), + AllMetrics.GCType.PERM_GEN.toString(), + AllMetrics.GCType.OLD_GEN.toString(), + AllMetrics.GCType.TOT_YOUNG_GC.toString(), + AllMetrics.GCType.TOT_FULL_GC.toString(), + AllMetrics.GCType.EDEN.toString(), + AllMetrics.GCType.SURVIVOR.toString()); + + @Test + public void validateCollectedGCTypes() throws Exception { + Set collectedGCTypes = new HashSet<>(); + + uut.collectMetrics(Instant.now().toEpochMilli()); + String metricString = uut.getValue().toString(); + int end = metricString.indexOf(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + metricString = metricString.substring(end + 1); + + while (!metricString.isEmpty()) { + end = metricString.indexOf(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor); + String metric = metricString.substring(0, end); + HeapMetricsCollector.HeapStatus heapStatus = + mapper.readValue(metric, HeapMetricsCollector.HeapStatus.class); + collectedGCTypes.add(heapStatus.getType()); + metricString = metricString.substring(end + 1); + } + + Assert.assertEquals(collectedGCTypes, gcTypes); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorTest.java new file mode 100644 index 0000000..162b362 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/HeapMetricsCollectorTest.java @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.junit.Assert; +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class HeapMetricsCollectorTest extends AbstractCollectorTest { + @Before + public void setup() { + int interval = + MetricsConfiguration.CONFIG_MAP.get(HeapMetricsCollector.class).samplingInterval; + setUut(new HeapMetricsCollector("HeapMetricsCollector", interval)); + } + + @Override + public void validateMetric(String metric) throws Exception { + HeapMetricsCollector.HeapStatus heapStatus = + mapper.readValue(metric, HeapMetricsCollector.HeapStatus.class); + // TODO implement further validation of the MetricStatus + Assert.assertFalse(heapStatus.getType().isEmpty()); + long collectionCount = heapStatus.getCollectionCount(); + Assert.assertTrue( + collectionCount >= 0 + || collectionCount == HeapMetricsCollector.HeapStatus.UNDEFINED); + long collectionTime = heapStatus.getCollectionTime(); + Assert.assertTrue( + collectionTime >= 0 || collectionTime == HeapMetricsCollector.HeapStatus.UNDEFINED); + long committed = heapStatus.getCommitted(); + Assert.assertTrue(committed >= 0 || committed == HeapMetricsCollector.HeapStatus.UNDEFINED); + long init = heapStatus.getInit(); + Assert.assertTrue(init >= 0 || init == HeapMetricsCollector.HeapStatus.UNDEFINED); + long max = heapStatus.getMax(); + // TODO max can end up being -1, is this intended? + Assert.assertTrue( + max >= 0 || max == HeapMetricsCollector.HeapStatus.UNDEFINED || max == -1); + long used = heapStatus.getUsed(); + Assert.assertTrue(used >= 0 || used == HeapMetricsCollector.HeapStatus.UNDEFINED); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollectorTest.java new file mode 100644 index 0000000..a12cfee --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/MountedPartitionMetricsCollectorTest.java @@ -0,0 +1,36 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.junit.Assert; +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class MountedPartitionMetricsCollectorTest extends AbstractCollectorTest { + @Before + public void setup() { + int interval = + MetricsConfiguration.CONFIG_MAP.get(MountedPartitionMetricsCollector.class) + .samplingInterval; + setUut(new MountedPartitionMetricsCollector("MountedPartitionMetricsCollector", interval)); + } + + @Override + public void validateMetric(String metric) throws Exception { + MountedPartitionMetrics partitionMetrics = + mapper.readValue(metric, MountedPartitionMetrics.class); + // TODO implement further validation of the MetricStatus + Assert.assertFalse(partitionMetrics.getMountPoint().isEmpty()); + Assert.assertFalse(partitionMetrics.getDevicePartition().isEmpty()); + long totalSpace = partitionMetrics.getTotalSpace(); + Assert.assertTrue(totalSpace >= 0 || totalSpace == -1); + long freeSpace = partitionMetrics.getFreeSpace(); + Assert.assertTrue(freeSpace >= 0 || freeSpace == -1); + long usableFreeSpace = partitionMetrics.getUsableFreeSpace(); + Assert.assertTrue(usableFreeSpace >= 0 || usableFreeSpace == -1); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollectorTest.java new file mode 100644 index 0000000..b5520c8 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkE2ECollectorTest.java @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Assert; +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class NetworkE2ECollectorTest extends AbstractCollectorTest { + private static final Logger LOG = LogManager.getLogger(NetworkE2ECollectorTest.class); + + @Before + public void setup() { + int interval = + MetricsConfiguration.CONFIG_MAP.get(NetworkE2ECollector.class).samplingInterval; + setUut(new NetworkE2ECollector("NetworkE2ECollector", interval)); + } + + @Override + public void validateMetric(String metric) throws Exception { + TCPStatus tcpStatus = mapper.readValue(metric, TCPStatus.class); + Assert.assertFalse(tcpStatus.getDest().isEmpty()); + // TODO implement further validation of the MetricStatus + int numFlows = tcpStatus.getNumFlows(); + double txQ = tcpStatus.getTxQ(); + double rxQ = tcpStatus.getRxQ(); + double curLost = tcpStatus.getCurLost(); + double sndCWND = tcpStatus.getSndCWND(); + double ssThresh = tcpStatus.getSsThresh(); + LOG.info( + "numFlows {}, txQ {}, rxQ {}, curLost {}, sendCWND {}, ssThresh {}", + numFlows, + txQ, + rxQ, + curLost, + sndCWND, + ssThresh); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollectorTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollectorTest.java new file mode 100644 index 0000000..63f9db1 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/collectors/NetworkInterfaceCollectorTest.java @@ -0,0 +1,32 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.metrics.MetricsConfiguration; + +public class NetworkInterfaceCollectorTest extends AbstractCollectorTest { + @Before + public void setup() { + int interval = + MetricsConfiguration.CONFIG_MAP.get(NetworkInterfaceCollector.class) + .samplingInterval; + setUut(new NetworkInterfaceCollector("NetworkInterfaceCollector", interval)); + } + + @Override + public void validateMetric(String metric) throws Exception { + NetInterfaceSummary interfaceSummary = mapper.readValue(metric, NetInterfaceSummary.class); + // TODO implement further validation of the MetricStatus + NetInterfaceSummary.Direction direction = interfaceSummary.getDirection(); + double packetRate4 = interfaceSummary.getPacketRate4(); + double dropRate4 = interfaceSummary.getDropRate4(); + double packetRate6 = interfaceSummary.getPacketRate6(); + double dropRate6 = interfaceSummary.getPacketRate6(); + double bps = interfaceSummary.getBps(); + } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetricsTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetricsTests.java new file mode 100644 index 0000000..ec90af8 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/GCMetricsTests.java @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import org.junit.Test; + +public class GCMetricsTests { + public static void main(String[] args) throws Exception { + runOnce(); + } + + private static void runOnce() { + GCMetrics.runGCMetrics(); + GCMetrics.printGCMetrics(); + } + + // - to enhance + @Test + public void testMetrics() {} +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetricsTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetricsTests.java new file mode 100644 index 0000000..a35cc52 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/HeapMetricsTests.java @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import java.lang.management.MemoryUsage; +import java.util.Map; +import java.util.function.Supplier; +import org.junit.Test; + +public class HeapMetricsTests { + public static void main(String[] args) throws Exception { + runOnce(); + } + + private static void runOnce() { + for (Map.Entry> entry : + HeapMetrics.getMemoryUsageSuppliers().entrySet()) { + MemoryUsage memoryUsage = entry.getValue().get(); + System.out.println(entry.getKey() + "_committed:" + memoryUsage.getCommitted()); + System.out.println(entry.getKey() + "_init" + memoryUsage.getInit()); + System.out.println(entry.getKey() + "_max" + memoryUsage.getMax()); + System.out.println(entry.getKey() + "_used" + memoryUsage.getUsed()); + } + } + + // - to enhance + @Test + public void testMetrics() {} +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTest.java b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTest.java new file mode 100644 index 0000000..b11ff9b --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTest.java @@ -0,0 +1,39 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import org.junit.Before; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; + +// This test only runs in linux systems as some of the static members of the ThreadList +// class are specific to Linux. +public class ThreadListTest { + @Before + public void before() { + org.junit.Assume.assumeNotNull(OSMetricsGeneratorFactory.getInstance()); + } + + // @Test + // public void testNullThreadInfo() throws InterruptedException { + // CommonStats.ERRORS_AND_EXCEPTIONS_AGGREGATOR = + // new SampleAggregator(StatExceptionCode.values()); + // String propertyName = "clk.tck"; + // String old_clk_tck = System.getProperty(propertyName); + // System.setProperty(propertyName, "100"); + // ThreadInfo[] infos = ThreadList.getAllThreadInfos(); + // // Artificially injecting a null to simulate that the thread id does not exist + // // any more and therefore the corresponding threadInfo is null. + // infos[0] = null; + // + // ThreadList.parseAllThreadInfos(infos); + // Assert.assertTrue( + // CommonsTestHelper.verify(StatExceptionCode.JVM_THREAD_ID_NO_LONGER_EXISTS)); + // if (old_clk_tck != null) { + // System.setProperty(propertyName, old_clk_tck); + // } + // } +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTests.java new file mode 100644 index 0000000..f241661 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadListTests.java @@ -0,0 +1,57 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.jvm; + + +import org.junit.Test; +import org.opensearch.performanceanalyzer.commons.hwnet.NetworkInterface; +import org.opensearch.performanceanalyzer.commons.os.OSGlobals; + +public class ThreadListTests { + // XXX: standalone test code + public static class HelloRunnable implements Runnable { + @Override + public void run() { + Thread.currentThread().setName("duMMy-thread"); + long i = 0; + while (true) { + synchronized (HelloRunnable.class) { + String.valueOf(i++); + } + } + } + } + + public static void main(String[] args) throws Exception { + // Configurator.setAllLevels(LogManager.getRootLogger().getName(), Level.DEBUG); + (new Thread(new HelloRunnable())).start(); + (new Thread(new HelloRunnable())).start(); + runOnce(); + } + + private static void runOnce() throws InterruptedException { + String params[] = new String[0]; + while (true) { + ThreadList.runThreadDump(OSGlobals.getPid(), params); + ThreadList.LOGGER.info(ThreadList.getNativeTidMap(false).values()); + + /*GCMetrics.runOnce(); + HeapMetrics.runOnce(); + ThreadCPU.runOnce(); + ThreadDiskIO.runOnce(); + ThreadSched.runOnce(); + NetworkE2E.runOnce(); + Disks.runOnce();*/ + NetworkInterface.runOnce(); + + Thread.sleep(ThreadList.samplingInterval); + } + } + + // - to enhance + @Test + public void testMetrics() {} +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java new file mode 100644 index 0000000..2cbe87b --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPUTests.java @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import org.junit.Test; + +public class ThreadCPUTests { + public static void main(String[] args) throws Exception { + runOnce(); + } + + private static void runOnce() { + ThreadCPU.INSTANCE.addSample(); + System.out.println( + "cpumap and pagemap:" + ThreadCPU.INSTANCE.getCPUPagingActivity().toString()); + } + + // - to enhance + @Test + public void testMetrics() {} +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIOTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIOTests.java new file mode 100644 index 0000000..f216cd0 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIOTests.java @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import org.junit.Test; + +public class ThreadDiskIOTests { + public static void main(String[] args) throws Exception { + runOnce(); + } + + public static void runOnce() { + ThreadDiskIO.addSample(); + System.out.println(ThreadDiskIO.getIOUtilization().toString()); + } + + // - to enhance + @Test + public void testMetrics() {} +} diff --git a/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java new file mode 100644 index 0000000..4373082 --- /dev/null +++ b/src/test/java/org/opensearch/performanceanalyzer/commons/os/ThreadSchedTests.java @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + + +import org.junit.Test; + +public class ThreadSchedTests { + public static void main(String[] args) throws Exception { + runOnce(); + } + + public static void runOnce() { + ThreadSched.INSTANCE.addSample(); + System.out.println(ThreadSched.INSTANCE.getSchedLatency().toString()); + } + + // - to enhance + @Test + public void testMetrics() {} +}