Skip to content

Commit 0e18a42

Browse files
committed
GH-627 virtual threads monitoring added
1 parent 09773da commit 0e18a42

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

visualvm/applicationviews/src/org/graalvm/visualvm/application/views/monitor/ApplicationMonitorModel.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,19 @@
2828
import java.io.File;
2929
import java.io.FileInputStream;
3030
import java.io.FileOutputStream;
31+
import java.io.IOException;
3132
import java.io.InputStream;
3233
import java.io.OutputStream;
3334
import java.lang.management.MemoryMXBean;
3435
import java.util.ArrayList;
3536
import java.util.Collections;
3637
import java.util.List;
38+
import java.util.logging.Level;
39+
import java.util.logging.Logger;
40+
import javax.management.Attribute;
41+
import javax.management.MBeanServerConnection;
42+
import javax.management.MalformedObjectNameException;
43+
import javax.management.ObjectName;
3744
import javax.swing.SwingUtilities;
3845
import javax.swing.event.ChangeEvent;
3946
import javax.swing.event.ChangeListener;
@@ -62,6 +69,7 @@
6269
*/
6370
final class ApplicationMonitorModel {
6471

72+
private static final Logger LOGGER = Logger.getLogger(ApplicationMonitorModel.class.getName());
6573
private static final String PROP_PREFIX = "ApplicationMonitorModel_"; // NOI18N
6674

6775
static final String SNAPSHOT_VERSION = PROP_PREFIX + "version"; // NOI18N
@@ -82,6 +90,7 @@ final class ApplicationMonitorModel {
8290
public static final String PROP_MEMORY_MONITORING_SUPPORTED = PROP_PREFIX + "memory_monitoring_supported"; // NOI18N
8391
public static final String PROP_CLASS_MONITORING_SUPPORTED = PROP_PREFIX + "class_monitoring_supported"; // NOI18N
8492
public static final String PROP_THREADS_MONITORING_SUPPORTED = PROP_PREFIX + "threads_monitoring_supported"; // NOI18N
93+
public static final String PROP_VIRTUAL_THREADS_MONITORING_SUPPORTED = PROP_PREFIX + "virtual_threads_monitoring_supported"; // NOI18N
8594
public static final String PROP_NUMBER_OF_PROCESSORS = PROP_PREFIX + "number_of_processors"; // NOI18N
8695

8796
public static final String PROP_PROCESS_CPU_TIME = PROP_PREFIX + "process_cpu_time"; // NOI18N
@@ -104,12 +113,17 @@ final class ApplicationMonitorModel {
104113
public static final String PROP_DAEMON_THREADS = PROP_PREFIX + "daemon_threads"; // NOI18N
105114
public static final String PROP_PEAK_THREADS = PROP_PREFIX + "peak_threads"; // NOI18N
106115
public static final String PROP_STARTED_THREADS = PROP_PREFIX + "started_threads"; // NOI18N
116+
public static final String PROP_PARALLELISM = PROP_PREFIX + "parallelism"; // NOI18N
117+
public static final String PROP_POOL_SIZE = PROP_PREFIX + "pool_size"; // NOI18N
118+
public static final String PROP_MOUNTED_VIRTUAL_THREADS_COUNT = PROP_PREFIX + "mounted_virtual_thread_count"; // NOI18N
119+
public static final String PROP_QUEUED_VIRTUAL_THREAD_COUNT = PROP_PREFIX + "queued_virtual_thread_count"; // NOI18N
107120

108121
private static final String CPU_CHART_STORAGE = "monitor_cpu.dat"; // NOI18N
109122
private static final String HEAP_CHART_STORAGE = "monitor_heap.dat"; // NOI18N
110123
private static final String PERMGEN_CHART_STORAGE = "monitor_permgen.dat"; // NOI18N
111124
private static final String CLASSES_CHART_STORAGE = "monitor_classes.dat"; // NOI18N
112125
private static final String THREADS_CHART_STORAGE = "monitor_threads.dat"; // NOI18N
126+
private static final String VIRTUAL_THREADS_CHART_STORAGE = "monitor_vthreads.dat"; // NOI18N
113127

114128
private boolean initialized;
115129
private final DataSource source;
@@ -120,6 +134,8 @@ final class ApplicationMonitorModel {
120134
private Jvm jvm;
121135
private MemoryMXBean memoryMXBean;
122136
private MonitoredDataListener monitoredDataListener;
137+
private ObjectName virtualThreadsName;
138+
private MBeanServerConnection connection;
123139

124140
private int chartCache = -1;
125141

@@ -133,6 +149,7 @@ final class ApplicationMonitorModel {
133149
private boolean memoryMonitoringSupported = false;
134150
private boolean classMonitoringSupported = false;
135151
private boolean threadsMonitoringSupported = false;
152+
private boolean virtualThreadsMonitoringSupported = false;
136153
private int processorsCount = -1;
137154

138155
private long processCpuTime = -1;
@@ -155,12 +172,17 @@ final class ApplicationMonitorModel {
155172
private long daemonThreads = -1;
156173
private long peakThreads = -1;
157174
private long startedThreads = -1;
175+
private int parallelism = -1;
176+
private int poolSize = -1;
177+
private int mountedVirtualThreadCount = -1;
178+
private long queuedVirtualThreadCount = -1;
158179

159180
private SimpleXYChartSupport cpuChartSupport;
160181
private SimpleXYChartSupport heapChartSupport;
161182
private SimpleXYChartSupport permGenChartSupport;
162183
private SimpleXYChartSupport classesChartSupport;
163184
private SimpleXYChartSupport threadsChartSupport;
185+
private SimpleXYChartSupport virtualThreadsChartSupport;
164186

165187

166188
public static ApplicationMonitorModel create(Application application, boolean live) {
@@ -186,6 +208,7 @@ public static ApplicationMonitorModel create(Snapshot snapshot) {
186208
public boolean isMemoryMonitoringSupported() { return memoryMonitoringSupported; }
187209
public boolean isClassMonitoringSupported() { return classMonitoringSupported; }
188210
public boolean isThreadsMonitoringSupported() { return threadsMonitoringSupported; }
211+
public boolean isVirtualThreadsMonitoringSupported() { return virtualThreadsMonitoringSupported; }
189212
public int getProcessorsCount() { return processorsCount; }
190213

191214
public long getTimestamp() { return timestamp; }
@@ -212,6 +235,10 @@ public static ApplicationMonitorModel create(Snapshot snapshot) {
212235
public long getDeamonThreads() { return daemonThreads; }
213236
public long getPeakThreads() { return peakThreads; }
214237
public long getStartedThreads() { return startedThreads; }
238+
public int getParallelism() { return parallelism; }
239+
public int getPoolSize() { return poolSize; }
240+
public int getMountedVirtualThreadCount() { return mountedVirtualThreadCount; }
241+
public long getQueuedVirtualThreadCount() { return queuedVirtualThreadCount; }
215242

216243

217244
public synchronized void initialize() {
@@ -276,6 +303,17 @@ public void run() {
276303
});
277304
}
278305

306+
public void registerVirtualThreadsChartSupport(final SimpleXYChartSupport virtualThreadsChartSupport) {
307+
this.virtualThreadsChartSupport = virtualThreadsChartSupport;
308+
if (virtualThreadsChartSupport != null && source instanceof Snapshot)
309+
VisualVM.getInstance().runTask(new Runnable() {
310+
public void run() {
311+
File file = new File(source.getStorage().getDirectory(), VIRTUAL_THREADS_CHART_STORAGE);
312+
if (file.isFile()) loadChartSupport(virtualThreadsChartSupport, file);
313+
}
314+
});
315+
}
316+
279317
public synchronized void cleanup() {
280318
listeners.clear();
281319
if (!initialized) return;
@@ -308,6 +346,7 @@ public void save(Snapshot snapshot) {
308346
setProperty(storage, PROP_MEMORY_MONITORING_SUPPORTED, Boolean.toString(memoryMonitoringSupported));
309347
setProperty(storage, PROP_CLASS_MONITORING_SUPPORTED, Boolean.toString(classMonitoringSupported));
310348
setProperty(storage, PROP_THREADS_MONITORING_SUPPORTED, Boolean.toString(threadsMonitoringSupported));
349+
setProperty(storage, PROP_VIRTUAL_THREADS_MONITORING_SUPPORTED, Boolean.toString(virtualThreadsMonitoringSupported));
311350
setProperty(storage, PROP_NUMBER_OF_PROCESSORS, Integer.toString(processorsCount));
312351

313352
setProperty(storage, PROP_PROCESS_CPU_TIME, Long.toString(processCpuTime));
@@ -330,6 +369,10 @@ public void save(Snapshot snapshot) {
330369
setProperty(storage, PROP_DAEMON_THREADS, Long.toString(daemonThreads));
331370
setProperty(storage, PROP_PEAK_THREADS, Long.toString(peakThreads));
332371
setProperty(storage, PROP_STARTED_THREADS, Long.toString(startedThreads));
372+
setProperty(storage, PROP_PARALLELISM, Integer.toString(parallelism));
373+
setProperty(storage, PROP_POOL_SIZE, Integer.toString(poolSize));
374+
setProperty(storage, PROP_MOUNTED_VIRTUAL_THREADS_COUNT, Integer.toString(mountedVirtualThreadCount));
375+
setProperty(storage, PROP_QUEUED_VIRTUAL_THREAD_COUNT, Long.toString(queuedVirtualThreadCount));
333376

334377
File dir = storage.getDirectory();
335378

@@ -343,6 +386,8 @@ public void save(Snapshot snapshot) {
343386
saveChartSupport(classesChartSupport, new File(dir, CLASSES_CHART_STORAGE));
344387
if (threadsMonitoringSupported)
345388
saveChartSupport(threadsChartSupport, new File(dir, THREADS_CHART_STORAGE));
389+
if (virtualThreadsMonitoringSupported)
390+
saveChartSupport(virtualThreadsChartSupport, new File(dir, VIRTUAL_THREADS_CHART_STORAGE));
346391

347392
}
348393

@@ -378,6 +423,7 @@ private void initialize(Snapshot snapshot) {
378423
memoryMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_MEMORY_MONITORING_SUPPORTED));
379424
classMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_CLASS_MONITORING_SUPPORTED));
380425
threadsMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_THREADS_MONITORING_SUPPORTED));
426+
virtualThreadsMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_VIRTUAL_THREADS_MONITORING_SUPPORTED));
381427
processorsCount = Integer.parseInt(getProperty(storage, PROP_NUMBER_OF_PROCESSORS));
382428

383429
processCpuTime = Long.parseLong(getProperty(storage, PROP_PROCESS_CPU_TIME));
@@ -398,6 +444,10 @@ private void initialize(Snapshot snapshot) {
398444
daemonThreads = Long.parseLong(getProperty(storage, PROP_DAEMON_THREADS));
399445
peakThreads = Long.parseLong(getProperty(storage, PROP_PEAK_THREADS));
400446
startedThreads = Long.parseLong(getProperty(storage, PROP_STARTED_THREADS));
447+
parallelism = Integer.parseInt(getProperty(storage, PROP_PARALLELISM));
448+
poolSize = Integer.parseInt(getProperty(storage, PROP_POOL_SIZE));
449+
mountedVirtualThreadCount = Integer.parseInt(getProperty(storage, PROP_MOUNTED_VIRTUAL_THREADS_COUNT));
450+
queuedVirtualThreadCount = Long.parseLong(getProperty(storage, PROP_QUEUED_VIRTUAL_THREAD_COUNT));
401451

402452
if (version.compareTo("1.1") >= 0) { // NOI18N
403453
heapName = getProperty(storage, PROP_HEAP_NAME);
@@ -450,6 +500,13 @@ private void initialize(Application application) {
450500
if (mxbeans != null) {
451501
memoryMXBean = mxbeans.getMemoryMXBean();
452502
}
503+
virtualThreadsName = getVirtualThreadsName();
504+
try {
505+
virtualThreadsMonitoringSupported = jmxModel.getMBeanServerConnection().isRegistered(virtualThreadsName);
506+
if (virtualThreadsMonitoringSupported) connection = jmxModel.getMBeanServerConnection();
507+
} catch (IOException ex) {
508+
509+
}
453510
}
454511

455512
if (jvm != null) {
@@ -514,6 +571,17 @@ private void updateValues(final long time, final MonitoredData data) {
514571
peakThreads = data.getThreadsLivePeak();
515572
startedThreads = data.getThreadsStarted();
516573
}
574+
if (virtualThreadsMonitoringSupported) {
575+
Object[] attributes = getVirtualThreadAttributes("Parallelism", "PoolSize", "MountedVirtualThreadCount", "QueuedVirtualThreadCount");
576+
if (attributes != null) {
577+
parallelism = (Integer)attributes[0];
578+
poolSize = (Integer)attributes[1];
579+
mountedVirtualThreadCount = (Integer)attributes[2];
580+
queuedVirtualThreadCount = (Long)attributes[3];
581+
} else {
582+
virtualThreadsMonitoringSupported = false;
583+
}
584+
}
517585
}
518586
}
519587

@@ -533,4 +601,26 @@ private ApplicationMonitorModel(DataSource source, boolean live) {
533601
listeners = Collections.synchronizedList(new ArrayList<>());
534602
}
535603

604+
private Object[] getVirtualThreadAttributes(String... names) {
605+
try {
606+
List<Attribute> attrs = connection.getAttributes(virtualThreadsName, names).asList();
607+
Object[] values = new Object[attrs.size()];
608+
609+
for (int i = 0; i < values.length; i++) {
610+
values[i] = attrs.get(i).getValue();
611+
}
612+
return values;
613+
} catch (Exception ex) {
614+
LOGGER.log(Level.INFO, "getAttributes", ex); // NOI18N
615+
}
616+
return null;
617+
}
618+
619+
private static ObjectName getVirtualThreadsName() {
620+
try {
621+
return new ObjectName("jdk.management:type=VirtualThreadScheduler"); // NOI18N
622+
} catch (MalformedObjectNameException ex) {
623+
throw new RuntimeException(ex);
624+
}
625+
}
536626
}

visualvm/applicationviews/src/org/graalvm/visualvm/application/views/monitor/ApplicationMonitorView.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ protected DataViewComponent createComponent() {
116116
dvc.addDetailsView(classesViewSupport.getDetailsView(), DataViewComponent.BOTTOM_LEFT);
117117

118118
final ThreadsViewSupport threadsViewSupport = new ThreadsViewSupport(model);
119+
final VirtualThreadsViewSupport vThreadsViewSupport = model.isVirtualThreadsMonitoringSupported() ? new VirtualThreadsViewSupport(model) : null;
119120
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(NbBundle.
120121
getMessage(ApplicationMonitorView.class, "LBL_Threads"), true), DataViewComponent.BOTTOM_RIGHT); // NOI18N
121122
dvc.addDetailsView(threadsViewSupport.getDetailsView(), DataViewComponent.BOTTOM_RIGHT);
123+
if (vThreadsViewSupport != null) dvc.addDetailsView(vThreadsViewSupport.getDetailsView(), DataViewComponent.BOTTOM_RIGHT);
122124

123125
final Runnable refresher = new Runnable() {
124126
public void run() {
@@ -128,6 +130,7 @@ public void run() {
128130
if (permGenViewSupport != null) permGenViewSupport.refresh(model);
129131
classesViewSupport.refresh(model);
130132
threadsViewSupport.refresh(model);
133+
if (vThreadsViewSupport != null) vThreadsViewSupport.refresh(model);
131134
}
132135
};
133136

@@ -688,4 +691,72 @@ private void initComponents() {
688691

689692
}
690693

694+
// --- Virtual Threads -------------------------------------------------------------
695+
696+
private static class VirtualThreadsViewSupport extends JPanel {
697+
698+
private static final String PARALLELISM = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Parallelism"); // NOI18N
699+
private static final String POOL_SIZE = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Pool_size"); // NOI18N
700+
private static final String MOUNTED_VT_COUNT = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Mounted_virtual_thread_count"); // NOI18N
701+
private static final String MOUNTED_VT_COUNT_LEG = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Mounted_virtual_thread_count_leg"); // NOI18N
702+
private static final String QUEUED_VT_COUNT = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Queued_virtual_thread_count");// NOI18N
703+
private static final String QUEUED_VT_COUNT_LEG = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Queued_virtual_thread_count_leg");// NOI18N
704+
private static final String VIRTUAL_THREADS = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Virtual_Threads"); // NOI18N
705+
706+
private boolean liveModel;
707+
private boolean virtualThreadsMonitoringSupported;
708+
709+
private SimpleXYChartSupport chartSupport;
710+
711+
712+
VirtualThreadsViewSupport(ApplicationMonitorModel model) {
713+
initModels(model);
714+
initComponents();
715+
}
716+
717+
public DataViewComponent.DetailsView getDetailsView() {
718+
return new DataViewComponent.DetailsView(VIRTUAL_THREADS, null, 10, this, null);
719+
}
720+
721+
public void refresh(ApplicationMonitorModel model) {
722+
int parallelism = model.getParallelism();
723+
int poolSize = model.getPoolSize();
724+
int mountedVirtualThreadCount = model.getMountedVirtualThreadCount();
725+
long queuedVirtualThreadCount = model.getQueuedVirtualThreadCount();
726+
727+
if (liveModel)
728+
chartSupport.addValues(model.getTimestamp(), new long[] { mountedVirtualThreadCount, queuedVirtualThreadCount });
729+
chartSupport.updateDetails(new String[] { chartSupport.formatDecimal(parallelism),
730+
chartSupport.formatDecimal(poolSize),
731+
chartSupport.formatDecimal(mountedVirtualThreadCount),
732+
chartSupport.formatDecimal(queuedVirtualThreadCount) });
733+
}
734+
735+
private void initModels(ApplicationMonitorModel model) {
736+
liveModel = model.isLive();
737+
virtualThreadsMonitoringSupported = model.isVirtualThreadsMonitoringSupported();
738+
739+
if (virtualThreadsMonitoringSupported) {
740+
SimpleXYChartDescriptor chartDescriptor =
741+
SimpleXYChartDescriptor.decimal(3, false, model.getChartCache());
742+
743+
chartDescriptor.addLineItems(MOUNTED_VT_COUNT_LEG, QUEUED_VT_COUNT_LEG);
744+
chartDescriptor.setDetailsItems(new String[] { PARALLELISM, POOL_SIZE,
745+
MOUNTED_VT_COUNT, QUEUED_VT_COUNT });
746+
747+
chartSupport = ChartFactory.createSimpleXYChart(chartDescriptor);
748+
model.registerThreadsChartSupport(chartSupport);
749+
750+
chartSupport.setZoomingEnabled(!liveModel);
751+
}
752+
}
753+
754+
private void initComponents() {
755+
setLayout(new BorderLayout());
756+
setOpaque(false);
757+
758+
add(chartSupport.getChart(), BorderLayout.CENTER);
759+
chartSupport.updateDetails(new String[] { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN });
760+
}
761+
}
691762
}

visualvm/applicationviews/src/org/graalvm/visualvm/application/views/monitor/Bundle.properties

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,19 @@ LBL_Live_threads_leg=Live threads
9696

9797
LBL_Daemon_threads_leg=Daemon threads
9898

99+
LBL_Virtual_Threads=Virtual Threads
100+
101+
LBL_Parallelism=Parallelism
102+
103+
LBL_Pool_size=Pool size
104+
105+
LBL_Mounted_virtual_thread_count=Mounted VT count
106+
107+
LBL_Mounted_virtual_thread_count_leg=Mounted VT count
108+
109+
LBL_Queued_virtual_thread_count=Queued VT count
110+
111+
LBL_Queued_virtual_thread_count_leg=Queued VT count
112+
99113
FORMAT_hms={0} hrs {1} min {2} sec
100114
FORMAT_ms={0} min {1} sec

0 commit comments

Comments
 (0)