1 /* 2 * Copyright (c) 2006, 2016, 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 6293795 27 * @summary Backend hangs when invokeMethod is called from a JDI eventHandler 28 * @author jjh 29 * 30 * @library /test/lib 31 * @modules java.management 32 * jdk.jdi 33 * 34 * @run build TestScaffold VMConnection TargetListener TargetAdapter 35 * @run compile -g InvokeHangTest.java 36 * @run driver InvokeHangTest 37 */ 38 import com.sun.jdi.*; 39 import com.sun.jdi.event.*; 40 import com.sun.jdi.request.*; 41 42 import java.util.*; 43 44 /* 45 * This debuggee basically runs two threads each of 46 * which loop, hitting a bkpt in each iteration. 47 * 48 */ 49 class InvokeHangTarg extends Thread { 50 static boolean one = false; 51 static String name1 = "Thread 1"; 52 static String name2 = "Thread 2"; 53 static int count = 100; 54 55 public static void main(String[] args) { 56 System.out.println("Howdy!"); 57 InvokeHangTarg t1 = new InvokeHangTarg(name1); 58 InvokeHangTarg t2 = new InvokeHangTarg(name2); 59 60 t1.start(); 61 t2.start(); 62 } 63 64 // This is called from the debugger via invokeMethod 65 public double invokeee() { 66 System.out.println("Debuggee: invokeee in thread "+Thread.currentThread().toString()); 67 Thread.yield(); 68 return longMethod(2); 69 } 70 public double longMethod(int n) { 71 double a = 0; 72 double s = 0; 73 for (int i = 0; i < n; i++) { 74 a += i; 75 for (int j = -1000*i; j < 1000*i; j++) { 76 a = a*(1 + i/(j + 0.5)); 77 s += Math.sin(a); 78 } 79 } 80 System.out.println("Debuggee: invokeee finished"); 81 return s; 82 } 83 84 public InvokeHangTarg(String name) { 85 super(name); 86 } 87 88 public void run() { 89 if (getName().equals(name1)) { 90 run1(); 91 } else { 92 run2(); 93 } 94 } 95 96 public void bkpt1(int i) { 97 System.out.println("Debuggee: " + Thread.currentThread() +" is running:" + i); 98 try { 99 Thread.currentThread().sleep(2); 100 } catch (InterruptedException iex) {} 101 //yield(); 102 } 103 104 public void run1() { 105 int i = 0; 106 while (i < count) { 107 i++; 108 bkpt1(i); 109 } 110 } 111 112 public void bkpt2(int i) { 113 System.out.println("Debuggee: " + Thread.currentThread() +" is running:" + i); 114 try { 115 Thread.currentThread().sleep(2); 116 } catch (InterruptedException iex) {} 117 //yield(); 118 } 119 120 public void run2() { 121 int i = 0; 122 while (i < count) { 123 i++; 124 bkpt2(i); 125 } 126 } 127 } 128 129 /********** test program **********/ 130 131 public class InvokeHangTest extends TestScaffold { 132 ReferenceType targetClass; 133 ThreadReference mainThread; 134 BreakpointRequest request1; 135 BreakpointRequest request2; 136 static volatile int bkpts = 0; 137 Thread timerThread; 138 static long waitTime = jdk.test.lib.Utils.adjustTimeout(20000); 139 140 InvokeHangTest (String args[]) { 141 super(args); 142 } 143 144 public static void main(String[] args) throws Exception { 145 new InvokeHangTest(args).startTests(); 146 } 147 148 void doInvoke(ThreadReference thread, ObjectReference ref, String methodName) { 149 List methods = ref.referenceType().methodsByName(methodName); 150 Method method = (Method) methods.get(0); 151 try { 152 System.err.println(" Debugger: Invoking in thread" + thread); 153 ref.invokeMethod(thread, method, new ArrayList(), ref.INVOKE_NONVIRTUAL); 154 System.err.println(" Debugger: Invoke done"); 155 } catch (Exception ex) { 156 ex.printStackTrace(); 157 failure("failure: Exception"); 158 } 159 } 160 161 // BreakpointEvent handler 162 public void breakpointReached(BreakpointEvent event) { 163 if (bkpts == 0) { 164 /* 165 * This thread will watch for n secs to go by with no 166 * calls to this method. 167 */ 168 timerThread.start(); 169 } 170 171 synchronized("abc") { 172 /* 173 * Note that this will most likely never get to 174 * the number of times the two bkpt lines in the debuggee 175 * are hit because bkpts are lost while they are disabled. 176 */ 177 bkpts++; 178 } 179 180 /* 181 * The bug occurs when the requests are disabled 182 * and then an invoke is done in the event handler. In some cases 183 * the other thread has hit a bkpt and the back-end is waiting 184 * to send it. When the back-end resumes the debuggee to do the 185 * invokeMethod, this 2nd bkpt is released, the debuggee is suspended, including 186 * the thread on which the invoke was done (because it is a SUSPEND_ALL bkpt), 187 * the bkpt is sent to the front-end, but the client event handler is sitting 188 * here waiting for the invoke to finish, so it doesn't get the 2nd bkpt and 189 * do the resume for it. Thus, the debuggee is suspended waiting for a resume 190 * that never comes. 191 */ 192 request1.disable(); 193 request2.disable(); 194 195 ThreadReference thread = event.thread(); 196 try { 197 StackFrame sf = thread.frame(0); 198 System.err.println(" Debugger: Breakpoint hit at "+sf.location()); 199 doInvoke(thread, sf.thisObject(), "invokeee"); 200 } catch (IncompatibleThreadStateException itsex) { 201 itsex.printStackTrace(); 202 failure("failure: Exception"); 203 } 204 request1.enable(); 205 request2.enable(); 206 207 } 208 209 /********** test core **********/ 210 211 protected void runTests() throws Exception { 212 213 /* 214 * Get to the top of main() 215 * to determine targetClass and mainThread 216 */ 217 BreakpointEvent bpe = startToMain("InvokeHangTarg"); 218 targetClass = bpe.location().declaringType(); 219 mainThread = bpe.thread(); 220 EventRequestManager erm = vm().eventRequestManager(); 221 final Thread mainThread = Thread.currentThread(); 222 223 /* 224 * Set event requests 225 */ 226 Location loc1 = findMethod(targetClass, "bkpt1", "(I)V").location(); 227 Location loc2 = findMethod(targetClass, "bkpt2", "(I)V").location(); 228 request1 = erm.createBreakpointRequest(loc1); 229 request2 = erm.createBreakpointRequest(loc2); 230 request1.enable(); 231 request2.enable(); 232 233 /* 234 * This thread will be started when we get the first bkpt. 235 * (Which we always expect to get). 236 * It awakens every n seconds and checks to see if we 237 * got any breakpoint events while it was asleep. If not, then 238 * we assume the debuggee is hung and fail the test. 239 */ 240 timerThread = new Thread("test timer") { 241 public void run() { 242 int myBkpts = bkpts; 243 while (true) { 244 try { 245 Thread.sleep(waitTime); 246 System.out.println("bkpts = " + bkpts); 247 if (myBkpts == bkpts) { 248 // no bkpt for 'waitTime' msecs 249 failure("failure: Debuggee appears to be hung"); 250 vmDisconnected = true; 251 // This awakens the main thread which is 252 // waiting for a VMDisconnect. 253 mainThread.interrupt(); 254 break; 255 } 256 myBkpts = bkpts; 257 } catch (InterruptedException ee) { 258 // If the test completes, this occurs. 259 println("timer Interrupted"); 260 break; 261 } 262 } 263 } 264 }; 265 266 /* 267 * resume the target, listening for events 268 */ 269 listenUntilVMDisconnect(); 270 timerThread.interrupt(); 271 /* 272 * deal with results of test 273 * if anything has called failure("foo") testFailed will be true 274 */ 275 if (!testFailed) { 276 println("InvokeHangTest: passed; bkpts = " + bkpts); 277 } else { 278 throw new Exception("InvokeHangTest: failed; bkpts = " + bkpts); 279 } 280 } 281 }