0783dd69a5
Introduce test coverage for hidden class events Reviewed-by: lmesnik, amenkov
230 lines
9.0 KiB
Java
230 lines
9.0 KiB
Java
/*
|
|
* Copyright (c) 2020, 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.jdi.HiddenClass.events;
|
|
|
|
import com.sun.jdi.ClassType;
|
|
import com.sun.jdi.Field;
|
|
import com.sun.jdi.Method;
|
|
import com.sun.jdi.ReferenceType;
|
|
import com.sun.jdi.ThreadReference;
|
|
|
|
import com.sun.jdi.event.Event;
|
|
import com.sun.jdi.event.EventIterator;
|
|
import com.sun.jdi.event.EventSet;
|
|
import com.sun.jdi.event.BreakpointEvent;
|
|
import com.sun.jdi.event.ClassPrepareEvent;
|
|
import com.sun.jdi.event.ClassUnloadEvent;
|
|
import com.sun.jdi.event.ModificationWatchpointEvent;
|
|
import com.sun.jdi.request.EventRequest;
|
|
|
|
import jdk.test.lib.Asserts;
|
|
import nsk.jdi.HiddenClass.events.DebuggerBase;
|
|
|
|
import nsk.share.Log;
|
|
|
|
/* This is a special thread to handle hidden class related events.
|
|
* The thread is looping on accepting events until all the expected
|
|
* event types are received. */
|
|
public class EventHandler extends Thread {
|
|
private static final int TIMEOUT_DELTA = 1000; // milliseconds
|
|
private final DebuggerBase debuggerBase;
|
|
private final Log log;
|
|
|
|
private volatile boolean testFailed = false;
|
|
private boolean breakpointEventRecieived = false;
|
|
private boolean classPrepareEventRecieived = false;
|
|
private boolean classUnloadEventRecieived = false;
|
|
private boolean modificationWatchpointEventRecieived = false;
|
|
|
|
// Hidden class ReferenceType which is saved for debugger.
|
|
private ReferenceType hcRefType = null;
|
|
|
|
// This method is called by the debugger main thread.
|
|
static EventHandler createAndStart(DebuggerBase debuggerBase) {
|
|
// start EventHandler thread
|
|
EventHandler handler = new EventHandler(debuggerBase);
|
|
handler.start();
|
|
return handler;
|
|
}
|
|
|
|
// Constructor
|
|
private EventHandler(DebuggerBase debuggerBase) {
|
|
this.debuggerBase = debuggerBase;
|
|
this.log = debuggerBase.log;
|
|
log.display("\n# EventHandler is started");
|
|
}
|
|
|
|
// This method is called by the debugger main thread.
|
|
void waitForCompleteness() {
|
|
log.display("\n# Waiting for EventHandler to complete");
|
|
// wait for all expected events received or timeout exceeds
|
|
try {
|
|
super.join();
|
|
if (isAlive()) {
|
|
log.complain("FAILURE: Timeout for waiting event was exceeded");
|
|
interrupt();
|
|
testFailed = true;
|
|
} else {
|
|
log.display("# EventHandler completed");
|
|
}
|
|
} catch (InterruptedException ie) {
|
|
log.complain("FAIL: InterruptedException caught while waiting for eventHandler's death");
|
|
testFailed = true;
|
|
}
|
|
}
|
|
|
|
// This method is called by the debugger main thread to wait and get
|
|
// the hidden class reference type from its ClassPrepare event.
|
|
// The readyCmdSync with the debuggeee is not enough because a
|
|
// ClassPrepare event is delivered over JDWP protocol with a delay.
|
|
// A wait/notify sync is to ensure the debugger gets non-null value.
|
|
synchronized ReferenceType waitAndGetHCRefType() throws InterruptedException {
|
|
while (hcRefType == null) {
|
|
wait();
|
|
}
|
|
return hcRefType;
|
|
}
|
|
|
|
// This method is called by the debugger main thread.
|
|
boolean failedStatus() { return testFailed; }
|
|
|
|
// Save hidden class ReferenceType when its ClassPrepare event is received.
|
|
private synchronized void setHCRefType(ReferenceType type) {
|
|
hcRefType = type;
|
|
notifyAll();
|
|
}
|
|
|
|
private EventSet getNextEventSet() throws InterruptedException {
|
|
EventSet eventSet = debuggerBase.vm().eventQueue().remove(TIMEOUT_DELTA);
|
|
return eventSet;
|
|
}
|
|
|
|
// Breakpoint event handler.
|
|
private void checkBreakpointEvent(BreakpointEvent event) {
|
|
Method method = event.location().method();
|
|
ClassType type = (ClassType)method.declaringType();
|
|
|
|
// got expected event in a hidden class method
|
|
log.display("\nBreakpointEvent: " + event.toString());
|
|
log.display("BreakpointEvent: " + type.name() + "::" + method.name());
|
|
breakpointEventRecieived = true;
|
|
|
|
// find another method in the same hidden class
|
|
ThreadReference thread = event.thread();
|
|
Method methodToInvoke = debuggerBase.findMethod(type, "getHCField");
|
|
|
|
// invoke hidden class static method getNCField in debuggee VM
|
|
testFailed |= debuggerBase.invokeStaticMethod(thread, methodToInvoke);
|
|
}
|
|
|
|
// ClassPrepare event handler.
|
|
private void checkClassPrepareEvent(ClassPrepareEvent event) {
|
|
ReferenceType type = event.referenceType();
|
|
String name = type.name();
|
|
String sign = type.signature();
|
|
|
|
// set hidden class ReferenceType object for debugger
|
|
setHCRefType(type);
|
|
|
|
log.display("\nClassPrepareEvent: " + event.toString());
|
|
log.display("ClassPrepareEvent class name: " + name);
|
|
log.display("ClassPrepareEvent class sign: " + sign);
|
|
classPrepareEventRecieived = true;
|
|
Asserts.assertTrue(name.indexOf("HiddenClass") > 0 && name.indexOf("/0x") > 0,
|
|
"FAIL: unexpected class in ClassPrepareEvent");
|
|
}
|
|
|
|
// ClassUnload event handler.
|
|
private void checkClassUnloadEvent(ClassUnloadEvent event) {
|
|
EventRequest request = event.request();
|
|
String name = event.className();
|
|
|
|
log.display("\nClassUnloadEvent class name: " + name);
|
|
log.display("ClassUnloadEvent class sign: " + event.classSignature());
|
|
classUnloadEventRecieived = true;
|
|
Asserts.assertTrue(name.indexOf("HiddenClass") > 0 && name.indexOf("/0x") > 0,
|
|
"FAIL: unexpected class in ClassUnloadEvent");
|
|
}
|
|
|
|
// ModificationWatchpoint event handler.
|
|
private void checkModificationWatchpointEvent(ModificationWatchpointEvent event) {
|
|
EventRequest request = event.request();
|
|
Field field = event.field();
|
|
ReferenceType type = field.declaringType();
|
|
log.display("\nModificationWatchpointEvent: " + event.toString());
|
|
log.display("ModificationWatchpointEvent: field: " + type.name() + "::" + field.name());
|
|
log.display("ModificationWatchpointEvent: value: " + event.valueToBe().toString());
|
|
modificationWatchpointEventRecieived = true;
|
|
}
|
|
|
|
private void processEventSet(EventSet eventSet) throws InterruptedException {
|
|
// handle each event from the event set
|
|
EventIterator eventIterator = eventSet.eventIterator();
|
|
while (eventIterator.hasNext()) {
|
|
Event event = eventIterator.nextEvent();
|
|
|
|
if (!breakpointEventRecieived &&
|
|
event instanceof BreakpointEvent) {
|
|
checkBreakpointEvent((BreakpointEvent)event);
|
|
}
|
|
if (!classPrepareEventRecieived &&
|
|
event instanceof ClassPrepareEvent) {
|
|
checkClassPrepareEvent((ClassPrepareEvent)event);
|
|
}
|
|
if (!classUnloadEventRecieived &&
|
|
event instanceof ClassUnloadEvent) {
|
|
checkClassUnloadEvent((ClassUnloadEvent)event);
|
|
}
|
|
if (!modificationWatchpointEventRecieived &&
|
|
event instanceof ModificationWatchpointEvent) {
|
|
checkModificationWatchpointEvent((ModificationWatchpointEvent)event);
|
|
}
|
|
// ignore all other events
|
|
}
|
|
}
|
|
|
|
public void run() {
|
|
log.display("\nEventHandler started");
|
|
try {
|
|
// Handle events until all expected events are received.
|
|
while (!breakpointEventRecieived ||
|
|
!classPrepareEventRecieived ||
|
|
!classUnloadEventRecieived ||
|
|
!modificationWatchpointEventRecieived
|
|
) {
|
|
EventSet eventSet = getNextEventSet();
|
|
if (eventSet == null) {
|
|
continue;
|
|
}
|
|
processEventSet(eventSet);
|
|
eventSet.resume();
|
|
}
|
|
} catch (Throwable t) {
|
|
log.complain("Throwable in EventHandler: " + t);
|
|
testFailed = true;
|
|
}
|
|
log.display("\nEventHandler finished");
|
|
}
|
|
} // class EventHandler
|