1 /*
2  * Copyright (c) 1998, 2016, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * This source code is provided to illustrate the usage of a given feature
28  * or technique and has been deliberately simplified. Additional steps
29  * required for a production-quality application, such as security checks,
30  * input validation and proper error handling, might not be present in
31  * this sample code.
32  */
33 
34 
35 package com.sun.tools.example.debug.tty;
36 
37 import com.sun.jdi.*;
38 import com.sun.jdi.event.*;
39 import com.sun.jdi.request.EventRequest;
40 
41 public class EventHandler implements Runnable {
42 
43     EventNotifier notifier;
44     Thread thread;
45     volatile boolean connected = true;
46     boolean completed = false;
47     String shutdownMessageKey;
48     boolean stopOnVMStart;
49 
EventHandler(EventNotifier notifier, boolean stopOnVMStart)50     EventHandler(EventNotifier notifier, boolean stopOnVMStart) {
51         this.notifier = notifier;
52         this.stopOnVMStart = stopOnVMStart;
53         this.thread = new Thread(this, "event-handler");
54         this.thread.start();
55     }
56 
shutdown()57     synchronized void shutdown() {
58         connected = false;  // force run() loop termination
59         thread.interrupt();
60         while (!completed) {
61             try {wait();} catch (InterruptedException exc) {}
62         }
63     }
64 
65     @Override
run()66     public void run() {
67         EventQueue queue = Env.vm().eventQueue();
68         while (connected) {
69             try {
70                 EventSet eventSet = queue.remove();
71                 boolean resumeStoppedApp = false;
72                 EventIterator it = eventSet.eventIterator();
73                 while (it.hasNext()) {
74                     resumeStoppedApp |= !handleEvent(it.nextEvent());
75                 }
76 
77                 if (resumeStoppedApp) {
78                     eventSet.resume();
79                 } else if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL) {
80                     setCurrentThread(eventSet);
81                     notifier.vmInterrupted();
82                 }
83             } catch (InterruptedException exc) {
84                 // Do nothing. Any changes will be seen at top of loop.
85             } catch (VMDisconnectedException discExc) {
86                 handleDisconnectedException();
87                 break;
88             }
89         }
90         synchronized (this) {
91             completed = true;
92             notifyAll();
93         }
94     }
95 
handleEvent(Event event)96     private boolean handleEvent(Event event) {
97         notifier.receivedEvent(event);
98 
99         if (event instanceof ExceptionEvent) {
100             return exceptionEvent(event);
101         } else if (event instanceof BreakpointEvent) {
102             return breakpointEvent(event);
103         } else if (event instanceof WatchpointEvent) {
104             return fieldWatchEvent(event);
105         } else if (event instanceof StepEvent) {
106             return stepEvent(event);
107         } else if (event instanceof MethodEntryEvent) {
108             return methodEntryEvent(event);
109         } else if (event instanceof MethodExitEvent) {
110             return methodExitEvent(event);
111         } else if (event instanceof ClassPrepareEvent) {
112             return classPrepareEvent(event);
113         } else if (event instanceof ClassUnloadEvent) {
114             return classUnloadEvent(event);
115         } else if (event instanceof ThreadStartEvent) {
116             return threadStartEvent(event);
117         } else if (event instanceof ThreadDeathEvent) {
118             return threadDeathEvent(event);
119         } else if (event instanceof VMStartEvent) {
120             return vmStartEvent(event);
121         } else {
122             return handleExitEvent(event);
123         }
124     }
125 
126     private boolean vmDied = false;
handleExitEvent(Event event)127     private boolean handleExitEvent(Event event) {
128         if (event instanceof VMDeathEvent) {
129             vmDied = true;
130             return vmDeathEvent(event);
131         } else if (event instanceof VMDisconnectEvent) {
132             connected = false;
133             if (!vmDied) {
134                 vmDisconnectEvent(event);
135             }
136             /*
137              * Inform jdb command line processor that jdb is being shutdown. JDK-8154144.
138              */
139             ((TTY)notifier).setShuttingDown(true);
140             Env.shutdown(shutdownMessageKey);
141             return false;
142         } else {
143             throw new InternalError(MessageOutput.format("Unexpected event type",
144                                                          new Object[] {event.getClass()}));
145         }
146     }
147 
handleDisconnectedException()148     synchronized void handleDisconnectedException() {
149         /*
150          * A VMDisconnectedException has happened while dealing with
151          * another event. We need to flush the event queue, dealing only
152          * with exit events (VMDeath, VMDisconnect) so that we terminate
153          * correctly.
154          */
155         EventQueue queue = Env.vm().eventQueue();
156         while (connected) {
157             try {
158                 EventSet eventSet = queue.remove();
159                 EventIterator iter = eventSet.eventIterator();
160                 while (iter.hasNext()) {
161                     handleExitEvent(iter.next());
162                 }
163             } catch (InterruptedException exc) {
164                 // ignore
165             } catch (InternalError exc) {
166                 // ignore
167             }
168         }
169     }
170 
eventThread(Event event)171     private ThreadReference eventThread(Event event) {
172         if (event instanceof ClassPrepareEvent) {
173             return ((ClassPrepareEvent)event).thread();
174         } else if (event instanceof LocatableEvent) {
175             return ((LocatableEvent)event).thread();
176         } else if (event instanceof ThreadStartEvent) {
177             return ((ThreadStartEvent)event).thread();
178         } else if (event instanceof ThreadDeathEvent) {
179             return ((ThreadDeathEvent)event).thread();
180         } else if (event instanceof VMStartEvent) {
181             return ((VMStartEvent)event).thread();
182         } else {
183             return null;
184         }
185     }
186 
setCurrentThread(EventSet set)187     private void setCurrentThread(EventSet set) {
188         ThreadReference thread;
189         if (set.size() > 0) {
190             /*
191              * If any event in the set has a thread associated with it,
192              * they all will, so just grab the first one.
193              */
194             Event event = set.iterator().next(); // Is there a better way?
195             thread = eventThread(event);
196         } else {
197             thread = null;
198         }
199         setCurrentThread(thread);
200     }
201 
setCurrentThread(ThreadReference thread)202     private void setCurrentThread(ThreadReference thread) {
203         ThreadInfo.invalidateAll();
204         ThreadInfo.setCurrentThread(thread);
205     }
206 
vmStartEvent(Event event)207     private boolean vmStartEvent(Event event)  {
208         VMStartEvent se = (VMStartEvent)event;
209         notifier.vmStartEvent(se);
210         return stopOnVMStart;
211     }
212 
breakpointEvent(Event event)213     private boolean breakpointEvent(Event event)  {
214         BreakpointEvent be = (BreakpointEvent)event;
215         notifier.breakpointEvent(be);
216         return true;
217     }
218 
methodEntryEvent(Event event)219     private boolean methodEntryEvent(Event event)  {
220         MethodEntryEvent me = (MethodEntryEvent)event;
221         notifier.methodEntryEvent(me);
222         return true;
223     }
224 
methodExitEvent(Event event)225     private boolean methodExitEvent(Event event)  {
226         MethodExitEvent me = (MethodExitEvent)event;
227         return notifier.methodExitEvent(me);
228     }
229 
fieldWatchEvent(Event event)230     private boolean fieldWatchEvent(Event event)  {
231         WatchpointEvent fwe = (WatchpointEvent)event;
232         notifier.fieldWatchEvent(fwe);
233         return true;
234     }
235 
stepEvent(Event event)236     private boolean stepEvent(Event event)  {
237         StepEvent se = (StepEvent)event;
238         notifier.stepEvent(se);
239         return true;
240     }
241 
classPrepareEvent(Event event)242     private boolean classPrepareEvent(Event event)  {
243         ClassPrepareEvent cle = (ClassPrepareEvent)event;
244         notifier.classPrepareEvent(cle);
245 
246         if (!Env.specList.resolve(cle)) {
247             MessageOutput.lnprint("Stopping due to deferred breakpoint errors.");
248             return true;
249         } else {
250             return false;
251         }
252     }
253 
classUnloadEvent(Event event)254     private boolean classUnloadEvent(Event event)  {
255         ClassUnloadEvent cue = (ClassUnloadEvent)event;
256         notifier.classUnloadEvent(cue);
257         return false;
258     }
259 
exceptionEvent(Event event)260     private boolean exceptionEvent(Event event) {
261         ExceptionEvent ee = (ExceptionEvent)event;
262         notifier.exceptionEvent(ee);
263         return true;
264     }
265 
threadDeathEvent(Event event)266     private boolean threadDeathEvent(Event event) {
267         ThreadDeathEvent tee = (ThreadDeathEvent)event;
268         ThreadInfo.removeThread(tee.thread());
269         return false;
270     }
271 
threadStartEvent(Event event)272     private boolean threadStartEvent(Event event) {
273         ThreadStartEvent tse = (ThreadStartEvent)event;
274         ThreadInfo.addThread(tse.thread());
275         notifier.threadStartEvent(tse);
276         return false;
277     }
278 
vmDeathEvent(Event event)279     public boolean vmDeathEvent(Event event) {
280         shutdownMessageKey = "The application exited";
281         notifier.vmDeathEvent((VMDeathEvent)event);
282         return false;
283     }
284 
vmDisconnectEvent(Event event)285     public boolean vmDisconnectEvent(Event event) {
286         shutdownMessageKey = "The application has been disconnected";
287         notifier.vmDisconnectEvent((VMDisconnectEvent)event);
288         return false;
289     }
290 }
291