1 /* 2 * Copyright (c) 2005, 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 6296125 27 * @summary JDI: Disabling an EventRequest can cause a multi-threaded debuggee to hang 28 * 29 * @author jjh 30 * 31 * @modules jdk.jdi 32 * @run build TestScaffold VMConnection TargetListener TargetAdapter 33 * @run compile -g TwoThreadsTest.java 34 * @run driver TwoThreadsTest 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 TwoThreadsTarg 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 TwoThreadsTarg t1 = new TwoThreadsTarg(name1); 56 TwoThreadsTarg t2 = new TwoThreadsTarg(name2); 57 58 t1.start(); 59 t2.start(); 60 } 61 62 public TwoThreadsTarg(String name) { 63 super(name); 64 } 65 66 public void run() { 67 if (getName().equals(name1)) { 68 run1(); 69 } else { 70 run2(); 71 } 72 } 73 74 public void bkpt1(int i) { 75 yield(); 76 } 77 78 public void run1() { 79 int i = 0; 80 while (i < count) { 81 i++; 82 bkpt1(i); 83 } 84 } 85 86 public void bkpt2(int i) { 87 yield(); 88 } 89 90 public void run2() { 91 int i = 0; 92 while (i < count) { 93 i++; 94 bkpt2(i); 95 } 96 } 97 } 98 99 /********** test program **********/ 100 101 public class TwoThreadsTest extends TestScaffold { 102 ReferenceType targetClass; 103 ThreadReference mainThread; 104 BreakpointRequest request1; 105 BreakpointRequest request2; 106 static volatile int bkpts = 0; 107 Thread timerThread; 108 static int waitTime = 20000; 109 110 TwoThreadsTest (String args[]) { 111 super(args); 112 } 113 114 public static void main(String[] args) throws Exception { 115 new TwoThreadsTest(args).startTests(); 116 } 117 118 /* BreakpointEvent handler */ 119 120 public void breakpointReached(BreakpointEvent event) { 121 if (bkpts == 0) { 122 /* 123 * This thread will watch for n secs to go by with no 124 * calls to this method. 125 */ 126 timerThread.start(); 127 } 128 129 synchronized("abc") { 130 /* 131 * Note that this will most likely never get to 132 * the number of times the two bkpt lines in the debuggee 133 * are hit because bkpts are lost while they are disabled. 134 */ 135 bkpts++; 136 } 137 138 /* 139 * The bug occurs when the requests are disabled 140 * and then re-enabled during the event handler. 141 */ 142 request1.disable(); 143 request2.disable(); 144 145 /* 146 * This code between the disables and enables 147 * is just filler that leaves the requests disabled 148 * for awhile. I suppose a sleep could be used instead 149 */ 150 Method mmm = event.location().method(); 151 List lvlist; 152 try { 153 lvlist = mmm.variablesByName("i"); 154 } catch (AbsentInformationException ee) { 155 failure("FAILED: can't get local var i"); 156 return; 157 } 158 LocalVariable ivar = (LocalVariable)lvlist.get(0); 159 160 ThreadReference thr = event.thread(); 161 StackFrame sf; 162 try { 163 sf = thr.frame(0); 164 } catch (IncompatibleThreadStateException ee) { 165 failure("FAILED: bad thread state"); 166 return; 167 } 168 Value ival = sf.getValue(ivar); 169 println("Got bkpt at: " + event.location() + ", i = " + ival); 170 request1.enable(); 171 request2.enable(); 172 173 } 174 175 /********** test core **********/ 176 177 protected void runTests() throws Exception { 178 179 /* 180 * Get to the top of main() 181 * to determine targetClass and mainThread 182 */ 183 BreakpointEvent bpe = startToMain("TwoThreadsTarg"); 184 targetClass = bpe.location().declaringType(); 185 mainThread = bpe.thread(); 186 EventRequestManager erm = vm().eventRequestManager(); 187 final Thread mainThread = Thread.currentThread(); 188 189 /* 190 * Set event requests 191 */ 192 Location loc1 = findMethod(targetClass, "bkpt1", "(I)V").location(); 193 Location loc2 = findMethod(targetClass, "bkpt2", "(I)V").location(); 194 request1 = erm.createBreakpointRequest(loc1); 195 request2 = erm.createBreakpointRequest(loc2); 196 request1.enable(); 197 request2.enable(); 198 199 /* 200 * This thread will be started when we get the first bkpt. 201 * (Which we always expect to get). 202 * It awakens every n seconds and checks to see if we 203 * got any breakpoint events while it was asleep. 204 */ 205 timerThread = new Thread("test timer") { 206 public void run() { 207 int myBkpts = bkpts; 208 while (true) { 209 try { 210 Thread.sleep(waitTime); 211 System.out.println("bkpts = " + bkpts); 212 if (myBkpts == bkpts) { 213 // no bkpt for 'waitTime' secs 214 failure("failure: Debuggee appears to be hung"); 215 vmDisconnected = true; 216 // This awakens the main thread which is 217 // waiting for a VMDisconnect. 218 mainThread.interrupt(); 219 break; 220 } 221 myBkpts = bkpts; 222 } catch (InterruptedException ee) { 223 // If the test completes, this occurs. 224 println("timer Interrupted"); 225 break; 226 } 227 } 228 } 229 }; 230 231 /* 232 * resume the target, listening for events 233 */ 234 listenUntilVMDisconnect(); 235 timerThread.interrupt(); 236 /* 237 * deal with results of test 238 * if anything has called failure("foo") testFailed will be true 239 */ 240 if (!testFailed) { 241 println("TwoThreadsTest: passed; bkpts = " + bkpts); 242 } else { 243 throw new Exception("TwoThreadsTest: failed; bkpts = " + bkpts); 244 } 245 } 246 }