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