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