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