08855df46a
Reviewed-by: vlivanov, erikj, mseledtsov, gthornbr
910 lines
34 KiB
Java
910 lines
34 KiB
Java
/*
|
|
* Copyright (c) 2006, 2018, 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.
|
|
*/
|
|
package nsk.share.jdi;
|
|
|
|
import java.io.PrintStream;
|
|
import java.lang.reflect.*;
|
|
import java.util.*;
|
|
|
|
import com.sun.jdi.*;
|
|
import com.sun.jdi.event.*;
|
|
import com.sun.jdi.request.*;
|
|
import nsk.share.TestBug;
|
|
|
|
/*
|
|
* Class is used as base debugger in tests for following events and event requests:
|
|
* - MonitorContendedEnterRequest / MonitorContendedEnterEvent
|
|
* - MonitorContendedEnteredRequest / MonitorContendedEnteredEvent
|
|
* - MonitorWaitRequest / MonitorWaitEvent
|
|
* - MonitorWaitedRequest / MonitorWaitedEvent
|
|
*
|
|
* In all these tests similar scenario is used:
|
|
* - debugger VM forces debuggee VM to create number of objects which should generate events during test
|
|
* - if any event filters are used each generating event object is checked is this object accepted by all filters,
|
|
* if object was accepted it should save information about all generated events and this information is available for debugger
|
|
* - debuggee performs event generation and stop at breakpoint
|
|
* - debugger reads data saved by event generators and checks is only expected events was generated
|
|
*/
|
|
public class JDIEventsDebugger extends TestDebuggerType2 {
|
|
// types of tested events
|
|
static public enum EventType {
|
|
MONITOR_CONTENTED_ENTER,
|
|
MONITOR_CONTENTED_ENTERED,
|
|
MONITOR_WAIT,
|
|
MONITOR_WAITED
|
|
}
|
|
|
|
/*
|
|
* Class contains information required for event testing
|
|
*/
|
|
static class TestedEventData {
|
|
// event class
|
|
public Class<?> eventClass;
|
|
|
|
// class representing event data on debuggee's side
|
|
public Class<?> eventDataMirrorClass;
|
|
|
|
// class representing event data on debugger's side
|
|
public Class<?> eventDataClass;
|
|
|
|
public TestedEventData(Class<?> eventClass, Class<?> eventDataMirrorClass, Class<?> eventDataClass) {
|
|
this.eventClass = eventClass;
|
|
this.eventDataMirrorClass = eventDataMirrorClass;
|
|
this.eventDataClass = eventDataClass;
|
|
}
|
|
}
|
|
|
|
static public Map<EventType, TestedEventData> testedEventData = new HashMap<EventType, TestedEventData>();
|
|
|
|
static {
|
|
testedEventData.put(EventType.MONITOR_CONTENTED_ENTER, new TestedEventData(MonitorContendedEnterEvent.class,
|
|
DebuggeeEventData.DebugMonitorEnterEventData.class, DebuggerEventData.DebugMonitorEnterEventData.class));
|
|
|
|
testedEventData.put(EventType.MONITOR_CONTENTED_ENTERED, new TestedEventData(MonitorContendedEnteredEvent.class,
|
|
DebuggeeEventData.DebugMonitorEnteredEventData.class, DebuggerEventData.DebugMonitorEnteredEventData.class));
|
|
|
|
testedEventData.put(EventType.MONITOR_WAIT, new TestedEventData(MonitorWaitEvent.class, DebuggeeEventData.DebugMonitorWaitEventData.class,
|
|
DebuggerEventData.DebugMonitorWaitEventData.class));
|
|
|
|
testedEventData.put(EventType.MONITOR_WAITED, new TestedEventData(MonitorWaitedEvent.class,
|
|
DebuggeeEventData.DebugMonitorWaitedEventData.class, DebuggerEventData.DebugMonitorWaitedEventData.class));
|
|
}
|
|
|
|
static public TestedEventData[] eventDataByEventTypes(EventType[] eventTypes) {
|
|
TestedEventData[] result = new TestedEventData[eventTypes.length];
|
|
|
|
int i = 0;
|
|
for (EventType eventType : eventTypes) {
|
|
TestedEventData eventData = testedEventData.get(eventType);
|
|
|
|
if (eventData == null)
|
|
throw new TestBug("Unsupported event type: " + eventType);
|
|
|
|
result[i++] = eventData;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Dummy event listener, just accepts all events
|
|
*/
|
|
public class DummyEventListener extends EventHandler.EventListener {
|
|
private volatile boolean breakpointEventReceived;
|
|
|
|
public boolean eventReceived(Event event) {
|
|
if (event instanceof BreakpointEvent) {
|
|
breakpointEventReceived = true;
|
|
vm.resume();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void waitBreakpoint() {
|
|
while (!breakpointEventReceived)
|
|
Thread.yield();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse common for event tests parameters
|
|
*/
|
|
protected String[] doInit(String[] args, PrintStream out) {
|
|
args = super.doInit(args, out);
|
|
|
|
ArrayList<String> standardArgs = new ArrayList<String>();
|
|
|
|
for (int i = 0; i < args.length; i++) {
|
|
if (args[i].equals("-allowExtraEvents")) {
|
|
extraEventClasses = createEventClassArray(args[i + 1]);
|
|
|
|
i++;
|
|
} else if (args[i].equals("-allowMissedEvents")) {
|
|
missedEventClasses = createEventClassArray(args[i + 1]);
|
|
|
|
i++;
|
|
} else
|
|
standardArgs.add(args[i]);
|
|
}
|
|
|
|
return standardArgs.toArray(new String[standardArgs.size()]);
|
|
}
|
|
|
|
// can't control some kinds of events (events from system libraries) and
|
|
// not all events should be saved for analysis
|
|
// (should be implemented in subclasses)
|
|
protected boolean shouldSaveEvent(Event event) {
|
|
return true;
|
|
}
|
|
|
|
public Class<?> findEventDataClass(TestedEventData[] testedEventData, Event event) {
|
|
for (TestedEventData eventData : testedEventData) {
|
|
if (eventData.eventClass.isAssignableFrom(event.getClass()))
|
|
return eventData.eventClass;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
* This event listener stores received monitor events until BreakpointEvent
|
|
* is not received, after getting of BreakpointEvent checks only expected
|
|
* events were received
|
|
*/
|
|
public class EventListener extends EventHandler.EventListener {
|
|
|
|
private TestedEventData[] testedEventData;
|
|
|
|
public EventListener(TestedEventData[] testedEventData) {
|
|
this.testedEventData = testedEventData;
|
|
}
|
|
|
|
private boolean shouldHandleEvent(Event event) {
|
|
return findEventDataClass(testedEventData, event) == null ? false : true;
|
|
}
|
|
|
|
volatile boolean breakpointWasReceived;
|
|
|
|
// execution was interrupted because of timeout
|
|
volatile boolean executionWasInterrupted;
|
|
|
|
public boolean eventReceived(Event event) {
|
|
if (shouldHandleEvent(event)) {
|
|
if (shouldSaveEvent(event)) {
|
|
|
|
Class<?> eventClass;
|
|
|
|
eventClass = findEventDataClass(testedEventData, event);
|
|
List<Event> events = allReceivedEvents.get(eventClass);
|
|
|
|
if (events == null) {
|
|
events = new LinkedList<Event>();
|
|
allReceivedEvents.put(eventClass, events);
|
|
}
|
|
|
|
events.add(event);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
// debuggee should stop at the end of test
|
|
else if (event instanceof BreakpointEvent) {
|
|
breakpointWasReceived = true;
|
|
|
|
try {
|
|
// if execution was interrupted because of timeout don't check received
|
|
// events because it can consume too much time
|
|
if (!executionWasInterrupted) {
|
|
// get data from debuggee about all generated events
|
|
initExpectedEvents(testedEventData);
|
|
|
|
checkEvents();
|
|
} else
|
|
log.complain("WARNING: execution was interrupted because of timeout, test doesn't check received events");
|
|
} catch (Throwable t) {
|
|
unexpectedException(t);
|
|
}
|
|
|
|
vm.resume();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected Class<?> extraEventClasses[];
|
|
|
|
protected Class<?> missedEventClasses[];
|
|
|
|
/*
|
|
* If test can't strictly control event generation it may allow generation
|
|
* of extra events and unexpected events aren't treated as error
|
|
* (subclasses should specify what kinds of extra events are allowed)
|
|
*/
|
|
private Class<?>[] allowedExtraEvents() {
|
|
return extraEventClasses;
|
|
}
|
|
|
|
/*
|
|
* If test can't strictly control event generation case when debugger doesn't
|
|
* receive expected event may be not treated as failure
|
|
* (subclasses should specify what kinds of expected events can be not received)
|
|
*/
|
|
private Class<?>[] allowedMissedEvents() {
|
|
return missedEventClasses;
|
|
}
|
|
|
|
private boolean isExtraEventAllowed(Class<?> eventClass) {
|
|
return checkEvent(eventClass, allowedExtraEvents());
|
|
}
|
|
|
|
private boolean isMissedEventAllowed(Class<?> eventClass) {
|
|
return checkEvent(eventClass, allowedMissedEvents());
|
|
}
|
|
|
|
private boolean checkEvent(Class<?> eventClass, Class<?> classes[]) {
|
|
if (classes == null)
|
|
return false;
|
|
|
|
for (Class<?> klass : classes) {
|
|
if (klass.isAssignableFrom(eventClass))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// flag is modified from the event listener thread
|
|
private volatile boolean eventsNotGenerated;
|
|
|
|
/*
|
|
* Method returns true if test expects event generation, but events weren't
|
|
* generated. If test can't strictly control event generation such case isn't
|
|
* necessarily treated as test failure (sublasses of JDIEventsDebugger can
|
|
* for example try to rerun test several times).
|
|
*/
|
|
protected boolean eventsNotGenerated() {
|
|
return eventsNotGenerated;
|
|
}
|
|
|
|
/*
|
|
* Print debug information about expected and received events(this data
|
|
* should be stored in lists 'allExpectedEvents' and 'allReceivedEvents')
|
|
* and check that only expected events were received
|
|
*/
|
|
private void checkEvents() {
|
|
if (getAllExpectedEvents().size() > 0 && getAllReceivedEvents().size() == 0 && allowedMissedEvents() != null) {
|
|
log.display("WARNING: didn't receive any event");
|
|
eventsNotGenerated = true;
|
|
}
|
|
|
|
log.display("ALL RECEIVED EVENTS: ");
|
|
for (Event event : getAllReceivedEvents())
|
|
log.display("received event: " + eventToString(event));
|
|
|
|
log.display("ALL EXPECTED EVENTS: ");
|
|
for (DebuggerEventData.DebugEventData eventData : getAllExpectedEvents())
|
|
log.display("expected event: " + eventData);
|
|
|
|
// try to find received event in the list of expected events, if this event
|
|
// was found remove data about events from both lists
|
|
for (Class<?> eventClass : allReceivedEvents.keySet()) {
|
|
List<Event> receivedEvents = allReceivedEvents.get(eventClass);
|
|
List<DebuggerEventData.DebugEventData> expectedEvents = allExpectedEvents.get(eventClass);
|
|
|
|
for (Iterator<Event> allReceivedEventsIterator = receivedEvents.iterator();
|
|
allReceivedEventsIterator.hasNext();) {
|
|
|
|
Event event = allReceivedEventsIterator.next();
|
|
|
|
for (Iterator<DebuggerEventData.DebugEventData> allExpectedEventsIterator = expectedEvents.iterator();
|
|
allExpectedEventsIterator.hasNext();) {
|
|
|
|
DebuggerEventData.DebugEventData debugEventData = allExpectedEventsIterator.next();
|
|
|
|
if (debugEventData.shouldCheckEvent(event)) {
|
|
if (debugEventData.checkEvent(event)) {
|
|
allExpectedEventsIterator.remove();
|
|
allReceivedEventsIterator.remove();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Event> receivedEventsLeft = getAllReceivedEvents();
|
|
|
|
// check is all received events were found in expected
|
|
if (receivedEventsLeft.size() > 0) {
|
|
// if allowExtraEvents = true extra events are not treated as error
|
|
for (Event event : receivedEventsLeft) {
|
|
if (!isExtraEventAllowed(event.getClass())) {
|
|
setSuccess(false);
|
|
log.complain("Unexpected event " + eventToString(event));
|
|
}
|
|
}
|
|
}
|
|
|
|
List<DebuggerEventData.DebugEventData> expectedEventsLeft = getAllExpectedEvents();
|
|
|
|
// check is all expected events were received
|
|
if (expectedEventsLeft.size() > 0) {
|
|
for (DebuggerEventData.DebugEventData eventData : expectedEventsLeft) {
|
|
if (!isMissedEventAllowed(eventData.eventClass)) {
|
|
setSuccess(false);
|
|
log.complain("Expected event was not generated: " + eventData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private String eventToString(Event event) {
|
|
try {
|
|
if (event instanceof MonitorContendedEnterEvent)
|
|
return event + ". Details(MonitorContendedEnterEvent):" + " Monitor: " + ((MonitorContendedEnterEvent) event).monitor() + " Thread: "
|
|
+ ((MonitorContendedEnterEvent) event).thread();
|
|
else if (event instanceof MonitorContendedEnteredEvent)
|
|
return event + ". Details(MonitorContendedEnteredEvent):" + " Monitor: " + ((MonitorContendedEnteredEvent) event).monitor()
|
|
+ " Thread: " + ((MonitorContendedEnteredEvent) event).thread();
|
|
else if (event instanceof MonitorWaitEvent)
|
|
return event + ". Details(MonitorWaitEvent):" + " Monitor: " + ((MonitorWaitEvent) event).monitor() + " Thread: "
|
|
+ ((MonitorWaitEvent) event).thread() + " Timeout: " + ((MonitorWaitEvent) event).timeout();
|
|
else if (event instanceof MonitorWaitedEvent)
|
|
return event + ". Details(MonitorWaitedEvent):" + " Monitor: " + ((MonitorWaitedEvent) event).monitor() + " Thread: "
|
|
+ ((MonitorWaitedEvent) event).thread() + " Timedout: " + ((MonitorWaitedEvent) event).timedout();
|
|
|
|
return event.toString();
|
|
}
|
|
// this exception can occur when unexpected event was received
|
|
catch (ObjectCollectedException e) {
|
|
// allowExtraEvents=true extra events are not treated as error
|
|
if (!isExtraEventAllowed(event.getClass())) {
|
|
setSuccess(false);
|
|
e.printStackTrace(log.getOutStream());
|
|
log.complain("Unexpected ObjectCollectedException was caught, possible unexpected event was received");
|
|
}
|
|
|
|
return event.getClass().getName() + " [ Can't get full description, ObjectCollectedException was thrown ]";
|
|
}
|
|
}
|
|
|
|
// events received during test execution are stored here
|
|
private Map<Class<?>, List<Event>> allReceivedEvents = new HashMap<Class<?>, List<Event>>();
|
|
|
|
private List<Event> getAllReceivedEvents() {
|
|
List<Event> result = new LinkedList<Event>();
|
|
|
|
for (Class<?> eventClass : allReceivedEvents.keySet()) {
|
|
result.addAll(allReceivedEvents.get(eventClass));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected Map<Class<?>, List<DebuggerEventData.DebugEventData>> allExpectedEvents = new HashMap<Class<?>, List<DebuggerEventData.DebugEventData>>();
|
|
|
|
private List<DebuggerEventData.DebugEventData> getAllExpectedEvents() {
|
|
List<DebuggerEventData.DebugEventData> result = new LinkedList<DebuggerEventData.DebugEventData>();
|
|
|
|
for (Class<?> eventClass : allExpectedEvents.keySet()) {
|
|
result.addAll(allExpectedEvents.get(eventClass));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// find in debuggee VM and add to the list 'allExpectedEvents' instances
|
|
// of classes representing generated events
|
|
protected void initExpectedEvents(TestedEventData testedEventData[]) {
|
|
List<DebuggerEventData.DebugEventData> events;
|
|
|
|
ReferenceType referenceType = debuggee.classByName(debuggeeClassNameWithoutArgs());
|
|
|
|
ArrayReference generatedEvents = (ArrayReference) referenceType.getValue(referenceType.fieldByName("generatedEvents"));
|
|
|
|
for (TestedEventData eventData : testedEventData) {
|
|
events = new LinkedList<DebuggerEventData.DebugEventData>();
|
|
allExpectedEvents.put(eventData.eventClass, events);
|
|
}
|
|
|
|
for (int i = 0; i < generatedEvents.length(); i++) {
|
|
ObjectReference debuggeeMirror = (ObjectReference) generatedEvents.getValue(i);
|
|
|
|
for (TestedEventData eventData : testedEventData) {
|
|
|
|
if (debuggeeMirror.referenceType().name().equals(eventData.eventDataMirrorClass.getName())) {
|
|
events = allExpectedEvents.get(eventData.eventClass);
|
|
|
|
/*
|
|
* Use reflection to create object representing generated
|
|
* event Event data class should has constructor with single
|
|
* parameter of type com.sun.jdi.ObjectReference
|
|
*/
|
|
Constructor<?> constructor;
|
|
|
|
try {
|
|
constructor = eventData.eventDataClass.getConstructor(new Class[] { ObjectReference.class });
|
|
} catch (NoSuchMethodException e) {
|
|
TestBug testBug = new TestBug(
|
|
"Class representing debug event data should implement constructor with single parameter of type com.sun.jdi.ObjectReference");
|
|
testBug.initCause(e);
|
|
throw testBug;
|
|
}
|
|
|
|
DebuggerEventData.DebugEventData expectedEvent;
|
|
|
|
try {
|
|
expectedEvent = (DebuggerEventData.DebugEventData) constructor.newInstance(new Object[] { debuggeeMirror });
|
|
} catch (Exception e) {
|
|
TestBug testBug = new TestBug("Error when create debug event data: " + e);
|
|
testBug.initCause(e);
|
|
throw testBug;
|
|
}
|
|
events.add(expectedEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void printFiltersInfo() {
|
|
if (eventFilters.size() > 0) {
|
|
log.display("Use following filters: ");
|
|
|
|
for (EventFilters.DebugEventFilter filter : eventFilters)
|
|
log.display("" + filter);
|
|
} else {
|
|
log.display("Don't use event filters");
|
|
}
|
|
}
|
|
|
|
// filters used in test
|
|
protected List<EventFilters.DebugEventFilter> eventFilters = new LinkedList<EventFilters.DebugEventFilter>();
|
|
|
|
// Check is object generating events matches all filters,
|
|
// if object was accepted by all filters set this object's field
|
|
// 'saveEventData' to 'true', otherwise to 'false',
|
|
private void checkEventGenerator(ThreadReference eventThread, ObjectReference executor) {
|
|
boolean acceptedByFilters = true;
|
|
|
|
for (EventFilters.DebugEventFilter eventFilter : eventFilters) {
|
|
if (!eventFilter.isObjectMatch(executor, eventThread)) {
|
|
acceptedByFilters = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
try {
|
|
executor.setValue(executor.referenceType().fieldByName("saveEventData"), vm.mirrorOf(acceptedByFilters));
|
|
} catch (Exception e) {
|
|
throw new TestBug("Unexpected exception when change object field in debugee VM: " + e, e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find all event generating threads in debuggee VM (instances of
|
|
* nsk.share.jdi.MonitorEventsDebuggee$MonitorActionsThread) all these
|
|
* threads have special field - 'executor', this is object which generates
|
|
* events. If event generating thread and event generating object was
|
|
* accepted by all filters generating object should store information about
|
|
* all generated events and this information will be available for debugger
|
|
*/
|
|
protected void initializeEventGenerators() {
|
|
printFiltersInfo();
|
|
|
|
List<ThreadReference> eventThreads = getEventThreads();
|
|
|
|
for (ThreadReference eventThread : eventThreads) {
|
|
ObjectReference executor = (ObjectReference) eventThread.getValue(eventThread.referenceType().fieldByName("executor"));
|
|
checkEventGenerator(eventThread, executor);
|
|
}
|
|
|
|
// debuggee's main thread also can generate events, need to filter it in
|
|
// the same way as other threads
|
|
checkEventGenerator(debuggee.threadByName(JDIEventsDebuggee.MAIN_THREAD_NAME), findSingleObjectReference(debuggeeClassNameWithoutArgs()));
|
|
}
|
|
|
|
// find instances of nsk.share.jdi.MonitorEventsDebuggee$MonitorActionsThread
|
|
protected List<ThreadReference> getEventThreads() {
|
|
ReferenceType referenceType = debuggee.classByName(JDIEventsDebuggee.EventActionsThread.class.getName());
|
|
List<ObjectReference> debuggeeEventThreads = referenceType.instances(0);
|
|
|
|
List<ThreadReference> result = new LinkedList<ThreadReference>();
|
|
for (ObjectReference threadReference : debuggeeEventThreads)
|
|
result.add((ThreadReference) threadReference);
|
|
|
|
return result;
|
|
}
|
|
|
|
// find instances of nsk.share.jdi.MonitorEventsDebuggee$MonitorActionsThread,
|
|
// and get value of this object's field with name 'executor'
|
|
protected List<ObjectReference> getEventObjects() {
|
|
List<ObjectReference> eventObjects = new LinkedList<ObjectReference>();
|
|
|
|
List<ThreadReference> eventThreads = getEventThreads();
|
|
|
|
for (ThreadReference eventThread : eventThreads) {
|
|
eventObjects.add((ObjectReference) eventThread.getValue(eventThread.referenceType().fieldByName("executor")));
|
|
}
|
|
|
|
return eventObjects;
|
|
}
|
|
|
|
// remove all filters, received and expected events
|
|
private void clearTestData() {
|
|
allExpectedEvents.clear();
|
|
allReceivedEvents.clear();
|
|
eventFilters.clear();
|
|
eventsNotGenerated = false;
|
|
}
|
|
|
|
private boolean isEventSupported(EventType eventType) {
|
|
switch (eventType) {
|
|
case MONITOR_CONTENTED_ENTER:
|
|
case MONITOR_CONTENTED_ENTERED:
|
|
case MONITOR_WAIT:
|
|
case MONITOR_WAITED:
|
|
return vm.canRequestMonitorEvents();
|
|
|
|
default:
|
|
throw new TestBug("Invalid tested event type: " + eventType);
|
|
}
|
|
}
|
|
|
|
// create instance of EventRequest depending on given eventType
|
|
private EventRequest createTestRequest(EventType eventType) {
|
|
switch (eventType) {
|
|
case MONITOR_CONTENTED_ENTER:
|
|
return debuggee.getEventRequestManager().createMonitorContendedEnterRequest();
|
|
case MONITOR_CONTENTED_ENTERED:
|
|
return debuggee.getEventRequestManager().createMonitorContendedEnteredRequest();
|
|
case MONITOR_WAIT:
|
|
return debuggee.getEventRequestManager().createMonitorWaitRequest();
|
|
case MONITOR_WAITED:
|
|
return debuggee.getEventRequestManager().createMonitorWaitedRequest();
|
|
|
|
default:
|
|
throw new TestBug("Invalid tested event type: " + eventType);
|
|
}
|
|
}
|
|
|
|
// create command depending on given eventType
|
|
private String createCommand(EventType eventTypes[], int eventsNumber) {
|
|
String command = JDIEventsDebuggee.COMMAND_CREATE_ACTIONS_EXECUTORS + ":" + eventsNumber + ":";
|
|
|
|
for (EventType eventType : eventTypes) {
|
|
switch (eventType) {
|
|
case MONITOR_CONTENTED_ENTER:
|
|
case MONITOR_CONTENTED_ENTERED:
|
|
case MONITOR_WAIT:
|
|
case MONITOR_WAITED:
|
|
command += " " + eventType.name();
|
|
break;
|
|
|
|
default:
|
|
throw new TestBug("Invalid tested event type: " + eventType);
|
|
}
|
|
}
|
|
|
|
return command;
|
|
}
|
|
|
|
// get list of event requests from EventRequestManager depending on the given eventType
|
|
private List<?> getEventRequestsFromManager(EventType eventType) {
|
|
switch (eventType) {
|
|
case MONITOR_CONTENTED_ENTER:
|
|
return debuggee.getEventRequestManager().monitorContendedEnterRequests();
|
|
case MONITOR_CONTENTED_ENTERED:
|
|
return debuggee.getEventRequestManager().monitorContendedEnteredRequests();
|
|
case MONITOR_WAIT:
|
|
return debuggee.getEventRequestManager().monitorWaitRequests();
|
|
case MONITOR_WAITED:
|
|
return debuggee.getEventRequestManager().monitorWaitedRequests();
|
|
|
|
default:
|
|
throw new TestBug("Invalid tested event type: " + eventType);
|
|
}
|
|
}
|
|
|
|
protected EventHandler eventHandler;
|
|
|
|
private EventListener eventListener;
|
|
|
|
// perform event generation before test begins to load all using classes
|
|
// and avoid unexpected events related to classloading
|
|
protected void prepareDebuggee(EventType[] eventTypes) {
|
|
initDefaultBreakpoint();
|
|
|
|
eventHandler = new EventHandler(debuggee, log);
|
|
eventHandler.startListening();
|
|
|
|
// use event listener which just skip all received events
|
|
DummyEventListener dummyEventListener = new DummyEventListener();
|
|
eventHandler.addListener(dummyEventListener);
|
|
|
|
EventRequest eventRequests[] = new EventRequest[eventTypes.length];
|
|
|
|
for (int i = 0; i < eventRequests.length; i++) {
|
|
eventRequests[i] = createTestRequest(eventTypes[i]);
|
|
eventRequests[i].setSuspendPolicy(EventRequest.SUSPEND_NONE);
|
|
eventRequests[i].enable();
|
|
}
|
|
|
|
// debuggee should create event generators
|
|
pipe.println(createCommand(eventTypes, 1));
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
// start event generation
|
|
pipe.println(JDIEventsDebuggee.COMMAND_START_EXECUTION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
for (int i = 0; i < eventRequests.length; i++)
|
|
eventRequests[i].disable();
|
|
|
|
dummyEventListener.waitBreakpoint();
|
|
|
|
eventHandler.removeListener(dummyEventListener);
|
|
|
|
pipe.println(JDIEventsDebuggee.COMMAND_WAIT_EXECUTION_COMPLETION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
eventListener = new EventListener(eventDataByEventTypes(eventTypes));
|
|
eventHandler.addListener(eventListener);
|
|
}
|
|
|
|
/*
|
|
* Method for stress testing, allows specify requests for several event
|
|
* types, number of events which should be generated during test and number
|
|
* of threads which simultaneously generate events
|
|
*/
|
|
protected void stressTestTemplate(EventType[] eventTypes, int eventsNumber, int threadsNumber) {
|
|
for (EventType eventType : eventTypes) {
|
|
if (!isEventSupported(eventType)) {
|
|
log.complain("Can't test event because of it isn't supported: " + eventType);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Used framework is intended for testing event filters and debuggee
|
|
// creates 3 threads performing event generation and there is possibility
|
|
// to filter events from some threads
|
|
for (int i = 0; i < threadsNumber; i++) {
|
|
pipe.println(createCommand(eventTypes, eventsNumber));
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
}
|
|
|
|
// clear data(if this method is executed several times)
|
|
clearTestData();
|
|
|
|
initializeEventGenerators();
|
|
|
|
EventRequest eventRequests[] = new EventRequest[eventTypes.length];
|
|
|
|
// create event requests
|
|
for (int i = 0; i < eventTypes.length; i++) {
|
|
eventRequests[i] = createTestRequest(eventTypes[i]);
|
|
eventRequests[i].setSuspendPolicy(EventRequest.SUSPEND_NONE);
|
|
eventRequests[i].enable();
|
|
|
|
log.display("Use following event request: " + eventRequests[i]);
|
|
}
|
|
|
|
// stressTestTemplate can control only execution time, so ignore iteration count
|
|
stresser.start(0);
|
|
try {
|
|
// start event generation
|
|
pipe.println(JDIEventsDebuggee.COMMAND_START_EXECUTION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
// check is stressTime exceeded
|
|
while (stresser.continueExecution()) {
|
|
try {
|
|
Thread.sleep(100);
|
|
} catch (InterruptedException e) {
|
|
unexpectedException(e);
|
|
}
|
|
|
|
// periodically check is test completed
|
|
if (eventListener.breakpointWasReceived)
|
|
break;
|
|
}
|
|
} finally {
|
|
stresser.finish();
|
|
}
|
|
|
|
// debugger should interrupt test because of timeout
|
|
if (!eventListener.breakpointWasReceived) {
|
|
|
|
eventListener.executionWasInterrupted = true;
|
|
|
|
log.complain("WARNING: time is exceeded, interrupt test");
|
|
|
|
pipe.println(JDIEventsDebuggee.COMMAND_STOP_EXECUTION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
}
|
|
|
|
pipe.println(JDIEventsDebuggee.COMMAND_WAIT_EXECUTION_COMPLETION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
for (int i = 0; i < eventRequests.length; i++)
|
|
eventRequests[i].disable();
|
|
}
|
|
|
|
/*
|
|
* Hook method for subclasses implementing tests against event filters (it is called from eventFilterTestTemplate)
|
|
*/
|
|
protected EventFilters.DebugEventFilter[] createTestFilters(int testedFilterIndex) {
|
|
throw new TestBug("Not implemented");
|
|
}
|
|
|
|
/*
|
|
* Test event request with filter
|
|
*
|
|
* Also this method check following:
|
|
* - InvalidRequestStateException is thrown if add filter for deleted or enabled request
|
|
* - EventRequestManager.xxxRequests() returns created event request
|
|
*/
|
|
protected void eventFilterTestTemplate(EventType eventType, int testedFilterIndex) {
|
|
if (!isEventSupported(eventType)) {
|
|
log.complain("Can't test event because of it isn't supported: " + eventType);
|
|
return;
|
|
}
|
|
|
|
// debuggee create event generators
|
|
pipe.println(createCommand(new EventType[] { eventType }, 1));
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
clearTestData();
|
|
|
|
EventFilters.DebugEventFilter[] filters = createTestFilters(testedFilterIndex);
|
|
|
|
for (EventFilters.DebugEventFilter filter : filters) {
|
|
if (filter.isSupported(vm))
|
|
eventFilters.add(filter);
|
|
else {
|
|
log.complain("Can't test filter because of it isn't supported: " + filter);
|
|
return;
|
|
}
|
|
}
|
|
|
|
initializeEventGenerators();
|
|
|
|
// create event request
|
|
EventRequest request = createTestRequest(eventType);
|
|
request.setSuspendPolicy(EventRequest.SUSPEND_NONE);
|
|
|
|
// try add filter to enabled request, expect
|
|
// 'InvalidRequestStateException'
|
|
request.enable();
|
|
try {
|
|
for (EventFilters.DebugEventFilter filter : filters)
|
|
filter.addFilter(request);
|
|
|
|
setSuccess(false);
|
|
log.complain("Expected 'InvalidRequestStateException' was not thrown");
|
|
} catch (InvalidRequestStateException e) {
|
|
// expected exception
|
|
} catch (Throwable e) {
|
|
setSuccess(false);
|
|
log.complain("Unexpected exception: " + e);
|
|
e.printStackTrace(log.getOutStream());
|
|
}
|
|
|
|
// add event filter
|
|
request.disable();
|
|
|
|
for (EventFilters.DebugEventFilter filter : filters)
|
|
addFilter(filter, request);
|
|
|
|
request.enable();
|
|
|
|
log.display("Use following event request: " + request);
|
|
|
|
// start event generation
|
|
pipe.println(JDIEventsDebuggee.COMMAND_START_EXECUTION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
// wait execution completion
|
|
pipe.println(JDIEventsDebuggee.COMMAND_WAIT_EXECUTION_COMPLETION);
|
|
|
|
if (!isDebuggeeReady())
|
|
return;
|
|
|
|
// check that method EventRequestManager.xxxRequests() return created
|
|
// request
|
|
if (!getEventRequestsFromManager(eventType).contains(request)) {
|
|
setSuccess(false);
|
|
log.complain("EventRequestManager doesn't return request: " + request);
|
|
}
|
|
|
|
// delete event request
|
|
debuggee.getEventRequestManager().deleteEventRequest(request);
|
|
|
|
// try add filter to removed request, expect
|
|
// 'InvalidRequestStateException'
|
|
try {
|
|
for (EventFilters.DebugEventFilter filter : filters)
|
|
filter.addFilter(request);
|
|
setSuccess(false);
|
|
log.complain("Expected 'InvalidRequestStateException' was not thrown");
|
|
} catch (InvalidRequestStateException e) {
|
|
// expected exception
|
|
} catch (Throwable t) {
|
|
unexpectedException(t);
|
|
}
|
|
}
|
|
|
|
private void addFilter(EventFilters.DebugEventFilter filter, EventRequest request) {
|
|
try {
|
|
filter.addFilter(request);
|
|
} catch (Throwable t) {
|
|
unexpectedException(t);
|
|
}
|
|
}
|
|
|
|
// used to parse parameters -allowExtraEvents and -allowMissedEvents
|
|
private Class<?>[] createEventClassArray(String string) {
|
|
String eventTypesNames[] = string.split(":");
|
|
EventType eventTypes[] = new EventType[eventTypesNames.length];
|
|
try {
|
|
for (int i = 0; i < eventTypesNames.length; i++) {
|
|
eventTypes[i] = EventType.valueOf(eventTypesNames[i]);
|
|
}
|
|
} catch (IllegalArgumentException e) {
|
|
throw new TestBug("Invalid event type", e);
|
|
}
|
|
|
|
if (eventTypesNames.length == 0)
|
|
throw new TestBug("Event types weren't specified");
|
|
|
|
Class<?>[] result = new Class[eventTypesNames.length];
|
|
|
|
for (int i = 0; i < result.length; i++)
|
|
result[i] = testedEventData.get(eventTypes[i]).eventClass;
|
|
|
|
return result;
|
|
}
|
|
|
|
}
|