Skip to content

Commit 463bf12

Browse files
committed
GH-439 group threads by state
1 parent ebd3781 commit 463bf12

File tree

7 files changed

+338
-30
lines changed

7 files changed

+338
-30
lines changed

visualvm/heapviewer/src/org/graalvm/visualvm/heapviewer/java/ThreadNode.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,22 @@ public class ThreadNode extends InstanceNode implements CCTNode.DoNotSortChildre
4242

4343
private final String name;
4444
private final boolean isOOME;
45+
private final Thread.State state;
4546

4647

4748
public ThreadNode(String name, Instance instance) {
48-
this(name, false, instance);
49+
this(name, null, false, instance);
50+
}
51+
52+
public ThreadNode(String name, Thread.State state, Instance instance) {
53+
this(name, state, false, instance);
4954
}
5055

51-
public ThreadNode(String name, boolean isOOME, Instance instance) {
56+
public ThreadNode(String name, Thread.State state, boolean isOOME, Instance instance) {
5257
super(instance);
5358
this.name = name;
5459
this.isOOME = isOOME;
60+
this.state = state;
5561
}
5662

5763

@@ -63,11 +69,14 @@ public boolean isOOMEThread() {
6369
return isOOME;
6470
}
6571

72+
public Thread.State getState() {
73+
return state;
74+
}
6675

6776
public static class Unknown extends ThreadNode {
6877

6978
public Unknown() {
70-
super(Bundle.ThreadNode_UnknownThread(), null);
79+
super(Bundle.ThreadNode_UnknownThread(), null, null);
7180
}
7281

7382
public boolean equals(Object o) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.visualvm.heapviewer.java;
26+
27+
import java.util.List;
28+
import org.graalvm.visualvm.heapviewer.model.DataType;
29+
import org.graalvm.visualvm.heapviewer.model.HeapViewerNode;
30+
import org.graalvm.visualvm.heapviewer.model.NodesCache;
31+
import org.graalvm.visualvm.lib.jfluid.heap.Heap;
32+
33+
/**
34+
*
35+
* @author Tomas Hurka
36+
*/
37+
public class ThreadStateNode extends HeapViewerNode {
38+
private final Thread.State state;
39+
40+
public ThreadStateNode(Thread.State state, List<HeapViewerNode> children) {
41+
this.state = state;
42+
setChildren(children.toArray(NO_NODES));
43+
}
44+
45+
public String getName() {
46+
if (state == null) return "Undefined"; // NOI18N
47+
return state.toString();
48+
}
49+
50+
public Thread.State getState() {
51+
return state;
52+
}
53+
54+
public String toString() {
55+
return getName();
56+
}
57+
58+
protected void resetChildren() {}
59+
60+
public void forgetChildren(NodesCache cache) {}
61+
62+
protected Object getValue(DataType type, Heap heap) {
63+
if (type == DataType.NAME) return getName();
64+
if (type == DataType.COUNT) return DataType.COUNT.getUnsupportedValue();
65+
if (type == DataType.OWN_SIZE) return DataType.OWN_SIZE./*getNoValue()*/getUnsupportedValue();
66+
if (type == DataType.RETAINED_SIZE) return DataType.RETAINED_SIZE./*getNoValue()*/getUnsupportedValue();
67+
68+
if (type == DataType.INSTANCE) return DataType.INSTANCE./*getNoValue()*/getUnsupportedValue();
69+
if (type == DataType.CLASS) return DataType.CLASS./*getNoValue()*/getUnsupportedValue();
70+
71+
if (type == DataType.LOGICAL_VALUE) return DataType.LOGICAL_VALUE./*getNoValue()*/getUnsupportedValue();
72+
73+
return super.getValue(type, heap);
74+
}
75+
76+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.visualvm.heapviewer.java;
26+
27+
import java.awt.Font;
28+
import java.util.HashMap;
29+
import java.util.Map;
30+
import javax.swing.Icon;
31+
import org.graalvm.visualvm.heapviewer.ui.HeapViewerRenderer;
32+
import org.graalvm.visualvm.lib.jfluid.global.CommonConstants;
33+
import org.graalvm.visualvm.lib.profiler.api.icons.Icons;
34+
import org.graalvm.visualvm.lib.profiler.api.icons.ProfilerIcons;
35+
import org.graalvm.visualvm.lib.ui.swing.renderer.LabelRenderer;
36+
import org.graalvm.visualvm.lib.ui.threads.ThreadStateIcon;
37+
38+
/**
39+
*
40+
* @author Tomas Hurka
41+
*/
42+
public class ThreadStateNodeRenderer extends LabelRenderer implements HeapViewerRenderer {
43+
44+
private static final Icon ICON = Icons.getIcon(ProfilerIcons.THREAD);
45+
46+
public ThreadStateNodeRenderer() {
47+
setIcon(ICON);
48+
setFont(getFont().deriveFont(Font.BOLD));
49+
}
50+
51+
52+
public void setValue(Object value, int row) {
53+
Icon i;
54+
ThreadStateNode node = (ThreadStateNode)value;
55+
setText(node.getName());
56+
setIcon(getIcon(node.getState()));
57+
}
58+
59+
public String getShortName() {
60+
return getText();
61+
}
62+
63+
private static final int THREAD_ICON_SIZE = 9;
64+
private static final Map<Thread.State, Icon> STATE_ICONS_CACHE = new HashMap();
65+
66+
private static Icon getIcon(Thread.State state) {
67+
Icon icon = STATE_ICONS_CACHE.get(state);
68+
69+
if (icon == null) {
70+
int pState;
71+
switch (state) {
72+
case RUNNABLE:
73+
pState = CommonConstants.THREAD_STATUS_RUNNING;
74+
break;
75+
case BLOCKED:
76+
pState = CommonConstants.THREAD_STATUS_MONITOR;
77+
break;
78+
case WAITING:
79+
pState = CommonConstants.THREAD_STATUS_WAIT;
80+
break;
81+
case TIMED_WAITING:
82+
pState = CommonConstants.THREAD_STATUS_SLEEPING;
83+
break;
84+
default:
85+
pState = CommonConstants.THREAD_STATUS_UNKNOWN;
86+
break;
87+
}
88+
icon = new ThreadStateIcon(pState, THREAD_ICON_SIZE, THREAD_ICON_SIZE);
89+
STATE_ICONS_CACHE.put(state, icon);
90+
}
91+
return icon;
92+
}
93+
94+
95+
}

visualvm/heapviewer/src/org/graalvm/visualvm/heapviewer/java/impl/JavaNodesRendererProvider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import org.graalvm.visualvm.heapviewer.java.StackFrameNodeRenderer;
4545
import org.graalvm.visualvm.heapviewer.java.ThreadNode;
4646
import org.graalvm.visualvm.heapviewer.java.ThreadNodeRenderer;
47+
import org.graalvm.visualvm.heapviewer.java.ThreadStateNode;
48+
import org.graalvm.visualvm.heapviewer.java.ThreadStateNodeRenderer;
4749
import org.graalvm.visualvm.heapviewer.model.HeapViewerNode;
4850
import org.graalvm.visualvm.heapviewer.ui.HeapViewerRenderer;
4951
import org.graalvm.visualvm.lib.jfluid.heap.Heap;
@@ -89,6 +91,9 @@ public void registerRenderers(Map<Class<? extends HeapViewerNode>, HeapViewerRen
8991

9092
// stack frames
9193
renderers.put(StackFrameNode.class, new StackFrameNodeRenderer());
94+
95+
// thread state
96+
renderers.put(ThreadStateNode.class, new ThreadStateNodeRenderer());
9297

9398
// local variables
9499
renderers.put(LocalObjectNode.class, new LocalObjectNodeRenderer(heap));

visualvm/heapviewer/src/org/graalvm/visualvm/heapviewer/java/impl/JavaThreadsProvider.java

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.graalvm.visualvm.heapviewer.java.LocalObjectNode;
3737
import org.graalvm.visualvm.heapviewer.java.StackFrameNode;
3838
import org.graalvm.visualvm.heapviewer.java.ThreadNode;
39+
import org.graalvm.visualvm.heapviewer.java.ThreadStateNode;
3940
import org.graalvm.visualvm.heapviewer.model.HeapViewerNode;
4041
import org.graalvm.visualvm.heapviewer.model.RootNode;
4142
import org.graalvm.visualvm.heapviewer.utils.HeapUtils;
@@ -69,31 +70,57 @@ class JavaThreadsProvider {
6970
private static final String LOCAL_VARIABLE = Bundle.JavaThreadsProvider_LocalVariable();
7071
private static final String JNI_LOCAL = Bundle.JavaThreadsProvider_JniLocal();
7172

72-
73-
static String getThreadName(JavaClass vtClass, Instance instance) {
74-
if (isVirtualThread(vtClass, instance)) {
75-
return "Virtual Thread "+DetailsSupport.getDetailsString(instance); // NOI18N
73+
private static class ThreadInfo {
74+
75+
String threadName;
76+
Long threadId;
77+
Boolean daemon;
78+
Integer priority;
79+
Thread.State threadState;
80+
String virtualName;
81+
82+
ThreadInfo(JavaClass vtClass, Instance instance) {
83+
if (isVirtualThread(vtClass, instance)) {
84+
virtualName = "Virtual Thread "+DetailsSupport.getDetailsString(instance); // NOI18N
85+
return;
86+
}
87+
threadName = getThreadInstanceName(instance);
88+
threadId = (Long) instance.getValueOfField("tid"); // NOI18N
89+
daemon = (Boolean) instance.getValueOfField("daemon"); // NOI18N
90+
priority = (Integer) instance.getValueOfField("priority"); // NOI18N
91+
Integer threadStatus = (Integer) instance.getValueOfField("threadStatus"); // NOI18N
92+
93+
if (daemon == null) {
94+
Instance holder = (Instance) instance.getValueOfField("holder"); // NOI18N
95+
if (holder != null) {
96+
daemon = (Boolean) holder.getValueOfField("daemon"); // NOI18N
97+
priority = (Integer) holder.getValueOfField("priority"); // NOI18N
98+
threadStatus = (Integer) holder.getValueOfField("threadStatus"); // NOI18N
99+
}
100+
}
101+
if (threadStatus != null) {
102+
threadState = toThreadState(threadStatus.intValue());
103+
}
104+
}
105+
106+
Thread.State getThreadState() {
107+
return threadState;
76108
}
77-
String threadName = getThreadInstanceName(instance);
78-
Long threadId = (Long)instance.getValueOfField("tid"); // NOI18N
79-
Boolean daemon = (Boolean)instance.getValueOfField("daemon"); // NOI18N
80-
Integer priority = (Integer)instance.getValueOfField("priority"); // NOI18N
81-
Integer threadStatus = (Integer)instance.getValueOfField("threadStatus"); // NOI18N
82109

83-
if (daemon == null) {
84-
Instance holder = (Instance)instance.getValueOfField("holder"); // NOI18N
85-
if (holder != null) {
86-
daemon = (Boolean)holder.getValueOfField("daemon"); // NOI18N
87-
priority = (Integer)holder.getValueOfField("priority"); // NOI18N
88-
threadStatus = (Integer)holder.getValueOfField("threadStatus"); // NOI18N
110+
public String toString() {
111+
if (virtualName != null) {
112+
return virtualName;
89113
}
114+
String tName = "\"" + threadName + "\"" + (daemon.booleanValue() ? " daemon" : "") + " prio=" + priority; // NOI18N
115+
if (threadId != null) tName += " tid=" + threadId; // NOI18N
116+
if (threadState != null) tName += " " + threadState; // NOI18N
117+
118+
return tName;
90119
}
120+
}
91121

92-
String tName = "\"" + threadName + "\"" + (daemon.booleanValue() ? " daemon" : "") + " prio=" + priority; // NOI18N
93-
if (threadId != null) tName += " tid=" + threadId; // NOI18N
94-
if (threadStatus != null) tName += " " + toThreadState(threadStatus.intValue()); // NOI18N
95-
96-
return tName;
122+
static String getThreadName(JavaClass vtClass, Instance instance) {
123+
return new ThreadInfo(vtClass, instance).toString();
97124
}
98125

99126
static ThreadObjectGCRoot getOOMEThread(Heap heap) {
@@ -132,6 +159,28 @@ static HeapViewerNode getNode(URL url, HeapContext context) {
132159
return null;
133160
}
134161

162+
static HeapViewerNode[] getStateNodes(RootNode root, Heap heap) throws InterruptedException {
163+
HeapViewerNode[] stateNodes;
164+
HeapViewerNode[] threadsNodes = getThreadsNodes(root, heap);
165+
Map<Thread.State,List<HeapViewerNode>> states = new HashMap<>();
166+
for (HeapViewerNode n : threadsNodes) {
167+
Thread.State s = ((ThreadNode)n).getState();
168+
List<HeapViewerNode> nodes = states.get(s);
169+
if (nodes == null) {
170+
nodes = new ArrayList<>();
171+
states.put(s, nodes);
172+
}
173+
nodes.add(n);
174+
}
175+
int i = 0;
176+
stateNodes = new HeapViewerNode[states.size()];
177+
for (Map.Entry<Thread.State, List<HeapViewerNode>> stateEntry : states.entrySet()) {
178+
Thread.State state = stateEntry.getKey();
179+
ThreadStateNode stateNode = new ThreadStateNode(state, stateEntry.getValue());
180+
stateNodes[i++] = stateNode;
181+
}
182+
return stateNodes;
183+
}
135184

136185
static HeapViewerNode[] getThreadsNodes(RootNode rootNode, Heap heap) throws InterruptedException {
137186
List<HeapViewerNode> threadNodes = new ArrayList();
@@ -151,10 +200,9 @@ static HeapViewerNode[] getThreadsNodes(RootNode rootNode, Heap heap) throws Int
151200
StackTraceElement stack[] = threadRoot.getStackTrace();
152201
Map<Integer,List<GCRoot>> localsMap = javaFrameMap.get(threadRoot);
153202

154-
String tName = JavaThreadsProvider.getThreadName(vtClass, threadInstance);
155-
203+
ThreadInfo ti = new ThreadInfo(vtClass, threadInstance);
156204
final List<HeapViewerNode> stackFrameNodes = new ArrayList();
157-
ThreadNode threadNode = new ThreadNode(tName, threadRoot.equals(oome), threadInstance) {
205+
ThreadNode threadNode = new ThreadNode(ti.toString(), ti.getThreadState(), threadRoot.equals(oome), threadInstance) {
158206
protected HeapViewerNode[] computeChildren(RootNode root) {
159207
return stackFrameNodes.toArray(HeapViewerNode.NO_NODES);
160208
}

visualvm/heapviewer/src/org/graalvm/visualvm/heapviewer/java/impl/JavaThreadsSummary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private JavaThreadsSummary(Instance oomeInstance, HeapContext context, HeapViewe
9898

9999
JavaClass vtClass = oomeInstance.getJavaClass().getHeap().getJavaClassByName("java.lang.VirtualThread"); // NOI18N
100100
String threadName = JavaThreadsProvider.getThreadName(vtClass, oomeInstance);
101-
threadData = new Object[][] {{ new ThreadNode(threadName, true, oomeInstance) }};
101+
threadData = new Object[][] {{ new ThreadNode(threadName, null, true, oomeInstance) }};
102102
}
103103

104104

0 commit comments

Comments
 (0)