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 sun.nio.ch.Poller; 36 37 /** 38 * The implementation for the jcmd Thread.vthread_summary diagnostic command. 39 */ 40 public class VThreadSummary { 41 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); 42 43 // maximum number of thread containers to print 44 private static final int MAX_THREAD_CONTAINERS = 256; 45 46 // set to true if I/O poller in use 47 private static volatile boolean pollerInitialized; 48 49 private VThreadSummary() { } 50 51 /** 52 * Invoked by the poller I/O mechanism when it initializes. 53 */ 54 public static void pollerInitialized() { 55 pollerInitialized = true; 56 } 57 58 /** 59 * Invoked by the VM to print virtual thread summary information. 60 * @return the UTF-8 encoded information to print 61 */ 62 private static byte[] print() { 63 StringBuilder sb = new StringBuilder(); 64 65 // print thread containers (thread groupings) 66 new ThreadContainersPrinter(sb, MAX_THREAD_CONTAINERS).run(); 67 sb.append(System.lineSeparator()); 68 69 // print virtual thread scheduler 70 printSchedulerInfo(sb); 71 sb.append(System.lineSeparator()); 72 73 // print I/O pollers if initialized 74 if (pollerInitialized) { 75 printPollerInfo(sb); 76 } 77 78 return sb.toString().getBytes(StandardCharsets.UTF_8); 79 } 80 81 /** 82 * Prints the tree of thread containers starting from the root container. 83 */ 84 private static class ThreadContainersPrinter { 85 private final StringBuilder sb; 86 private final int max; 87 private int count; 88 89 ThreadContainersPrinter(StringBuilder sb, int max) { 90 this.sb = sb; 91 this.max = max; 92 } 93 94 void run() { 95 printThreadContainers(ThreadContainers.root(), 0); 96 } 97 98 /** 99 * Prints the given thread container and its children. 100 * @return true if the thread container and all its children were printed, 101 * false if the output was truncated because the max was reached 102 */ 103 private boolean printThreadContainers(ThreadContainer container, int depth) { 104 if (!printThreadContainer(container, depth)) { 105 return false; 106 } 107 boolean truncated = container.children() 108 .map(c -> printThreadContainers(c, depth + 1)) 109 .anyMatch(b -> b == false); 110 return !truncated; 111 } 112 113 /** 114 * Prints the given thread container or a "truncated" message if the maximum 115 * number of thread containers has already been printed. 116 * @param container the thread container 117 * @param depth the depth in the tree, for indentation purposes 118 * @return true if the thread container was printed, false if beyond max 119 */ 120 private boolean printThreadContainer(ThreadContainer container, int depth) { 121 count++; 122 if (count > max) { 123 sb.append("<truncated ...>") 124 .append(System.lineSeparator()); 125 return false; 126 } 127 128 Map<Boolean, Long> threadCounts = container.threads() 129 .collect(Collectors.partitioningBy(Thread::isVirtual, Collectors.counting())); 130 long platformThreadCount = threadCounts.get(Boolean.FALSE); 131 long virtualThreadCount = threadCounts.get(Boolean.TRUE); 132 if (depth > 0) { 133 int indent = depth * 4; 134 sb.append(" ".repeat(indent)).append("+-- "); 135 } 136 sb.append(container) 137 .append(" [platform threads = ") 138 .append(platformThreadCount) 139 .append(", virtual threads = ") 140 .append(virtualThreadCount) 141 .append("]") 142 .append(System.lineSeparator()); 143 144 return true; 145 } 146 } 147 148 /** 149 * Print information on the virtual thread schedulers to given string buffer. 150 */ 151 static void printSchedulerInfo(StringBuilder sb) { 152 sb.append("Default virtual thread scheduler:") 153 .append(System.lineSeparator()); 154 sb.append(JLA.virtualThreadDefaultScheduler()) 155 .append(System.lineSeparator()); 156 157 sb.append(System.lineSeparator()); 158 159 sb.append("Timeout schedulers:") 160 .append(System.lineSeparator()); 161 var schedulers = JLA.virtualThreadDelayedTaskSchedulers().toList(); 162 for (int i = 0; i < schedulers.size(); i++) { 163 sb.append('[') 164 .append(i) 165 .append("] ") 166 .append(schedulers.get(i)) 167 .append(System.lineSeparator()); 168 } 169 } 170 171 /** 172 * Print information on threads registered for I/O to the given string buffer. 173 */ 174 private static void printPollerInfo(StringBuilder sb) { 175 Poller masterPoller = Poller.masterPoller(); 176 List<Poller> readPollers = Poller.readPollers(); 177 List<Poller> writePollers = Poller.writePollers(); 178 179 if (masterPoller != null) { 180 sb.append("Master I/O poller:") 181 .append(System.lineSeparator()) 182 .append(masterPoller) 183 .append(System.lineSeparator()); 184 185 sb.append(System.lineSeparator()); 186 } 187 188 sb.append("Read I/O pollers:"); 189 sb.append(System.lineSeparator()); 190 IntStream.range(0, readPollers.size()) 191 .forEach(i -> sb.append('[') 192 .append(i) 193 .append("] ") 194 .append(readPollers.get(i)) 195 .append(System.lineSeparator())); 196 197 sb.append(System.lineSeparator()); 198 199 sb.append("Write I/O pollers:"); 200 sb.append(System.lineSeparator()); 201 IntStream.range(0, writePollers.size()) 202 .forEach(i -> sb.append('[') 203 .append(i) 204 .append("] ") 205 .append(writePollers.get(i)) 206 .append(System.lineSeparator())); 207 } 208 }