1 /* 2 * Copyright (c) 2020, 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 java.lang; 26 27 import java.io.PrintStream; 28 import java.security.AccessController; 29 import java.security.PrivilegedAction; 30 import java.util.LinkedHashMap; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Objects; 34 import java.util.Set; 35 import java.util.stream.Collectors; 36 import static java.lang.StackWalker.Option.*; 37 38 /** 39 * Helper class to print the virtual thread stack trace when pinned. 40 * 41 * The class maintains a ClassValue with the hashes of stack traces that are pinned by 42 * code in that Class. This is used to avoid printing the same stack trace many times. 43 */ 44 class PinnedThreadPrinter { 45 static final StackWalker STACK_WALKER; 46 static { 47 var options = Set.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE); 48 PrivilegedAction<StackWalker> pa = () -> 49 LiveStackFrame.getStackWalker(options, VirtualThread.continuationScope()); 50 @SuppressWarnings("removal") 51 var stackWalker = AccessController.doPrivileged(pa); 52 STACK_WALKER = stackWalker; 53 } 54 55 private static final ClassValue<Hashes> HASHES = new ClassValue<>() { 56 @Override 57 protected Hashes computeValue(Class<?> type) { 58 return new Hashes(); 59 } 60 }; 61 62 @SuppressWarnings("serial") 63 private static class Hashes extends LinkedHashMap<Integer, Boolean> { 64 boolean add(int hash) { 65 return (putIfAbsent(hash, Boolean.TRUE) == null); 66 } 67 @Override 68 protected boolean removeEldestEntry(Map.Entry<Integer, Boolean> oldest) { 69 // limit number of hashes 70 return size() > 8; 71 } 72 } 73 74 /** 75 * Returns a hash of the given stack trace. The hash is based on the class, 76 * method and bytecode index. 77 */ 78 private static int hash(List<LiveStackFrame> stack) { 79 int hash = 0; 80 for (LiveStackFrame frame : stack) { 81 hash = (31 * hash) + Objects.hash(frame.getDeclaringClass(), 82 frame.getMethodName(), 83 frame.getByteCodeIndex()); 84 } 85 return hash; 86 } 87 88 /** 89 * Prints the continuation stack trace. 90 * 91 * @param printAll true to print all stack frames, false to only print the 92 * frames that are native or holding a monitor 93 */ 94 static void printStackTrace(PrintStream out, boolean printAll) { 95 List<LiveStackFrame> stack = STACK_WALKER.walk(s -> 96 s.map(f -> (LiveStackFrame) f) 97 .filter(f -> f.getDeclaringClass() != PinnedThreadPrinter.class) 98 .collect(Collectors.toList()) 99 ); 100 101 // find the closest frame that is causing the thread to be pinned 102 stack.stream() 103 .filter(f -> (f.isNativeMethod() || f.getMonitors().length > 0)) 104 .map(LiveStackFrame::getDeclaringClass) 105 .findFirst() 106 .ifPresentOrElse(klass -> { 107 int hash = hash(stack); 108 Hashes hashes = HASHES.get(klass); 109 synchronized (hashes) { 110 // print the stack trace if not already seen 111 if (hashes.add(hash)) { 112 printStackTrace(stack, out, printAll); 113 } 114 } 115 }, () -> printStackTrace(stack, out, true)); // not found 116 } 117 118 private static void printStackTrace(List<LiveStackFrame> stack, 119 PrintStream out, 120 boolean printAll) { 121 out.println(Thread.currentThread()); 122 for (LiveStackFrame frame : stack) { 123 var ste = frame.toStackTraceElement(); 124 int monitorCount = frame.getMonitors().length; 125 if (monitorCount > 0) { 126 out.format(" %s <== monitors:%d%n", ste, monitorCount); 127 } else if (frame.isNativeMethod() || printAll) { 128 out.format(" %s%n", ste); 129 } 130 } 131 } 132 }