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