|
1 /* |
|
2 * Copyright (c) 2019, 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. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 /* |
|
25 * @test |
|
26 * @bug 8213397 |
|
27 * @summary Check that the thread dump shows when a thread is blocked |
|
28 * on a class initialization monitor |
|
29 * |
|
30 * @library /test/lib |
|
31 * @run main/othervm TestThreadDumpClassInitMonitor |
|
32 */ |
|
33 |
|
34 import jdk.test.lib.process.OutputAnalyzer; |
|
35 import jdk.test.lib.JDKToolFinder; |
|
36 |
|
37 import java.io.IOException; |
|
38 import java.util.List; |
|
39 |
|
40 public class TestThreadDumpClassInitMonitor { |
|
41 // jstack tends to be closely bound to the VM that we are running |
|
42 // so use getTestJDKTool() instead of getCompileJDKTool() or even |
|
43 // getJDKTool() which can fall back to "compile.jdk". |
|
44 final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack"); |
|
45 final static String PID = "" + ProcessHandle.current().pid(); |
|
46 |
|
47 final static Thread current = Thread.currentThread(); |
|
48 |
|
49 /* |
|
50 * This is the output we're looking for: |
|
51 * |
|
52 * "TestThread" #22 prio=5 os_prio=0 cpu=1.19ms elapsed=0.80s tid=0x00007f8f9405d800 nid=0x568b in Object.wait() [0x00007f8fd94d0000] |
|
53 * java.lang.Thread.State: RUNNABLE |
|
54 * Thread: 0x00007f8f9405d800 [0x568b] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0 |
|
55 * JavaThread state: _thread_blocked |
|
56 * at TestThreadDumpClassInitMonitor$Target$1.run(TestThreadDumpClassInitMonitor.java:69) |
|
57 * - waiting on the Class initialization monitor for TestThreadDumpClassInitMonitor$Target |
|
58 * |
|
59 */ |
|
60 final static String TEST_THREAD = "TestThread"; |
|
61 final static String TEST_THREAD_ENTRY = "\"" + TEST_THREAD; |
|
62 final static String IN_OBJECT_WAIT = "in Object.wait()"; |
|
63 final static String THREAD_STATE = "java.lang.Thread.State: RUNNABLE"; |
|
64 final static String THREAD_INFO = "Thread:"; // the details are not important |
|
65 final static String JAVATHREAD_STATE = "JavaThread state: _thread_blocked"; |
|
66 final static String CURRENT_METHOD = "at TestThreadDumpClassInitMonitor$Target$1.run"; |
|
67 final static String WAIT_INFO = "- waiting on the Class initialization monitor for TestThreadDumpClassInitMonitor$Target"; |
|
68 |
|
69 volatile static boolean ready = false; |
|
70 |
|
71 static List<String> stackDump; // jstack output as lines |
|
72 |
|
73 static class Target { |
|
74 |
|
75 static int field; |
|
76 |
|
77 // The main thread will initialize this class and so |
|
78 // execute the actual test logic here. |
|
79 static { |
|
80 if (Thread.currentThread() != current) { |
|
81 throw new Error("Initialization logic error"); |
|
82 } |
|
83 System.out.println("Initializing Target class in main thread"); |
|
84 |
|
85 Thread t = new Thread() { |
|
86 public void run() { |
|
87 System.out.println("Test thread about to access Target"); |
|
88 ready = true; // tell main thread we're close |
|
89 // This will block until the main thread completes |
|
90 // static initialization of target |
|
91 Target.field = 42; |
|
92 System.out.println("Test thread done"); |
|
93 } |
|
94 }; |
|
95 t.setName(TEST_THREAD); |
|
96 t.start(); |
|
97 |
|
98 // We want to run jstack once the test thread is blocked but |
|
99 // there's no programmatic way to detect that. So we check the flag |
|
100 // that will be set just before it should block, then by the time |
|
101 // we can exec jstack it should be ready. |
|
102 try { |
|
103 while (!ready) { |
|
104 Thread.sleep(200); |
|
105 } |
|
106 } |
|
107 catch (InterruptedException ie) { |
|
108 throw new Error("Shouldn't happen"); |
|
109 } |
|
110 |
|
111 // Now run jstack |
|
112 try { |
|
113 ProcessBuilder pb = new ProcessBuilder(JSTACK, PID); |
|
114 OutputAnalyzer output = new OutputAnalyzer(pb.start()); |
|
115 output.shouldHaveExitValue(0); |
|
116 stackDump = output.asLines(); |
|
117 } |
|
118 catch (IOException ioe) { |
|
119 throw new Error("Launching jstack failed", ioe); |
|
120 } |
|
121 } |
|
122 } |
|
123 |
|
124 |
|
125 public static void main(String[] args) throws Throwable { |
|
126 // Implicitly run the main test logic |
|
127 Target.field = 21; |
|
128 |
|
129 // Now check the output of jstack |
|
130 try { |
|
131 int foundLines = 0; |
|
132 parseStack: for (String line : stackDump) { |
|
133 switch(foundLines) { |
|
134 case 0: { |
|
135 if (!line.startsWith(TEST_THREAD_ENTRY)) { |
|
136 continue; |
|
137 } |
|
138 foundLines++; |
|
139 if (!line.contains(IN_OBJECT_WAIT)) { |
|
140 throw new Error("Unexpected initial stack line: " + line); |
|
141 } |
|
142 continue; |
|
143 } |
|
144 case 1: { |
|
145 if (!line.trim().equals(THREAD_STATE)) { |
|
146 throw new Error("Unexpected thread state line: " + line); |
|
147 } |
|
148 foundLines++; |
|
149 continue; |
|
150 } |
|
151 case 2: { |
|
152 if (!line.startsWith(THREAD_INFO)) { |
|
153 throw new Error("Unexpected thread info line: " + line); |
|
154 } |
|
155 foundLines++; |
|
156 continue; |
|
157 } |
|
158 case 3: { |
|
159 if (!line.trim().equals(JAVATHREAD_STATE)) { |
|
160 throw new Error("Unexpected JavaThread state line: " + line); |
|
161 } |
|
162 foundLines++; |
|
163 continue; |
|
164 } |
|
165 case 4: { |
|
166 if (!line.trim().startsWith(CURRENT_METHOD)) { |
|
167 throw new Error("Unexpected current method line: " + line); |
|
168 } |
|
169 foundLines++; |
|
170 continue; |
|
171 } |
|
172 case 5: { |
|
173 if (!line.trim().equals(WAIT_INFO)) { |
|
174 throw new Error("Unexpected monitor information line: " + line); |
|
175 } |
|
176 break parseStack; |
|
177 } |
|
178 default: throw new Error("Logic error in case statement"); |
|
179 } |
|
180 } |
|
181 |
|
182 if (foundLines == 0) { |
|
183 throw new Error("Unexpected stack content - did not find line starting with " |
|
184 + TEST_THREAD_ENTRY); |
|
185 } |
|
186 } |
|
187 catch (Error e) { |
|
188 // Dump the full stack trace on error so we can check the content |
|
189 for (String line : stackDump) { |
|
190 System.out.println(line); |
|
191 } |
|
192 throw e; |
|
193 } |
|
194 } |
|
195 } |