File: InterruptHangTest.java

package info (click to toggle)
openjdk-25 25.0.1%2B8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 825,408 kB
  • sloc: java: 5,585,680; cpp: 1,333,948; xml: 1,321,242; ansic: 488,034; asm: 404,003; objc: 21,088; sh: 15,106; javascript: 13,265; python: 8,319; makefile: 2,518; perl: 357; awk: 351; pascal: 103; exp: 83; sed: 72; jsp: 24
file content (339 lines) | stat: -rw-r--r-- 13,663 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*
 * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import com.sun.jdi.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;

/**
 * @test
 * @bug 6459476
 * @summary Test interrupting debuggee with single stepping enable
 * @author jjh
 *
 * @run build TestScaffold VMConnection TargetListener TargetAdapter
 * @run compile -g InterruptHangTest.java
 * @run driver InterruptHangTest precise
 * @run driver InterruptHangTest aggressive
 * @run driver InterruptHangTest remote
 */

/**
 * The debuggee loops in the main thread while the debugger has single stepping
 * enabled for the debuggee's main thread. The main thread is repeatedly
 * interrupted while it is looping. If a long time goes by with the debugger
 * not getting a single step event, the test fails.
 *
 * Interrupts are generated in 3 difference modes:
 *   precise - The debuggee creates a 2nd thread that repeatedly calls
 *       Thread.interrupt() on the main thread, but does so in a controlled
 *       fashion that allows to test to verify that every interrupt is
 *       received and handled.
 *   aggressive - The debuggee creates a 2nd thread that repeatedly calls
 *       Thread.interrupt() on the main thread, but does so at a high pace
 *       and without any coordination with the main thread. Because of
 *       this it is not possible to account for all the interrupts since some
 *       might be done before the previous interrupt is handled.
 *   remote - Much like the "aggressive" mode, except interrupts are remotely
 *       generated by the debugger using ThreadReference.interrupt(). There
 *       is no "precise" mode for remotely generated interrupts since it is
 *       difficult to coordinate between the debugger and debuggee in a
 *       reasonable way.
 */

class InterruptHangTarg {
    static String sync = "sync";
    static int interruptsSent;
    static boolean remoteMode;
    static boolean preciseMode;
    static boolean aggressiveMode;

    private final static int INTERRUPTS_EXPECTED = 200;

    public static void main(String[] args){
        int answer = 0; // number of interrupts answered

        System.out.println("Howdy!");

        remoteMode = "remote".equals(args[0]);
        preciseMode = "precise".equals(args[0]);
        aggressiveMode = "aggressive".equals(args[0]);

        if (!remoteMode && !preciseMode && !aggressiveMode) {
            throw new RuntimeException("Invalid first arg for debuggee: " + args[0]);
        }

        // Create the debuggee interruptor thread if needed.
        Thread interruptorThread;
        if (preciseMode) {
            interruptorThread =
                DebuggeeWrapper.newThread(new PreciseInterruptor(Thread.currentThread()));
            interruptorThread.start();
        } else if (aggressiveMode) {
            interruptorThread =
                DebuggeeWrapper.newThread(new AggressiveInterruptor(Thread.currentThread()));
            interruptorThread.start();
        } else {
            interruptorThread = null; // we are in "remote" interrupt mode
        }

        // Debugger will keep stepping thru this loop
        for (int ii = 0; ii < INTERRUPTS_EXPECTED; ii++) {
            boolean wasInterrupted = false;
            try {
                // Give other thread a chance to interrupt. Normally only a very short
                // sleep is needed, but we need to account for unexpected delays in
                // the interrupt due to machine and network hiccups.
                Thread.sleep(10*1000);
            } catch (InterruptedException ee) {
                answer++;
                wasInterrupted = true;
                boolean isInterrupted = Thread.currentThread().isInterrupted();
                System.out.println("Debuggee interruptee(" + ii + "): isInterrupted(" + isInterrupted + ")");
                if (preciseMode) {
                    // When Thread.sleep() is interrupted, the interrupted status of the thread
                    // should remain cleared (since InterruptedException was thrown), and the
                    // the next interrupt won't come in until after the sync.notify() below.
                    // Note this is not true for the aggressive and remote modes since
                    // interrupts can come in at any time, even while we are handling
                    // an intrrupt.
                    if (isInterrupted) {
                        throw new RuntimeException("Thread should not have interrupted status set.");
                    }
                    synchronized(InterruptHangTarg.sync) {
                        // Let the interruptor thread know it can start interrupting again
                        sync.notify();
                    }
                }
            }
            // Every Thread.sleep() call should get interrupted
            if (!wasInterrupted) {
                throw new RuntimeException("Thread was never interrupted during sleep: " + ii);
            }
        }

        if (interruptorThread != null) {
            synchronized(InterruptHangTarg.sync) {
                // Kill the interrupter thread
                interruptorThread.interrupt();
            }
        }

        // Print how many times an interrupt was sent. When in remote mode, the RemoteInterruptor
        // is in a different process, so interruptsSent is not updated, therefore we don't
        // print it here. The RemoteInterruptor thread keeps its own count and prints
        // it before it exits.
        if (!remoteMode) {
            System.out.println("interrupts sent: " + interruptsSent);
        }

        // When in precise mode, make sure that every interrupt sent resulted in
        // an InterruptedException. Note the interruptor always ends up sending
        // one extra interrupt at the end.
        if (preciseMode && interruptsSent != answer + 1) {
            throw new RuntimeException("Too many interrupts sent. Sent: " + interruptsSent +
                                       ". Expected to send: " + answer + 1);
        }
        System.out.println("Goodbye from InterruptHangTarg!");
    }

    static class AggressiveInterruptor implements Runnable {
        Thread interruptee;
        AggressiveInterruptor(Thread interruptee) {
            this.interruptee = interruptee;
        }

        public void run() {
            while (true) {
                InterruptHangTarg.interruptsSent++;
                interruptee.interrupt();
                try {
                    Thread.sleep(5);
                } catch (InterruptedException ee) {
                    System.out.println("Debuggee Interruptor: finished after " +
                                       InterruptHangTarg.interruptsSent + " iterrupts");
                    break;
                }
            }
        }
    }

    static class PreciseInterruptor implements Runnable {
        Thread interruptee;
        PreciseInterruptor(Thread interruptee) {
            this.interruptee = interruptee;
        }

        public void run() {
            synchronized(InterruptHangTarg.sync) {
                while (true) {
                    InterruptHangTarg.interruptsSent++;
                    interruptee.interrupt();
                    try {
                        // Wait until the interruptee has handled the interrupt
                        InterruptHangTarg.sync.wait();
                    } catch (InterruptedException ee) {
                        System.out.println("Debuggee Interruptor: finished after " +
                                           InterruptHangTarg.interruptsSent + " iterrupts");
                        break;
                    }
                }
            }
        }
    }
}

    /********** test program **********/

public class InterruptHangTest extends TestScaffold {
    class RemoteInterruptor extends Thread {
        static int interruptsSent;
        ThreadReference interruptee;

        RemoteInterruptor(ThreadReference interruptee) {
            this.interruptee = interruptee;
        }

        public void run() {
            try {
                while (true) {
                    interruptee.interrupt();
                    interruptsSent++;
                    Thread.sleep(5);
                }
            } catch (InterruptedException ee) {
                println("RemoteInterruptor thread: Unexpected Interrupt");
                throw new RuntimeException(ee);
            } catch (IllegalThreadStateException | VMDisconnectedException ee) {
                println("RemoteInterruptor thread: Got expected " + ee.getClass().getSimpleName()
                        + " after " + interruptsSent + " interrupts sent. Exiting.");
            } catch (Throwable ee) {
                println("RemoteInterruptor thread: Got unexpected exception after "
                        + interruptsSent + " interrupts sent. Exiting with exception.");
                ee.printStackTrace(System.out);
                throw new RuntimeException(ee);
            }
        }
    }

    ThreadReference mainThread;
    Thread timerThread;
    String sync = "sync";
    static int nSteps = 0;
    static boolean remoteMode;

    InterruptHangTest (String args[]) {
        super(args);
    }

    public static void main(String[] args)      throws Exception {
        remoteMode = "remote".equals(args[0]);
        new InterruptHangTest(args).startTests();
    }

    /********** event handlers **********/

    public void stepCompleted(StepEvent event) {
        synchronized(sync) {
            nSteps++;
        }
        println("Got StepEvent " + nSteps + " at line " +
                event.location().method() + ":" +
                event.location().lineNumber());
        if (nSteps == 1) {
            timerThread.start();
        }
    }

    /********** test core **********/

    protected void runTests() throws Exception {
        BreakpointEvent bpe = startToMain("InterruptHangTarg");
        mainThread = bpe.thread();
        EventRequestManager erm = vm().eventRequestManager();

        /*
         * Set event requests
         */
        StepRequest request = erm.createStepRequest(mainThread,
                                                    StepRequest.STEP_LINE,
                                                    StepRequest.STEP_OVER);
        request.enable();

        // Will be started by the step event handler
        timerThread = new Thread("test timer") {
                public void run() {
                    int mySteps = 0;
                    float timeoutFactor = Float.parseFloat(System.getProperty("test.timeout.factor", "1.0"));
                    long sleepSeconds = (long)(20 * timeoutFactor);
                    println("Timer watching for steps every " + sleepSeconds + " seconds");
                    while (true) {
                        try {
                            Thread.sleep(sleepSeconds * 1000);
                            synchronized(sync) {
                                println("steps = " + nSteps);
                                if (mySteps == nSteps) {
                                    // no step for a long time
                                    failure("failure: Debuggee appears to be hung (no steps for " + sleepSeconds + "s)");
                                    vm().exit(-1);
                                    break;
                                }
                            }
                            mySteps = nSteps;
                        } catch (InterruptedException ee) {
                            break;
                        }
                    }
                }
            };

        Thread remoteInterruptorThread = null;
        if (remoteMode) {
            // Create a thread to call ThreadReference.interrupt() on the
            // debuggee main thread.
            remoteInterruptorThread = new RemoteInterruptor(mainThread);
            remoteInterruptorThread.setName("RemoteInterruptor");
            remoteInterruptorThread.setDaemon(true);
            remoteInterruptorThread.start();
        }

        /*
         * resume the target listening for events
         */
        listenUntilVMDisconnect();
        if (remoteInterruptorThread != null) {
            remoteInterruptorThread.join();
        }
        timerThread.interrupt();

        /*
         * deal with results of test
         * if anything has called failure("foo") testFailed will be true
         */
        if (!testFailed) {
            println("InterruptHangTest("+ args[0] + "): passed");
        } else {
            throw new Exception("InterruptHangTest("+ args[0] + "): failed");
        }
    }
}