Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/jdi/EventHandler.java
41161 views
/*1* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share.jdi;2425import java.util.*;26import com.sun.jdi.*;27import com.sun.jdi.event.*;28import com.sun.jdi.request.*;29import nsk.share.*;3031/**32* This class provides a separate thread for asynchronous listening33* to JDI events. All received events are sequentially passed34* to event listeners. If current event listener returns true35* then the event is considered be processed and it is not passed36* to remaining listeners.37* <p>38* The <code>EventHandler</code> thread runs until <code>VMDisconnectEvent</code>39* is received or <code>VMDisconnectionedException</code> is caught.40*41* @see EventListener42*/43public class EventHandler implements Runnable {4445private Debugee debuggee = null;46private Log log = null;4748private VirtualMachine vm;49private EventRequestManager requestManager;50/**51* Container for event listeners52*/53private static List<EventListener> listeners = Collections.synchronizedList(new Vector<EventListener>());54private Thread listenThread;5556/**57* Exit status of the <code>EventHandler</code> thread58*/59private static volatile int status = -1;6061/**62* Return an exit status of the <code>EventHandler</code> thread63* were the values are:64* <li><i>-1</i> - no value65* <li><i>0</i> - normal termination66* <li><i>1</i> - the thread is still running67* <li><i>2</i> - abnormal termination68*/69public static synchronized int getStatus() {70return status;71}7273/**74* Default ExceptionRequest with SUSPEND_EVENT_THREAD to be able75* to catch debuggee exceptions and output a message.76*/77private static ExceptionRequest defaultExceptionRequest = null;7879/**80* This flag will be set true if event of uncaught exception has been81* received.82*/83private static volatile boolean defaultExceptionCaught = false;84public static synchronized boolean getExceptionCaught() {85return defaultExceptionCaught;86}8788/**89* This flag will be set true if any event which does not have specific90* listener has been received.91*/92private static volatile boolean unexpectedEventCaught = false;93public static synchronized boolean unexpectedEventCaught() {94return unexpectedEventCaught;95}9697/**98* This flag shows if debugged VM is connected to debugger.99*/100private static volatile boolean vmDisconnected = false;101public static synchronized boolean isDisconnected() {102return vmDisconnected;103}104105public EventHandler(Debugee debuggee, Log log) {106this.listenThread = new Thread(this);107this.listenThread.setDaemon(true);108109this.debuggee = debuggee;110this.log = log;111this.vm = debuggee.VM();112this.requestManager = vm.eventRequestManager();113}114115private void display(String str) {116log.display("EventHandler> " + str);117}118119// is EventHandler was interrupted120private volatile boolean wasInterrupted;121122public void stopEventHandler() {123wasInterrupted = true;124125listenThread.interrupt();126127try {128listenThread.join();129}130catch(InterruptedException e) {131throw new TestBug("Unexpected exception: " + e);132}133}134/**135* The <code>EventHandler</code> thread keeps running until a VMDisconnectedEvent occurs136* or some exception occurs during event processing.137*/138public void run() {139synchronized(EventHandler.this) {140status = 1; // running141}142do {143try {144EventSet set = vm.eventQueue().remove();145146switch (set.suspendPolicy()) {147case EventRequest.SUSPEND_NONE:148display("Received event set with policy = SUSPEND_NONE");149break;150case EventRequest.SUSPEND_ALL:151display("Received event set with policy = SUSPEND_ALL");152break;153case EventRequest.SUSPEND_EVENT_THREAD:154display("Received event set with policy = SUSPEND_EVENT_THREAD");155break;156}157158synchronized (listeners) {159synchronized (EventHandler.this) {160for (EventListener listener : listeners) {161// proloque listener for a event set162listener.eventSetReceived(set);163}164165for (Event event : set) {166// print only event class name here because of Event,toString may cause unexpected exception167display("Event: " + event.getClass().getSimpleName()168+ " req " + event.request());169boolean processed = false;170for (EventListener listener : listeners) {171processed = listener.eventReceived(event);172173if (processed) {174if (listener.shouldRemoveListener()) {175listener.eventSetComplete(set);176removeListener(listener);177}178179break;180}181}182}183184for (EventListener listener : listeners) {185// epiloque listener for a event set186listener.eventSetComplete(set);187}188}189}190191}192catch (Exception e) {193194if(e instanceof InterruptedException) {195if(wasInterrupted)196break;197}198199log.complain("Exception occured in eventHandler thread: " + e.getMessage());200e.printStackTrace(log.getOutStream());201synchronized(EventHandler.this) {202// This will make the waiters such as waitForVMDisconnect203// exit their wait loops.204vmDisconnected = true;205status = 2; // abnormal termination206EventHandler.this.notifyAll();207}208throw new Failure(e);209}210} while (!wasInterrupted && !isDisconnected());211212if (unexpectedEventCaught || defaultExceptionCaught) {213synchronized(EventHandler.this) {214status = 2;215}216}217display("finished");218}219220221/**222* This is normally called in the main thread of the test debugger.223* It starts up an <code>EventHandler</code> thread that gets events coming in224* from the debuggee and distributes them to listeners.225*/226public void startListening() {227createDefaultEventRequests();228createDefaultListeners();229listenThread.start();230}231232233/**234* This method sets up default requests.235*/236private void createDefaultEventRequests() {237/**238* The following request will allow to print a warning if a debuggee gets an239* unexpected exception. The unexpected exception will be handled in240* the eventReceived method in the default listener created.241* If a test case does not want an uncaught exception to cause a242* message, it must add new listener for uncaught exception events to243* handle them.244*/245defaultExceptionRequest = requestManager.createExceptionRequest(null, false, true);246defaultExceptionRequest.enable();247}248249/**250* This method sets up default listeners.251*/252private void createDefaultListeners() {253/**254* This listener catches up all unexpected events.255*256*/257addListener(258new EventListener() {259public boolean eventReceived(Event event) {260log.complain("EventHandler> Unexpected event: " + event.getClass().getName());261unexpectedEventCaught = true;262return true;263}264}265);266267/**268* This listener catches up VMStart event.269*/270addListener(271new EventListener() {272public boolean eventReceived(Event event) {273if (event instanceof VMStartEvent) {274display("received VMStart");275removeListener(this);276return true;277}278return false;279}280}281);282283/**284* This listener catches up VMDeath event.285*/286addListener(287new EventListener() {288public boolean eventReceived(Event event) {289if (event instanceof VMDeathEvent) {290display("receieved VMDeath");291removeListener(this);292return true;293}294return false;295}296}297);298299/**300* This listener catches up <code>VMDisconnectEvent</code>event and301* signals <code>EventHandler</code> thread to finish.302*/303addListener(304new EventListener() {305public boolean eventReceived(Event event) {306if (event instanceof VMDisconnectEvent ) {307display("receieved VMDisconnect");308synchronized(EventHandler.this) {309vmDisconnected = true;310status = 0; // OK finish311EventHandler.this.notifyAll();312removeListener(this);313}314return true;315}316return false;317}318}319);320321/**322* This listener catches uncaught exceptions and print a message.323*/324addListener( new EventListener() {325public boolean eventReceived(Event event) {326boolean handled = false;327328if (event instanceof ExceptionEvent &&329defaultExceptionRequest != null &&330defaultExceptionRequest.equals(event.request())) {331332if (EventFilters.filtered(event) == false) {333log.complain("EventHandler> Unexpected Debuggee Exception: " +334(ExceptionEvent)event);335defaultExceptionCaught = true;336}337338handled = true;339vm.resume();340}341342return handled;343}344}345);346}347348/**349* Add at beginning of the list because we want350* the LAST added listener to be FIRST to process351* current event.352*/353public void addListener(EventListener listener) {354display("Adding listener " + listener);355synchronized(listeners) {356listeners.add(0, listener);357}358}359360/**361* Removes the listener from the list.362*/363public void removeListener(EventListener listener) {364display("Removing listener " + listener);365synchronized(listeners) {366listeners.remove(listener);367}368}369370371/**372* Returns an event which is received for any of given requests.373*/374public Event waitForRequestedEvent( final EventRequest[] requests,375long timeout,376boolean shouldRemoveListeners) {377class EventNotification {378volatile Event event = null;379}380final EventNotification en = new EventNotification();381382EventListener listener = new EventListener() {383public boolean eventReceived(Event event) {384for (int i = 0; i < requests.length; i++) {385EventRequest request = requests[i];386if (!request.isEnabled())387continue;388if (request.equals(event.request())) {389display("waitForRequestedEvent: Received event(" + event + ") for request(" + request + ")");390synchronized (EventHandler.this) {391en.event = event;392EventHandler.this.notifyAll();393}394return true;395}396}397return false;398}399};400if (shouldRemoveListeners) {401display("waitForRequestedEvent: enabling remove of listener " + listener);402listener.enableRemovingThisListener();403}404for (int i = 0; i < requests.length; i++) {405requests[i].enable();406}407addListener(listener);408409try {410long timeToFinish = System.currentTimeMillis() + timeout;411long timeLeft = timeout;412synchronized (EventHandler.this) {413display("waitForRequestedEvent: vm.resume called");414vm.resume();415416while (!isDisconnected() && en.event == null && timeLeft > 0) {417EventHandler.this.wait(timeLeft);418timeLeft = timeToFinish - System.currentTimeMillis();419}420}421} catch (InterruptedException e) {422return null;423}424if (shouldRemoveListeners && !isDisconnected()) {425for (int i = 0; i < requests.length; i++) {426requests[i].disable();427}428}429if (en.event == null) {430throw new Failure("waitForRequestedEvent: no requested events have been received.");431}432return en.event;433}434435/**436* Returns an event set which is received for any of given requests.437*/438public EventSet waitForRequestedEventSet( final EventRequest[] requests,439long timeout,440boolean shouldRemoveListeners) {441class EventNotification {442volatile EventSet set = null;443}444final EventNotification en = new EventNotification();445446EventListener listener = new EventListener() {447public void eventSetReceived(EventSet set) {448449EventIterator eventIterator = set.eventIterator();450451while (eventIterator.hasNext()) {452453Event event = eventIterator.nextEvent();454455for (int i = 0; i < requests.length; i++) {456EventRequest request = requests[i];457if (!request.isEnabled())458continue;459460if (request.equals(event.request())) {461display("waitForRequestedEventSet: Received event set for request: " + request);462synchronized (EventHandler.this) {463en.set = set;464EventHandler.this.notifyAll();465}466return;467}468}469}470}471472public boolean eventReceived(Event event) {473return (en.set != null);474}475};476477if (shouldRemoveListeners) {478display("waitForRequestedEventSet: enabling remove of listener " + listener);479listener.enableRemovingThisListener();480}481for (int i = 0; i < requests.length; i++) {482requests[i].enable();483}484addListener(listener);485486try {487long timeToFinish = System.currentTimeMillis() + timeout;488long timeLeft = timeout;489synchronized (EventHandler.this) {490display("waitForRequestedEventSet: vm.resume called");491vm.resume();492493while (!isDisconnected() && en.set == null && timeLeft > 0) {494EventHandler.this.wait(timeLeft);495timeLeft = timeToFinish - System.currentTimeMillis();496}497}498} catch (InterruptedException e) {499return null;500}501if (shouldRemoveListeners && !isDisconnected()) {502for (int i = 0; i < requests.length; i++) {503requests[i].disable();504}505}506if (en.set == null) {507throw new Failure("waitForRequestedEventSet: no requested events have been received.");508}509return en.set;510}511512public synchronized void waitForVMDisconnect() {513display("waitForVMDisconnect");514while (!isDisconnected()) {515try {516wait();517} catch (InterruptedException e) {518}519}520display("waitForVMDisconnect: done");521}522523/**524* This is a superclass for any event listener.525*/526public static class EventListener {527528/**529* This flag shows if the listener must be removed530* after current event has been processed by531* this listener.532*/533volatile boolean shouldRemoveListener = false;534public boolean shouldRemoveListener() {535return shouldRemoveListener;536}537538public void enableRemovingThisListener() {539shouldRemoveListener = true;540}541542/**543* This method will be called by <code>EventHandler</code>544* for received event set before any call of <code>eventReceived</code>545* method for events contained in this set.546*/547public void eventSetReceived(EventSet set) {}548549550/**551* This method will be called by <code>EventHandler</code>552* for received event set after all calls of <code>eventReceived</code>553* and event specific methods for all events contained in this set.554*/555public void eventSetComplete(EventSet set) {}556557/**558* This method will be called by <code>EventHandler</code>559* for any event contained in received event set.560*561* @return <code>true</code> if event was processed by this562* <code>EventListener<code> or <code>false</code> otherwise.563*/564public boolean eventReceived(Event event) {565return false;566}567}568}569570571