1 /* 2 * Copyright (c) 2015, 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 * @summary Call Object.wait() method. Check that monitor information 27 * presented in the stack is correct. Call notifyAll method 28 * monitor info have to disappear from the stack. 29 * Repeats the same scenario calling interrupt() method 30 * @modules java.base/jdk.internal.misc 31 * @library /test/lib 32 * @library ../share 33 * @run main/othervm -XX:+UsePerfData WaitNotifyThreadTest 34 */ 35 import common.ToolResults; 36 import java.util.Iterator; 37 import utils.*; 38 39 public class WaitNotifyThreadTest { 40 41 private Object monitor = new Object(); 42 private final String OBJECT = "a java.lang.Object"; 43 private final String OBJECT_WAIT = "java.lang.Object.wait"; 44 45 interface Action { 46 47 void doAction(Thread thread); 48 } 49 50 class ActionNotify implements Action { 51 52 @Override 53 public void doAction(Thread thread) { 54 //Notify the waiting thread, so it stops waiting and sleeps 55 synchronized (monitor) { 56 monitor.notifyAll(); 57 } 58 } 59 } 60 61 class ActionInterrupt implements Action { 62 63 @Override 64 public void doAction(Thread thread) { 65 // Interrupt the thread 66 thread.interrupt(); 67 } 68 } 69 70 class WaitThread extends Thread { 71 72 @Override 73 public void run() { 74 try { 75 synchronized (monitor) { 76 monitor.wait(); 77 } 78 } catch (InterruptedException x) { 79 80 } 81 Utils.sleep(); 82 } 83 } 84 85 public static void main(String[] args) throws Exception { 86 new WaitNotifyThreadTest().doTest(); 87 } 88 89 private void doTest() throws Exception { 90 91 // Verify stack trace consistency when notifying the thread 92 doTest(new ActionNotify()); 93 94 // Verify stack trace consistency when interrupting the thread 95 doTest(new ActionInterrupt()); 96 } 97 98 private void doTest(Action action) throws Exception { 99 100 final String WAITING_THREAD_NAME = "MyWaitingThread"; 101 102 // Start athread that just waits 103 WaitThread waitThread = new WaitThread(); 104 waitThread.setName(WAITING_THREAD_NAME); 105 waitThread.start(); 106 107 // Collect output from the jstack tool 108 JstackTool jstackTool = new JstackTool(ProcessHandle.current().getPid()); 109 ToolResults results = jstackTool.measure(); 110 111 // Analyze the jstack output for the patterns needed 112 JStack jstack1 = new DefaultFormat().parse(results.getStdoutString()); 113 ThreadStack ti1 = jstack1.getThreadStack(WAITING_THREAD_NAME); 114 analyzeThreadStackWaiting(ti1); 115 116 action.doAction(waitThread); 117 118 // Collect output from the jstack tool again 119 results = jstackTool.measure(); 120 121 // Analyze the output again 122 JStack jstack2 = new DefaultFormat().parse(results.getStdoutString()); 123 ThreadStack ti2 = jstack2.getThreadStack(WAITING_THREAD_NAME); 124 analyzeThreadStackNoWaiting(ti2); 125 126 } 127 128 private void analyzeThreadStackWaiting(ThreadStack ti1) { 129 Iterator<MethodInfo> it = ti1.getStack().iterator(); 130 131 String monitorAddress = null; 132 while (it.hasNext()) { 133 MethodInfo mi = it.next(); 134 if (mi.getName().startsWith(OBJECT_WAIT) && mi.getCompilationUnit() == null /*native method*/) { 135 if (mi.getLocks().size() == 1) { 136 MonitorInfo monInfo = mi.getLocks().getFirst(); 137 if (monInfo.getType().equals("waiting on") && compareMonitorClass(monInfo)) { 138 monitorAddress = monInfo.getMonitorAddress(); 139 } else { 140 System.err.println("Error: incorrect monitor info: " + monInfo.getType() + ", " + monInfo.getMonitorClass()); 141 throw new RuntimeException("Incorrect lock record in " 142 + OBJECT_WAIT + " method"); 143 } 144 145 } else { 146 throw new RuntimeException(OBJECT_WAIT 147 + " method has to contain one lock record bu it contains " + mi.getLocks().size()); 148 } 149 } 150 151 if (mi.getName().startsWith("WaitThread.run")) { 152 if (monitorAddress == null) { 153 throw new RuntimeException("Cannot found monitor info associated with " + OBJECT_WAIT + " method"); 154 } 155 156 int numLocks = mi.getLocks().size(); 157 for (int i = 0; i < numLocks - 1; ++i) { 158 assertMonitorInfo("waiting to re-lock in wait()", mi.getLocks().get(i), monitorAddress); 159 } 160 assertMonitorInfo("locked", mi.getLocks().getLast(), monitorAddress); 161 } 162 } 163 164 } 165 166 private void assertMonitorInfo(String expectedMessage, MonitorInfo monInfo, String monitorAddress) { 167 if (monInfo.getType().equals(expectedMessage) 168 && compareMonitorClass(monInfo) 169 && monInfo.getMonitorAddress().equals( 170 monitorAddress)) { 171 System.out.println("Correct monitor info found"); 172 } else { 173 System.err.println("Error: incorrect monitor info: " + monInfo.getType() + ", " + monInfo.getMonitorClass() + ", " + monInfo.getMonitorAddress()); 174 System.err.println("Expected: " + expectedMessage + ", a java.lang.Object, " + monitorAddress); 175 throw new RuntimeException("Incorrect lock record in 'run' method"); 176 } 177 } 178 179 private boolean compareMonitorClass(MonitorInfo monInfo) { 180 // If monitor class info is present in the jstack output 181 // then compare it with the class of the actual monitor object 182 // If there is no monitor class info available then return true 183 return OBJECT.equals(monInfo.getMonitorClass()) || (monInfo.getMonitorClass() == null); 184 } 185 186 private void analyzeThreadStackNoWaiting(ThreadStack ti2) { 187 Iterator<MethodInfo> it = ti2.getStack().iterator(); 188 189 while (it.hasNext()) { 190 MethodInfo mi = it.next(); 191 if (mi.getLocks().size() != 0) { 192 throw new RuntimeException("Unexpected lock record in " 193 + mi.getName() + " method"); 194 } 195 } 196 } 197 198 }