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 }