diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java index 781a6af30ab2f..f89455e03f1df 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java @@ -13,6 +13,7 @@ import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequestBuilder; import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsResponse; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.monitor.jvm.HotThreads; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matcher; @@ -66,7 +67,7 @@ public void testHotThreadsDontFail() throws ExecutionException, InterruptedExcep break; } assertThat(type, notNullValue()); - nodesHotThreadsRequestBuilder.setType(type); + nodesHotThreadsRequestBuilder.setType(HotThreads.ReportType.of(type)); } else { type = null; } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java index d199d28ca4ca1..1801be97c48b7 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.monitor.jvm.HotThreads; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -19,7 +20,7 @@ public class NodesHotThreadsRequest extends BaseNodesRequest { int threads = 3; - String type = "cpu"; + HotThreads.ReportType type = HotThreads.ReportType.CPU; TimeValue interval = new TimeValue(500, TimeUnit.MILLISECONDS); int snapshots = 10; boolean ignoreIdleThreads = true; @@ -29,7 +30,7 @@ public NodesHotThreadsRequest(StreamInput in) throws IOException { super(in); threads = in.readInt(); ignoreIdleThreads = in.readBoolean(); - type = in.readString(); + type = HotThreads.ReportType.of(in.readString()); interval = in.readTimeValue(); snapshots = in.readInt(); } @@ -60,12 +61,12 @@ public NodesHotThreadsRequest ignoreIdleThreads(boolean ignoreIdleThreads) { return this; } - public NodesHotThreadsRequest type(String type) { + public NodesHotThreadsRequest type(HotThreads.ReportType type) { this.type = type; return this; } - public String type() { + public HotThreads.ReportType type() { return this.type; } @@ -92,7 +93,7 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeInt(threads); out.writeBoolean(ignoreIdleThreads); - out.writeString(type); + out.writeString(type.getTypeValue()); out.writeTimeValue(interval); out.writeInt(snapshots); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java index d25559b0236cd..50751c7e1f9c3 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.support.nodes.NodesOperationRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.monitor.jvm.HotThreads; public class NodesHotThreadsRequestBuilder extends NodesOperationRequestBuilder { @@ -29,7 +30,7 @@ public NodesHotThreadsRequestBuilder setIgnoreIdleThreads(boolean ignoreIdleThre return this; } - public NodesHotThreadsRequestBuilder setType(String type) { + public NodesHotThreadsRequestBuilder setType(HotThreads.ReportType type) { request.type(type); return this; } diff --git a/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java b/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java index 87957a0fa9639..e5182cb642b1e 100644 --- a/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java +++ b/server/src/main/java/org/elasticsearch/monitor/jvm/HotThreads.java @@ -31,15 +31,42 @@ public class HotThreads { private static final Object mutex = new Object(); + private static final StackTraceElement[] EMPTY = new StackTraceElement[0]; private static final DateFormatter DATE_TIME_FORMATTER = DateFormatter.forPattern("date_optional_time"); private int busiestThreads = 3; private TimeValue interval = new TimeValue(500, TimeUnit.MILLISECONDS); - private TimeValue threadElementsSnapshotDelay = new TimeValue(10); + private TimeValue threadElementsSnapshotDelay = new TimeValue(10, TimeUnit.MILLISECONDS); private int threadElementsSnapshotCount = 10; - private String type = "cpu"; + private ReportType type = ReportType.CPU; private boolean ignoreIdleThreads = true; + public enum ReportType { + + CPU("cpu"), + WAIT("wait"), + BLOCK("block"); + + private final String type; + + ReportType(String type) { + this.type = type; + } + + public String getTypeValue() { + return type; + } + + public static ReportType of(String type) { + for (ReportType report : values()) { + if (report.type.equals(type)) { + return report; + } + } + throw new IllegalArgumentException("type not supported [" + type + "]"); + } + } + public HotThreads interval(TimeValue interval) { this.interval = interval; return this; @@ -65,12 +92,8 @@ public HotThreads threadElementsSnapshotCount(int threadElementsSnapshotCount) { return this; } - public HotThreads type(String type) { - if ("cpu".equals(type) || "wait".equals(type) || "block".equals(type)) { - this.type = type; - } else { - throw new IllegalArgumentException("type not supported [" + type + "]"); - } + public HotThreads type(ReportType type) { + this.type = type; return this; } @@ -134,7 +157,7 @@ String innerDetect(ThreadMXBean threadBean, long currentThreadId) throws Excepti sb.append(ignoreIdleThreads); sb.append(":\n"); - Map threadInfos = new HashMap<>(); + Map threadInfos = new HashMap<>(); for (long threadId : threadBean.getAllThreadIds()) { // ignore our own thread... if (currentThreadId == threadId) { @@ -148,7 +171,7 @@ String innerDetect(ThreadMXBean threadBean, long currentThreadId) throws Excepti if (info == null) { continue; } - threadInfos.put(threadId, new MyThreadInfo(cpu, info)); + threadInfos.put(threadId, new ThreadTimeAccumulator(info, cpu)); } Thread.sleep(interval.millis()); for (long threadId : threadBean.getAllThreadIds()) { @@ -166,43 +189,25 @@ String innerDetect(ThreadMXBean threadBean, long currentThreadId) throws Excepti threadInfos.remove(threadId); continue; } - MyThreadInfo data = threadInfos.get(threadId); + ThreadTimeAccumulator data = threadInfos.get(threadId); if (data != null) { - data.setDelta(cpu, info); + data.setDelta(info, cpu); } else { threadInfos.remove(threadId); } } // sort by delta CPU time on thread. - List hotties = new ArrayList<>(threadInfos.values()); + List hotties = new ArrayList<>(threadInfos.values()); final int busiestThreads = Math.min(this.busiestThreads, hotties.size()); // skip that for now - final ToLongFunction getter; - if ("cpu".equals(type)) { - getter = o -> { - assert o.cpuTime >= -1 : "cpu time should not be negative, but was " + o.cpuTime + ", thread info: " + o.info; - return o.cpuTime; - }; - } else if ("wait".equals(type)) { - getter = o -> { - assert o.waitedTime >= -1 : "waited time should not be negative, but was " + o.waitedTime + ", thread info: " + o.info; - return o.waitedTime; - }; - } else if ("block".equals(type)) { - getter = o -> { - assert o.blockedTime >= -1 : "blocked time should not be negative, but was " + o.blockedTime + ", thread info: " + o.info; - return o.blockedTime; - }; - } else { - throw new IllegalArgumentException("expected thread type to be either 'cpu', 'wait', or 'block', but was " + type); - } + final ToLongFunction getter = ThreadTimeAccumulator.valueGetterForReportType(type); CollectionUtil.introSort(hotties, Comparator.comparingLong(getter).reversed()); // analyse N stack traces for M busiest threads long[] ids = new long[busiestThreads]; for (int i = 0; i < busiestThreads; i++) { - MyThreadInfo info = hotties.get(i); + ThreadTimeAccumulator info = hotties.get(i); ids[i] = info.info.getThreadId(); } ThreadInfo[][] allInfos = new ThreadInfo[threadElementsSnapshotCount][]; @@ -231,7 +236,7 @@ String innerDetect(ThreadMXBean threadBean, long currentThreadId) throws Excepti } double percent = (((double) time) / interval.nanos()) * 100; sb.append(String.format(Locale.ROOT, "%n%4.1f%% (%s out of %s) %s usage by thread '%s'%n", - percent, TimeValue.timeValueNanos(time), interval, type, threadName)); + percent, TimeValue.timeValueNanos(time), interval, type.getTypeValue(), threadName)); // for each snapshot (2nd array index) find later snapshot for same thread with max number of // identical StackTraceElements (starting from end of each) boolean[] done = new boolean[threadElementsSnapshotCount]; @@ -276,8 +281,6 @@ String innerDetect(ThreadMXBean threadBean, long currentThreadId) throws Excepti return sb.toString(); } - private static final StackTraceElement[] EMPTY = new StackTraceElement[0]; - int similarity(ThreadInfo threadInfo, ThreadInfo threadInfo0) { StackTraceElement[] s1 = threadInfo == null ? EMPTY : threadInfo.getStackTrace(); StackTraceElement[] s2 = threadInfo0 == null ? EMPTY : threadInfo0.getStackTrace(); @@ -293,7 +296,7 @@ int similarity(ThreadInfo threadInfo, ThreadInfo threadInfo0) { } - class MyThreadInfo { + static class ThreadTimeAccumulator { long cpuTime; long blockedCount; long blockedTime; @@ -302,7 +305,7 @@ class MyThreadInfo { boolean deltaDone; ThreadInfo info; - MyThreadInfo(long cpuTime, ThreadInfo info) { + ThreadTimeAccumulator(ThreadInfo info, long cpuTime) { blockedCount = info.getBlockedCount(); blockedTime = info.getBlockedTime(); waitedCount = info.getWaitedCount(); @@ -311,7 +314,7 @@ class MyThreadInfo { this.info = info; } - void setDelta(long cpuTime, ThreadInfo info) { + void setDelta(ThreadInfo info, long cpuTime) { if (deltaDone) throw new IllegalStateException("setDelta already called once"); blockedCount = info.getBlockedCount() - blockedCount; blockedTime = info.getBlockedTime() - blockedTime; @@ -321,5 +324,29 @@ void setDelta(long cpuTime, ThreadInfo info) { deltaDone = true; this.info = info; } + + static ToLongFunction valueGetterForReportType(ReportType type) { + switch (type) { + case CPU: + return o -> { + assert o.cpuTime >= -1 : + "cpu time should not be negative, but was " + o.cpuTime + ", thread info: " + o.info; + return o.cpuTime; + }; + case WAIT: + return o -> { + assert o.waitedTime >= -1 : + "waited time should not be negative, but was " + o.waitedTime + ", thread info: " + o.info; + return o.waitedTime; + }; + case BLOCK: + return o -> { + assert o.blockedTime >= -1 : + "blocked time should not be negative, but was " + o.blockedTime + ", thread info: " + o.info; + return o.blockedTime; + }; + } + throw new IllegalArgumentException("expected thread type to be either 'cpu', 'wait', or 'block', but was " + type); + } } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java index 9b919ffe31439..d2f5c8cb1ed1e 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestNodesHotThreadsAction.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.core.RestApiVersion; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.monitor.jvm.HotThreads; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestRequest; @@ -93,7 +94,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC NodesHotThreadsRequest nodesHotThreadsRequest = new NodesHotThreadsRequest(nodesIds); nodesHotThreadsRequest.threads(request.paramAsInt("threads", nodesHotThreadsRequest.threads())); nodesHotThreadsRequest.ignoreIdleThreads(request.paramAsBoolean("ignore_idle_threads", nodesHotThreadsRequest.ignoreIdleThreads())); - nodesHotThreadsRequest.type(request.param("type", nodesHotThreadsRequest.type())); + nodesHotThreadsRequest.type(HotThreads.ReportType.of(request.param("type", nodesHotThreadsRequest.type().getTypeValue()))); nodesHotThreadsRequest.interval(TimeValue.parseTimeValue(request.param("interval"), nodesHotThreadsRequest.interval(), "interval")); nodesHotThreadsRequest.snapshots(request.paramAsInt("snapshots", nodesHotThreadsRequest.snapshots())); nodesHotThreadsRequest.timeout(request.param("timeout")); diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/hotthreads/NodesHotThreadsRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/hotthreads/NodesHotThreadsRequestTests.java new file mode 100644 index 0000000000000..f433922f3cb5d --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/hotthreads/NodesHotThreadsRequestTests.java @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.cluster.hotthreads; + +import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequest; +import org.elasticsearch.action.support.nodes.BaseNodesRequest; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.monitor.jvm.HotThreads; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +public class NodesHotThreadsRequestTests extends ESTestCase { + + /** Simple override of BaseNodesRequest to ensure we read the + * common fields of the nodes request. + */ + static class NodesHotThreadsRequestHelper extends BaseNodesRequest { + NodesHotThreadsRequestHelper(StreamInput in) throws IOException { + super(in); + } + + NodesHotThreadsRequestHelper(String... nodesIds) { + super(nodesIds); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } + } + + public void testBWCSerialization() throws IOException { + BytesStreamOutput out = new BytesStreamOutput(); + + TimeValue sampleInterval = new TimeValue(50, TimeUnit.MINUTES); + + NodesHotThreadsRequestHelper outHelper = new NodesHotThreadsRequestHelper("123"); + + outHelper.writeTo(out); + // Write manually some values that differ from the defaults + // in NodesHotThreadsRequest + out.writeInt(4); // threads + out.writeBoolean(false); // ignoreIdleThreads + out.writeString("block"); // type + out.writeTimeValue(sampleInterval); // interval + out.writeInt(3); // snapshots + + NodesHotThreadsRequest inRequest = new NodesHotThreadsRequest(out.bytes().streamInput()); + + assertEquals(4, inRequest.threads()); + assertFalse(inRequest.ignoreIdleThreads()); + assertEquals(HotThreads.ReportType.BLOCK, inRequest.type()); + assertEquals(sampleInterval, inRequest.interval()); + assertEquals(3, inRequest.snapshots()); + + // Change the report type enum + inRequest.type(HotThreads.ReportType.WAIT); + + BytesStreamOutput writeOut = new BytesStreamOutput(); + inRequest.writeTo(writeOut); + + StreamInput whatWeWrote = writeOut.bytes().streamInput(); + + // We construct the helper to read the common serialized fields from the in. + new NodesHotThreadsRequestHelper(whatWeWrote); + // Make sure we serialized in the following format + assertEquals(4, whatWeWrote.readInt()); + assertFalse(whatWeWrote.readBoolean()); + assertEquals("wait", whatWeWrote.readString()); // lowercase enum value, not label + assertEquals(sampleInterval, whatWeWrote.readTimeValue()); + assertEquals(3, whatWeWrote.readInt()); + } +} diff --git a/server/src/test/java/org/elasticsearch/monitor/jvm/HotThreadsTests.java b/server/src/test/java/org/elasticsearch/monitor/jvm/HotThreadsTests.java index fe05d049c3ba4..ec4e1834b69ef 100644 --- a/server/src/test/java/org/elasticsearch/monitor/jvm/HotThreadsTests.java +++ b/server/src/test/java/org/elasticsearch/monitor/jvm/HotThreadsTests.java @@ -15,7 +15,6 @@ import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; @@ -30,12 +29,12 @@ public class HotThreadsTests extends ESTestCase { public void testSupportedThreadsReportType() { for (String type: new String[] {"unsupported", "", null, "CPU", "WAIT", "BLOCK" }) { - expectThrows(IllegalArgumentException.class, () -> new HotThreads().type(type)); + expectThrows(IllegalArgumentException.class, () -> new HotThreads().type(HotThreads.ReportType.of(type))); } for (String type : new String[] { "cpu", "wait", "block" }) { try { - new HotThreads().type(type); + new HotThreads().type(HotThreads.ReportType.of(type)); } catch (IllegalArgumentException e) { fail(String.format(Locale.ROOT, "IllegalArgumentException called when creating HotThreads for supported type [%s]", type)); } @@ -69,7 +68,7 @@ public void testIdleThreadsDetection() { } List testJvmStack = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"org.elasticsearch.monitor.test", "methodOne"}, new String[]{"org.elasticsearch.monitor.testOther", "methodTwo"}, new String[]{"org.elasticsearch.monitor.test", "methodThree"}, @@ -83,7 +82,7 @@ public void testIdleThreadsDetection() { assertFalse(HotThreads.isIdleThread(notIdleThread)); List idleThreadStackElements = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"java.util.concurrent.ThreadPoolExecutor", "getTask"}, new String[]{"sun.nio.ch.SelectorImpl", "select"}, new String[]{"org.elasticsearch.threadpool.ThreadPool$CachedTimeThread", "run"}, @@ -115,25 +114,25 @@ public void testIdleThreadsDetection() { public void testSimilarity() { StackTraceElement[] stackOne = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"org.elasticsearch.monitor.test", "methodOne"}, new String[]{"org.elasticsearch.monitor.testOther", "methodTwo"} )).toArray(new StackTraceElement[0]); StackTraceElement[] stackTwo = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"org.elasticsearch.monitor.test1", "methodOne"}, new String[]{"org.elasticsearch.monitor.testOther", "methodTwo"} )).toArray(new StackTraceElement[0]); StackTraceElement[] stackThree = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"org.elasticsearch.monitor.testOther", "methodTwo"}, new String[]{"org.elasticsearch.monitor.test", "methodOne"} )).toArray(new StackTraceElement[0]); StackTraceElement[] stackFour = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"org.elasticsearch.monitor.testPrior", "methodOther"}, new String[]{"org.elasticsearch.monitor.test", "methodOne"}, new String[]{"org.elasticsearch.monitor.testOther", "methodTwo"} @@ -205,7 +204,7 @@ private List makeThreadInfoMocksHelper(ThreadMXBean mockedMXBean, lo when(mockedThreadInfo.getThreadId()).thenReturn(threadId); StackTraceElement[] stack = makeThreadStackHelper( - Arrays.asList( + org.elasticsearch.core.List.of( new String[]{"org.elasticsearch.monitor.test", String.format(Locale.ROOT, "method_%d", (threadId) % 2)}, new String[]{"org.elasticsearch.monitor.testOther", "methodFinal"} )).toArray(new StackTraceElement[0]); @@ -226,12 +225,13 @@ public void testInnerDetect() throws Exception { when(mockedMXBean.getAllThreadIds()).thenReturn(threadIds); List allInfos = makeThreadInfoMocksHelper(mockedMXBean, threadIds); - List cpuOrderedInfos = Arrays.asList(allInfos.get(3), allInfos.get(2), allInfos.get(1), allInfos.get(0)); + List cpuOrderedInfos = org.elasticsearch.core.List.of( + allInfos.get(3), allInfos.get(2), allInfos.get(1), allInfos.get(0)); when(mockedMXBean.getThreadInfo(Matchers.any(), anyInt())).thenReturn(cpuOrderedInfos.toArray(new ThreadInfo[0])); HotThreads hotThreads = new HotThreads() .busiestThreads(4) - .type("cpu") + .type(HotThreads.ReportType.CPU) .interval(TimeValue.timeValueNanos(10)) .threadElementsSnapshotCount(11) .ignoreIdleThreads(false); @@ -259,13 +259,14 @@ public void testInnerDetect() throws Exception { HotThreads hotWaitingThreads = new HotThreads() .busiestThreads(4) - .type("wait") + .type(HotThreads.ReportType.WAIT) .interval(TimeValue.timeValueNanos(10)) .threadElementsSnapshotCount(11) .ignoreIdleThreads(false); allInfos = makeThreadInfoMocksHelper(mockedMXBean, threadIds); - List waitOrderedInfos = Arrays.asList(allInfos.get(3), allInfos.get(1), allInfos.get(0), allInfos.get(2)); + List waitOrderedInfos = org.elasticsearch.core.List.of( + allInfos.get(3), allInfos.get(1), allInfos.get(0), allInfos.get(2)); when(mockedMXBean.getThreadInfo(Matchers.any(), anyInt())).thenReturn(waitOrderedInfos.toArray(new ThreadInfo[0])); String waitInnerResult = hotWaitingThreads.innerDetect(mockedMXBean, mockCurrentThreadId); @@ -277,13 +278,14 @@ public void testInnerDetect() throws Exception { HotThreads hotBlockedThreads = new HotThreads() .busiestThreads(4) - .type("block") + .type(HotThreads.ReportType.BLOCK) .interval(TimeValue.timeValueNanos(10)) .threadElementsSnapshotCount(11) .ignoreIdleThreads(false); allInfos = makeThreadInfoMocksHelper(mockedMXBean, threadIds); - List blockOrderedInfos = Arrays.asList(allInfos.get(2), allInfos.get(0), allInfos.get(1), allInfos.get(3)); + List blockOrderedInfos = org.elasticsearch.core.List.of( + allInfos.get(2), allInfos.get(0), allInfos.get(1), allInfos.get(3)); when(mockedMXBean.getThreadInfo(Matchers.any(), anyInt())).thenReturn(blockOrderedInfos.toArray(new ThreadInfo[0])); String blockInnerResult = hotBlockedThreads.innerDetect(mockedMXBean, mockCurrentThreadId); @@ -308,7 +310,7 @@ public void testEnsureInnerDetectSkipsCurrentThread() throws Exception { HotThreads hotThreads = new HotThreads() .busiestThreads(4) - .type("cpu") + .type(HotThreads.ReportType.CPU) .interval(TimeValue.timeValueNanos(10)) .threadElementsSnapshotCount(11) .ignoreIdleThreads(false); @@ -317,4 +319,22 @@ public void testEnsureInnerDetectSkipsCurrentThread() throws Exception { assertEquals(1, innerResult.split("\r\n|\r|\n").length); } + + public void testReportTypeValueGetter() { + ThreadInfo mockedThreadInfo = mock(ThreadInfo.class); + + when(mockedThreadInfo.getBlockedTime()).thenReturn(2L).thenReturn(0L); + when(mockedThreadInfo.getWaitedTime()).thenReturn(3L).thenReturn(0L); + + HotThreads.ThreadTimeAccumulator info = new HotThreads.ThreadTimeAccumulator(mockedThreadInfo, 1L); + + assertEquals(1L, HotThreads.ThreadTimeAccumulator.valueGetterForReportType(HotThreads.ReportType.CPU).applyAsLong(info)); + assertEquals(3L, HotThreads.ThreadTimeAccumulator.valueGetterForReportType(HotThreads.ReportType.WAIT).applyAsLong(info)); + assertEquals(2L, HotThreads.ThreadTimeAccumulator.valueGetterForReportType(HotThreads.ReportType.BLOCK).applyAsLong(info)); + + //Ensure all enum types have a report type getter + for (HotThreads.ReportType type : HotThreads.ReportType.values()) { + assertNotNull(HotThreads.ThreadTimeAccumulator.valueGetterForReportType(type)); + } + } }