1 /* 2 * Copyright (c) 2024, 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 jdk.internal.vm; 26 27 import java.nio.charset.StandardCharsets; 28 import java.util.Iterator; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.stream.Collectors; 32 import java.util.stream.IntStream; 33 import jdk.internal.access.JavaLangAccess; 34 import jdk.internal.access.SharedSecrets; 35 import jdk.internal.misc.Unsafe; 36 import sun.nio.ch.Poller; 37 38 /** 39 * The implementation for the jcmd Thread.vthread_summary diagnostic command. 40 */ 41 public class VThreadSummary { 42 private static final Unsafe U = Unsafe.getUnsafe(); 43 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); 44 45 // maximum number of thread containers to print 46 private static final int MAX_THREAD_CONTAINERS = 256; 47 48 private VThreadSummary() { } 49 50 /** 51 * Invoked by the VM to print virtual thread summary information. 52 * @return the UTF-8 encoded information to print 53 */ 54 private static byte[] print() { 55 StringBuilder sb = new StringBuilder(); 56 57 // print virtual thread scheduler 58 printSchedulers(sb); 59 sb.append(System.lineSeparator()); 60 61 // print I/O pollers if initialized 62 if (!U.shouldBeInitialized(Poller.class)) { 63 printPollers(sb); 64 sb.append(System.lineSeparator()); 65 } 66 67 // print thread groupings/containers 68 printThreadContainers(sb); 69 sb.append(System.lineSeparator()); 70 71 return sb.toString().getBytes(StandardCharsets.UTF_8); 72 } 73 74 /** 75 * Print information on the virtual thread schedulers to given string builder. 76 */ 77 private static void printSchedulers(StringBuilder sb) { 78 sb.append("Virtual thread scheduler:") 79 .append(System.lineSeparator()); 80 sb.append(JLA.virtualThreadDefaultScheduler()) 81 .append(System.lineSeparator()); 82 83 sb.append(System.lineSeparator()); 84 85 sb.append("Timeout schedulers:") 86 .append(System.lineSeparator()); 87 var schedulers = JLA.virtualThreadDelayedTaskSchedulers().toList(); 88 for (int i = 0; i < schedulers.size(); i++) { 89 sb.append('[') 90 .append(i) 91 .append("] ") 92 .append(schedulers.get(i)) 93 .append(System.lineSeparator()); 94 } 95 } 96 97 /** 98 * Print information on threads registered for I/O to the given string builder. 99 */ 100 private static void printPollers(StringBuilder sb) { 101 Poller masterPoller = Poller.masterPoller(); 102 List<Poller> readPollers = Poller.readPollers(); 103 List<Poller> writePollers = Poller.writePollers(); 104 105 if (masterPoller != null) { 106 sb.append("Master I/O poller:") 107 .append(System.lineSeparator()) 108 .append(masterPoller) 109 .append(System.lineSeparator()); 110 sb.append(System.lineSeparator()); 111 } 112 113 sb.append("Read I/O pollers:"); 114 sb.append(System.lineSeparator()); 115 IntStream.range(0, readPollers.size()) 116 .forEach(i -> sb.append('[') 117 .append(i) 118 .append("] ") 119 .append(readPollers.get(i)) 120 .append(System.lineSeparator())); 121 sb.append(System.lineSeparator()); 122 123 sb.append("Write I/O pollers:"); 124 sb.append(System.lineSeparator()); 125 IntStream.range(0, writePollers.size()) 126 .forEach(i -> sb.append('[') 127 .append(i) 128 .append("] ") 129 .append(writePollers.get(i)) 130 .append(System.lineSeparator())); 131 } 132 133 /** 134 * Print the thread containers that don't have an owner to the given string builder. 135 * The output will include the root container and all thread pools. 136 * 137 * In the future, this could be extended to support structured concurrency so that 138 * it prints a tree of owned thread containers. 139 */ 140 private static void printThreadContainers(StringBuilder sb) { 141 sb.append("Thread groupings:") 142 .append(System.lineSeparator()); 143 144 ThreadContainer root = ThreadContainers.root(); 145 printThreadContainer(root, sb); 146 147 int printed = 1; 148 Iterator<ThreadContainer> iterator = root.children().iterator(); 149 while (iterator.hasNext() && printed < MAX_THREAD_CONTAINERS) { 150 ThreadContainer container = iterator.next(); 151 if (container.owner() == null) { 152 printThreadContainer(container, sb); 153 printed++; 154 } 155 } 156 if (iterator.hasNext()) { 157 sb.append("<truncated ...>") 158 .append(System.lineSeparator()); 159 } 160 } 161 162 /** 163 * Print a thread container to the given string builder. 164 */ 165 private static void printThreadContainer(ThreadContainer container, StringBuilder sb) { 166 Map<Boolean, Long> threadCounts = container.threads() 167 .collect(Collectors.partitioningBy(Thread::isVirtual, Collectors.counting())); 168 long platformThreadCount = threadCounts.get(Boolean.FALSE); 169 long virtualThreadCount = threadCounts.get(Boolean.TRUE); 170 sb.append(container) 171 .append(" [platform threads = ") 172 .append(platformThreadCount) 173 .append(", virtual threads = ") 174 .append(virtualThreadCount) 175 .append("]") 176 .append(System.lineSeparator()); 177 } 178 }