diff --git a/build.gradle b/build.gradle index 16f0eef..8367f29 100644 --- a/build.gradle +++ b/build.gradle @@ -95,7 +95,6 @@ dependencies { implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: "${log4jVersion}" implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: "${log4jVersion}" - testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.0' testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.0' testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.23.0' @@ -124,6 +123,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/OSMetricsCollector.java b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/OSMetricsCollector.java new file mode 100644 index 0000000..318e404 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/collectors/OSMetricsCollector.java @@ -0,0 +1,164 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.collectors; + + +import java.util.Map; +import org.opensearch.performanceanalyzer.commons.OSMetricsGeneratorFactory; +import org.opensearch.performanceanalyzer.commons.jvm.ThreadList; +import org.opensearch.performanceanalyzer.commons.metrics.AllMetrics.OSMetrics; +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.CPUPagingActivityGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.DiskIOMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.SchedMetricsGenerator; + +public class OSMetricsCollector extends PerformanceAnalyzerMetricsCollector + implements MetricsProcessor { + public static final int SAMPLING_TIME_INTERVAL = + MetricsConfiguration.CONFIG_MAP.get(ThreadList.class).samplingInterval; + private static final int KEYS_PATH_LENGTH = 1; + private StringBuilder value; + private OSMetricsGenerator osMetricsGenerator; + + public enum MetaDataFields { + threadName + } + + public OSMetricsCollector() { + super(SAMPLING_TIME_INTERVAL, "OSMetrics"); + value = new StringBuilder(); + osMetricsGenerator = OSMetricsGeneratorFactory.getInstance(); + } + + @Override + public void collectMetrics(long startTime) { + CPUPagingActivityGenerator threadCPUPagingActivityGenerator = + osMetricsGenerator.getPagingActivityGenerator(); + threadCPUPagingActivityGenerator.addSample(); + + SchedMetricsGenerator schedMetricsGenerator = osMetricsGenerator.getSchedMetricsGenerator(); + schedMetricsGenerator.addSample(); + + Map threadStates = + ThreadList.getNativeTidMap(getThreadContentionMonitoringEnabled()); + + DiskIOMetricsGenerator diskIOMetricsGenerator = + osMetricsGenerator.getDiskIOMetricsGenerator(); + diskIOMetricsGenerator.addSample(); + + for (String threadId : osMetricsGenerator.getAllThreadIds()) { + value.setLength(0); + value.append(PerformanceAnalyzerMetrics.getCurrentTimeMetric()) + .append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.CPU_UTILIZATION) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadCPUPagingActivityGenerator.getCPUUtilization(threadId)); + + if (threadCPUPagingActivityGenerator.hasPagingActivity(threadId)) { + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.PAGING_MAJ_FLT_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadCPUPagingActivityGenerator.getMajorFault(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.PAGING_MIN_FLT_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadCPUPagingActivityGenerator.getMinorFault(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.PAGING_RSS) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadCPUPagingActivityGenerator.getResidentSetSize(threadId)); + } + + if (schedMetricsGenerator.hasSchedMetrics(threadId)) { + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.SCHED_RUNTIME) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(schedMetricsGenerator.getAvgRuntime(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.SCHED_WAITTIME) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(schedMetricsGenerator.getAvgWaittime(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.SCHED_CTX_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(schedMetricsGenerator.getContextSwitchRate(threadId)); + } + + ThreadList.ThreadState threadState = threadStates.get(Long.valueOf(threadId)); + if (threadState != null) { + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.HEAP_ALLOC_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadState.heapAllocRate); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(MetaDataFields.threadName.toString()) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadState.threadName); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.THREAD_BLOCKED_TIME) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadState.avgBlockedTime); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.THREAD_BLOCKED_EVENT) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadState.blockedCount); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.THREAD_WAITED_TIME) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadState.avgWaitedTime); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.THREAD_WAITED_EVENT) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(threadState.waitedCount); + } + + if (diskIOMetricsGenerator.hasDiskIOMetrics(threadId)) { + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.IO_READ_THROUGHPUT) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(diskIOMetricsGenerator.getAvgReadThroughputBps(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.IO_WRITE_THROUGHPUT) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(diskIOMetricsGenerator.getAvgWriteThroughputBps(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.IO_TOT_THROUGHPUT) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(diskIOMetricsGenerator.getAvgTotalThroughputBps(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.IO_READ_SYSCALL_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(diskIOMetricsGenerator.getAvgReadSyscallRate(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.IO_WRITE_SYSCALL_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(diskIOMetricsGenerator.getAvgWriteSyscallRate(threadId)); + value.append(PerformanceAnalyzerMetrics.sMetricNewLineDelimitor) + .append(OSMetrics.IO_TOTAL_SYSCALL_RATE) + .append(PerformanceAnalyzerMetrics.sKeyValueDelimitor) + .append(diskIOMetricsGenerator.getAvgTotalSyscallRate(threadId)); + } + + saveMetricValues(value.toString(), startTime, threadId); + } + } + + @Override + public String getMetricsPath(long startTime, String... keysPath) { + // throw exception if keys.length is not equal to 1...which is thread ID + if (keysPath.length != KEYS_PATH_LENGTH) { + throw new RuntimeException("keys length should be " + KEYS_PATH_LENGTH); + } + return PerformanceAnalyzerMetrics.generatePath( + startTime, + PerformanceAnalyzerMetrics.sThreadsPath, + keysPath[0], + PerformanceAnalyzerMetrics.sOSPath); + } +} 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..2fee572 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/jvm/ThreadList.java @@ -0,0 +1,434 @@ +/* + * 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); + } + } + } + + 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); + } + + /** + * A thread dump is a snapshot of the state of all the threads of a Java process. 1. We use + * HotSpotVirtualMachine to capture thread dump. VirtualMachine is type cast to + * HotSpotVirtualMachine to access the method at runtime. 2. Use this method ONLY when + * NativeThreadId is required, for all other purpose us ThreadMxBeans API’s. 3. Running Thread + * Dump is expensive and should be only judiciously done. + * + * @param pid + * @param args + */ + 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..a1cf337 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,11 @@ import java.util.HashMap; import java.util.Map; +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 +38,10 @@ 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(OSGlobals.class, cdefault); } } diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/ThreadIDUtil.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/ThreadIDUtil.java new file mode 100644 index 0000000..6a0e457 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics/ThreadIDUtil.java @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics; + + +import org.opensearch.performanceanalyzer.commons.jvm.ThreadList; + +public final class ThreadIDUtil { + private ThreadIDUtil() {} + + public static final ThreadIDUtil INSTANCE = new ThreadIDUtil(); + + public long getNativeCurrentThreadId() { + + return getNativeThreadId(Thread.currentThread().getId()); + } + + public long getNativeThreadId(long jTid) { + ThreadList.ThreadState threadState1 = ThreadList.getThreadState(jTid); + + long nid = -1; + if (threadState1 != null) { + nid = threadState1.nativeTid; + } + + return nid; + } +} 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..4be358c --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/CPUPagingActivityGenerator.java @@ -0,0 +1,29 @@ +/* + * 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 + * + * @param threadId + * @return + */ + boolean hasPagingActivity(String threadId); + + double getCPUUtilization(String threadId); + + double getMajorFault(String threadId); + + double getMinorFault(String threadId); + + double getResidentSetSize(String threadId); + + void addSample(); + + void addSample(String sample); +} 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..3e4f532 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/DiskIOMetricsGenerator.java @@ -0,0 +1,37 @@ +/* + * 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); + + double getAvgPageCacheReadThroughputBps(String threadId); + + double getAvgPageCacheWriteThroughputBps(String threadId); + + double getAvgPageCacheTotalThroughputBps(String threadId); + + void addSample(); + + void addSample(String threadId); +} 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..67f6486 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/OSMetricsGenerator.java @@ -0,0 +1,22 @@ +/* + * 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(); +} 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..9d0731a --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/SchedMetricsGenerator.java @@ -0,0 +1,23 @@ +/* + * 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(); + + void addSample(String sample); +} 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..c1b7c9a --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxCPUPagingActivityGenerator.java @@ -0,0 +1,85 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + +import static org.opensearch.performanceanalyzer.commons.util.Util.ALL_THREADS; + +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(ALL_THREADS); + } + + @Override + public void addSample(String threadId) { + cpu.remove(threadId); + pagingActivities.remove(threadId); + + ThreadCPU.INSTANCE.addSample(threadId); + } + + 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..a6f05b4 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxDiskIOMetricsGenerator.java @@ -0,0 +1,93 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + +import static org.opensearch.performanceanalyzer.commons.util.Util.ALL_THREADS; + +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 double getAvgPageCacheReadThroughputBps(final String threadId) { + return diskIOMetricsMap.get(threadId).avgPageCacheReadThroughputBps; + } + + @Override + public double getAvgPageCacheWriteThroughputBps(String threadId) { + return diskIOMetricsMap.get(threadId).avgPageCacheWriteThroughputBps; + } + + @Override + public double getAvgPageCacheTotalThroughputBps(String threadId) { + return diskIOMetricsMap.get(threadId).avgPageCacheTotalThroughputBps; + } + + @Override + public boolean hasDiskIOMetrics(final String threadId) { + + return diskIOMetricsMap.containsKey(threadId); + } + + @Override + public void addSample() { + ThreadDiskIO.addSample(ALL_THREADS); + } + + @Override + public void addSample(String threadId) { + ThreadDiskIO.addSample(threadId); + } + + 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/LinuxOSMetricsGenerator.java b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxOSMetricsGenerator.java new file mode 100644 index 0000000..d61d7bc --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxOSMetricsGenerator.java @@ -0,0 +1,60 @@ +/* + * 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.metrics_generator.CPUPagingActivityGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.DiskIOMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.OSMetricsGenerator; +import org.opensearch.performanceanalyzer.commons.metrics_generator.SchedMetricsGenerator; +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(); + } +} 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..fb4de4b --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/metrics_generator/linux/LinuxSchedMetricsGenerator.java @@ -0,0 +1,64 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.metrics_generator.linux; + +import static org.opensearch.performanceanalyzer.commons.util.Util.ALL_THREADS; + +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(ALL_THREADS); + } + + @Override + public void addSample(String threadId) { + schedMetricsMap.remove(threadId); + ThreadSched.INSTANCE.addSample(threadId); + } + + public void setSchedMetric(final String threadId, final ThreadSched.SchedMetrics schedMetrics) { + + schedMetricsMap.put(threadId, schedMetrics); + } +} 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..e9412e7 --- /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.collectors.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..2e82b77 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadCPU.java @@ -0,0 +1,267 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + +import static org.opensearch.performanceanalyzer.commons.util.Util.ALL_THREADS; + +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(String threadInfo) { + oldtidKVMap.clear(); + oldtidKVMap.putAll(tidKVMap); + + if (ALL_THREADS.equals(threadInfo)) { + addSampleForAllThreads(); + } else { + addSampleForThread(threadInfo); + } + } + + /** + * Creates the thread sample and adds it to a sample map Additionally, we need 2 service timers + * - to measure the time taken for parsing, calculateCPUDetails and calculatePagingActivity + */ + private void addSampleForAllThreads() { + tids = OSGlobals.getTids(); + + tidKVMap.clear(); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + for (String tid : tids) { + Map sample = + (new SchemaFileParser( + "/proc/" + pid + "/task/" + tid + "/stat", + statKeys, + statTypes, + true)) + .parse(); + tidKVMap.put(tid, sample); + } + + calculateCPUDetails(ALL_THREADS); + calculatePagingActivity(ALL_THREADS); + } + + private void addSampleForThread(String threadId) { + tidKVMap.remove(threadId); + + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + Map sample = + (new SchemaFileParser( + "/proc/" + pid + "/task/" + threadId + "/stat", + statKeys, + statTypes, + true)) + .parse(); + tidKVMap.put(threadId, sample); + + calculateCPUDetails(threadId); + calculatePagingActivity(threadId); + } + + private void calculateCPUDetails(String threadInfo) { + if (oldkvTimestamp == kvTimestamp) { + return; + } + if (ALL_THREADS.equals(threadInfo)) { + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + calculateThreadCPUDetails(entry.getKey(), v); + } + } else { + Map v = tidKVMap.get(threadInfo); + calculateThreadCPUDetails(threadInfo, v); + } + } + + private void calculateThreadCPUDetails(String threadId, Map v) { + Map oldv = oldtidKVMap.get(threadId); + if (v != null && oldv != null) { + if (!v.containsKey("utime") || !oldv.containsKey("utime")) { + return; + } + 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(threadId, util); + } + } + + /** Note: major faults include mmap()'ed accesses */ + private void calculatePagingActivity(String threadInfo) { + if (oldkvTimestamp == kvTimestamp) { + return; + } + if (ALL_THREADS.equals(threadInfo)) { + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + calculateThreadPagingActivity(entry.getKey(), v); + } + } else { + Map v = tidKVMap.get(threadInfo); + calculateThreadPagingActivity(threadInfo, v); + } + } + + private void calculateThreadPagingActivity(String threadId, Map v) { + Map oldv = oldtidKVMap.get(threadId); + if (v != null && oldv != null) { + if (!v.containsKey("majflt") || !oldv.containsKey("majflt")) { + return; + } + 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(threadId, 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..4e97d36 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadDiskIO.java @@ -0,0 +1,187 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + +import static org.opensearch.performanceanalyzer.commons.util.Util.ALL_THREADS; + +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.collectors.StatExceptionCode; +import org.opensearch.performanceanalyzer.commons.metrics_generator.linux.LinuxDiskIOMetricsGenerator; + +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(String threadInfo) { + oldtidKVMap.clear(); + oldtidKVMap.putAll(tidKVMap); + + if (ALL_THREADS.equals(threadInfo)) { + addSampleForAllThreads(); + } else { + addSampleForThread(threadInfo); + } + } + + private static void addSampleForAllThreads() { + tids = OSGlobals.getTids(); + + tidKVMap.clear(); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + + for (String tid : tids) { + addSampleTid(tid); + } + } + + private static void addSampleForThread(String threadId) { + tidKVMap.remove(threadId); + + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + addSampleTid(threadId); + } + + 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..c4b0121 --- /dev/null +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/os/ThreadSched.java @@ -0,0 +1,175 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.performanceanalyzer.commons.os; + +import static org.opensearch.performanceanalyzer.commons.util.Util.ALL_THREADS; + +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(String threadInfo) { + oldtidKVMap.clear(); + oldtidKVMap.putAll(tidKVMap); + + if (ALL_THREADS.equals(threadInfo)) { + addSampleForAllThreads(); + } else { + addSampleForThread(threadInfo); + } + } + + private void addSampleForAllThreads() { + tids = OSGlobals.getTids(); + + 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(ALL_THREADS); + } + + private void addSampleForThread(String threadId) { + tidKVMap.remove(threadId); + oldkvTimestamp = kvTimestamp; + kvTimestamp = System.currentTimeMillis(); + Map sample = + (new SchemaFileParser( + "/proc/" + pid + "/task/" + threadId + "/schedstat", + schedKeys, + schedTypes)) + .parse(); + tidKVMap.put(threadId, sample); + + calculateSchedLatency(threadId); + } + + private synchronized void calculateSchedLatency(String threadInfo) { + if (oldkvTimestamp == kvTimestamp) { + return; + } + + if (ALL_THREADS.equals(threadInfo)) { + for (Map.Entry> entry : tidKVMap.entrySet()) { + Map v = entry.getValue(); + calculateThreadSchedLatency(entry.getKey(), v); + } + } else { + Map v = tidKVMap.get(threadInfo); + calculateThreadSchedLatency(threadInfo, v); + } + } + + private void calculateThreadSchedLatency(String threadId, Map v) { + Map oldv = oldtidKVMap.get(threadId); + if (v != null && oldv != null) { + if (!v.containsKey("totctxsws") || !oldv.containsKey("totctxsws")) { + return; + } + 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( + threadId, new SchedMetrics(avgRuntime, avgWaittime, contextSwitchRate)); + } + } + + public synchronized SchedMetricsGenerator getSchedLatency() { + + return schedLatencyMap; + } +} diff --git a/src/main/java/org/opensearch/performanceanalyzer/commons/util/Util.java b/src/main/java/org/opensearch/performanceanalyzer/commons/util/Util.java index 3eddaa5..3a5186d 100644 --- a/src/main/java/org/opensearch/performanceanalyzer/commons/util/Util.java +++ b/src/main/java/org/opensearch/performanceanalyzer/commons/util/Util.java @@ -36,6 +36,8 @@ public class Util { public static final String LEGACY_OPENDISTRO_ACTIONS_QUERY_URL = LEGACY_OPENDISTRO_PA_BASE_URL + "/actions"; + public static final String ALL_THREADS = "all_threads"; + public static final String OPENSEARCH_HOME = System.getProperty("opensearch.path.home"); public static final String PLUGIN_LOCATION = OPENSEARCH_HOME diff --git a/src/main/resources/security.policy b/src/main/resources/security.policy new file mode 100644 index 0000000..b8c6c99 --- /dev/null +++ b/src/main/resources/security.policy @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +grant { + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; + permission java.lang.RuntimePermission "accessDeclaredMembers"; + //- Java 8 jdk.attach fucntionality needs write permissions for /proc/pid/cwd, which varies, + permission java.io.FilePermission "/-","read,readlink,write,delete,execute"; + //permission java.io.FilePermission "/dev/shm/-","read,readlink,write,delete,execute"; + //permission java.io.FilePermission "/proc/-","read,readlink,execute,write,delete"; + //permission java.io.FilePermission "/sys/block/-","read,readlink,execute,write,delete"; + permission java.io.FilePermission "build/tmp/junit_metrics", "read"; + permission com.sun.tools.attach.AttachPermission "attachVirtualMachine"; + permission com.sun.tools.attach.AttachPermission "createAttachProvider"; + permission java.lang.RuntimePermission "manageProcess"; + permission java.lang.RuntimePermission "loadLibrary.attach"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.tools.attach"; + permission java.lang.RuntimePermission "createClassLoader"; + permission java.lang.RuntimePermission "defineClass"; + permission java.lang.management.ManagementPermission "control"; +}; + +