1 /*
2  * Copyright (c) 2001, 2018, 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 package nsk.jdi.MethodExitEvent._itself_;
25 
26 import com.sun.jdi.*;
27 import com.sun.jdi.event.*;
28 import com.sun.jdi.request.*;
29 
30 import java.io.*;
31 import java.util.List;
32 import java.util.Iterator;
33 
34 import nsk.share.*;
35 import nsk.share.jpda.*;
36 import nsk.share.jdi.*;
37 
38 
39 // This class is the debugger application in the test
40 
41 public class methodexit001 {
42     // exit status constants
43     static final int PASSED = 0;
44     static final int FAILED = 2;
45     static final int JCK_STATUS_BASE = 95;
46 
47     // timeout interval for waiting events in a loop
48     static final int TIMEOUT_DELTA = 1000; // milliseconds
49 
50     // synchronization commands
51     static final String COMMAND_READY = "ready";
52     static final String COMMAND_QUIT  = "quit";
53     static final String COMMAND_GO    = "go";
54     static final String COMMAND_DONE  = "done";
55 
56     // class names
57     static final String TEST_NAME     = "nsk.jdi.MethodExitEvent._itself_.methodexit001";
58     static final String DEBUGGEE_NAME = TEST_NAME + "a";
59 
60     // JDI scaffold objects
61     static private Debugee debuggee;
62     static private IOPipe pipe;
63     static private VirtualMachine vm;
64     static private Log log;
65     static private ArgumentHandler argHandler;
66     static private EventSet eventSet;
67 
68     // mirrors for tested debuggee entities
69     static private MethodExitRequest  checkedRequest;
70     static private Method             checkedMethod;
71     static private ReferenceType      checkedClass;
72 
73     // auxilary breakpoints
74     static private BreakpointRequest  startingBreakpointRequest;
75     static private BreakpointRequest  endingBreakpointRequest;
76     static private Method             runMethod;
77 
78     // flags and counters
79     static private long eventTimeout;
80     static private int depthVal, eventsCounter;
81     static private volatile boolean testFailed, eventReceived;
82 
83     // start test from command line
main(String args[])84     public static void main (String args[]) {
85           System.exit(run(args, System.out) + JCK_STATUS_BASE);
86     }
87 
88     // start test from JCK-compatible environment
run(final String args[], final PrintStream out)89     public static int run(final String args[], final PrintStream out) {
90 
91         testFailed = false;
92         eventReceived = false;
93         eventsCounter = 0;
94 
95         argHandler = new ArgumentHandler(args);
96         log = new Log(out, argHandler);
97         eventTimeout = argHandler.getWaitTime() * 60 * 1000; // milliseconds
98 
99         // launch debuggee
100         Binder binder = new Binder(argHandler, log);
101         log.display("Connecting to debuggee");
102         debuggee = binder.bindToDebugee(DEBUGGEE_NAME);
103         debuggee.redirectStderr(log, "debuggee >");
104 
105         pipe = debuggee.createIOPipe();
106 
107         // resume debuggee
108         log.display("Resuming debuggee");
109         debuggee.resume();
110 
111         try {
112 
113             // wait for debugee started
114             log.display("Waiting for command: " + COMMAND_READY);
115             String command = pipe.readln();
116             if (!command.equals(COMMAND_READY)) {
117                 throw new Failure("TEST BUG: unknown debuggee's command: " + command);
118             }
119 
120             // get mirrors for checked class, thread, and method
121 
122             vm = debuggee.VM();
123 
124             log.display("Getting loaded class in debuggee");
125             checkedClass = debuggee.classByName(DEBUGGEE_NAME);
126 
127             log.display("Getting reference to method 'foo'");
128             checkedMethod = debuggee.methodByName(checkedClass, "foo");
129 
130             // create event request (initially disabled)
131 
132             EventRequestManager eventRManager = vm.eventRequestManager();
133 
134             log.display("Creating MethodExitRequest");
135             if ((checkedRequest = eventRManager.createMethodExitRequest()) == null) {
136                 throw new Failure("TEST BUG: unable to create MethodExitRequest");
137             }
138 
139             checkedRequest.addClassFilter(checkedClass);
140             log.display("MethodExitRequest is created");
141 
142             // create two auxilary breakpoints
143 
144             log.display("Getting reference to method <run>");
145             runMethod = debuggee.methodByName(checkedClass, "run");
146             if (runMethod == null) {
147                 throw new Failure("TEST BUG: returned null reference to method <run>");
148             }
149 
150             log.display("Creating two auxilary breakpoints into method <run>");
151             startingBreakpointRequest = debuggee.setBreakpoint(runMethod, methodexit001a.STARTING_BREAKPOINT_LINE);
152             endingBreakpointRequest = debuggee.setBreakpoint(runMethod, methodexit001a.ENDING_BREAKPOINT_LINE);
153 
154             // define separate thread for handling events
155             class EventHandler extends Thread {
156                 public void run() {
157                     eventSet = null;
158                     try {
159                         while (!eventReceived) {
160                             eventSet = vm.eventQueue().remove();
161 
162                             EventIterator eventIterator = eventSet.eventIterator();
163                             while (eventIterator.hasNext()) {
164 
165                                 Event event = eventIterator.nextEvent();
166 
167                                 // enable or disable checked event request at BreakpointEvent
168                                 if (event instanceof BreakpointEvent) {
169                                     Location eventLocation  = ((BreakpointEvent)event).location();
170                                     int lineNumber = eventLocation.lineNumber();
171                                     log.display("BreakpointEvent received for location " + lineNumber);
172                                     if (lineNumber == methodexit001a.STARTING_BREAKPOINT_LINE) {
173                                         log.display("Enabling MethodExitRequest at breakpoint before invoking method");
174                                         checkedRequest.enable();
175                                     } else if (lineNumber == methodexit001a.ENDING_BREAKPOINT_LINE) {
176                                         log.display("Disabling MethodExitRequest at breakpoint after invoking method");
177                                         checkedRequest.disable();
178                                         eventReceived = true;
179                                     } else {
180                                         testFailed = true;
181                                         throw new Failure("TEST BUG: Unknown location of breakpoint event: " + lineNumber);
182                                     }
183                                 }
184 
185                                 // handle checked MethodEntryEvent
186                                 if (event instanceof MethodExitEvent) {
187                                     MethodExitEvent castedEvent = (MethodExitEvent) event;
188                                     EventRequest eventRequest = castedEvent.request();
189 
190                                     if (castedEvent.method().equals(checkedMethod)) {
191                                         eventsCounter++;
192                                         log.display("FAILURE 1: MethodExitEvent is received for method " +
193                                            checkedMethod.name() + " at location " + castedEvent.location().lineNumber());
194                                         testFailed = true;
195                                     }
196                                 }
197                             }
198                             eventSet.resume();
199                         }
200                     } catch (InterruptedException e) {
201                         log.complain("TEST INCOMPLETE: caught InterruptedException while waiting for event");
202                         testFailed = true;
203                     } catch (VMDisconnectedException e) {
204                         log.complain("TEST INCOMPLETE: caught VMDisconnectedException while waiting for event");
205                         testFailed = true;
206                     }
207                     log.display("eventHandler completed");
208                 }
209             }
210 
211             // start event handling thread
212             EventHandler eventHandler = new EventHandler();
213             log.display("Starting eventHandler");
214             eventHandler.start();
215 
216             // force debuggee to invoke method
217             log.display("Sending command: " + COMMAND_GO);
218             pipe.println(COMMAND_GO);
219 
220             log.display("");
221 
222             // waiting for debuggee comfirms method invoked
223             log.display("Waiting for command: " + COMMAND_DONE);
224             command = pipe.readln();
225             if (!command.equals(COMMAND_DONE)) {
226                 throw new Failure("TEST BUG: unknown debuggee's command: " + command);
227             }
228 
229             log.display("");
230 
231             // wait for all expected events received or timeout exceeds
232             log.display("Waiting for all expected events received");
233             try {
234                 eventHandler.join(eventTimeout);
235                 if (eventHandler.isAlive()) {
236                     log.complain("FAILURE 20: Timeout for waiting event was exceeded");
237                     eventHandler.interrupt();
238                     testFailed = true;
239                 }
240             } catch (InterruptedException e) {
241                 log.complain("TEST INCOMPLETE: InterruptedException caught while waiting for eventHandler's death");
242                 testFailed = true;
243             }
244 
245             // check whether all expected events received or not
246             if (eventsCounter == 0) {
247                 log.display("No any MethodExitEvent received for checked method as expected");
248             }
249 
250         } catch (Failure e) {
251             log.complain("TEST FAILURE: " + e.getMessage());
252             testFailed = true;
253         } catch (Exception e) {
254             log.complain("Unexpected exception: " + e);
255             e.printStackTrace(out);
256             testFailed = true;
257         } finally {
258 
259             log.display("");
260 
261             // force debuggee to exit
262             log.display("Sending command: " + COMMAND_QUIT);
263             pipe.println(COMMAND_QUIT);
264 
265             // wait for debuggee exits and analyze its exit code
266             log.display("Waiting for debuggee terminating");
267             int debuggeeStatus = debuggee.endDebugee();
268             if (debuggeeStatus == PASSED + JCK_STATUS_BASE) {
269                 log.display("Debuggee PASSED with exit code: " + debuggeeStatus);
270             } else {
271                 log.complain("Debuggee FAILED with exit code: " + debuggeeStatus);
272                 testFailed = true;
273             }
274         }
275 
276         // check test results
277         if (testFailed) {
278             log.complain("TEST FAILED");
279             return FAILED;
280         }
281 
282         log.display("TEST PASSED");
283         return PASSED;
284 
285     }
286 }
287