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