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