blob: 19a30b1d3fc536e6f5879f9ee440ee43148da9d8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.debug.jdi.tests;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.Location;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.ClassUnloadEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.ClassUnloadRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.StepRequest;
/**
* Tests for the hot code replacement JDI extension.
*/
public class HotCodeReplacementTest extends AbstractJDITest {
/**
* Creates a new test.
*/
public HotCodeReplacementTest() {
super();
}
private void dropTopFrame(
ThreadReference thread,
org.eclipse.jdi.hcr.ThreadReference hcrThread) {
// Get stack size
int stackSize = 0;
try {
stackSize = thread.frames().size();
} catch (IncompatibleThreadStateException e) {
fail("dropTopFrame.1");
}
// Create and install step out request
StepRequest request =
fVM.eventRequestManager().createStepRequest(
thread,
StepRequest.STEP_MIN,
StepRequest.STEP_OUT);
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
request.enable();
// Prepare to receive the event
EventWaiter waiter = new EventWaiter(request, false);
fEventReader.addEventListener(waiter);
// Do return
boolean finallyBlocksSkipped = hcrThread.doReturn(null, true);
assertFalse("dropTopFrame.2", finallyBlocksSkipped);
// Wait for the event to come in
Event event = waitForEvent(waiter, 10000); // Wait 10s max
assertNotNull("dropTopFrame.3", event);
fEventReader.removeEventListener(waiter);
fVM.eventRequestManager().deleteEventRequest(request);
// Check thread has dropped top frame
assertTrue("dropTopFrame.4", thread.isSuspended());
int newStackSize = 0;
try {
newStackSize = thread.frames().size();
} catch (IncompatibleThreadStateException e) {
fail("dropTopFrame.5");
}
assertEquals("dropTopFrame.6", stackSize - 1, newStackSize);
}
/**
* Init the fields that are used by this test only.
*/
@Override
public void localSetUp() {
waitUntilReady();
}
/**
* Run all tests and output to standard output.
* @param args
*/
public static void main(String[] args) {
new HotCodeReplacementTest().runSuite(args);
}
/**
* Gets the name of the test case.
* @see junit.framework.TestCase#getName()
*/
@Override
public String getName() {
return "Hot code replacement extension to JDI (org.eclipse.jdi.hcr) tests";
}
private void reenterOnExit(ThreadReference thread) {
// Get top frame's location
Location location = null;
try {
StackFrame frame = thread.frames(0, 1).get(0);
location = frame.location();
} catch (IncompatibleThreadStateException e) {
fail("reenterOnExit.1");
}
// Create and install reenter step request
org.eclipse.jdi.hcr.EventRequestManager eventRequestManager =
(org.eclipse.jdi.hcr.EventRequestManager) fVM.eventRequestManager();
org.eclipse.jdi.hcr.ReenterStepRequest request =
eventRequestManager.createReenterStepRequest(thread);
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
request.enable();
// Prepare to receive the step event
EventWaiter waiter = new EventWaiter(request, false);
fEventReader.addEventListener(waiter);
// Resume thread with a doReturn so that the frame is reentered right away
((org.eclipse.jdi.hcr.ThreadReference) thread).doReturn(null, false);
// Wait for the step event to come in
StepEvent event = (StepEvent) waitForEvent(waiter, 10000); // Wait 10s max
assertNotNull("reenterOnExit.2", event);
fEventReader.removeEventListener(waiter);
fVM.eventRequestManager().deleteEventRequest(request);
// Check that the top frame location is as expected
Location newLocation = null;
try {
StackFrame frame = thread.frames(0, 1).get(0);
newLocation = frame.location();
} catch (IncompatibleThreadStateException e) {
fail("reenterOnExit.3");
}
assertFalse("reenterOnExit.4", newLocation.equals(location));
assertTrue("reenterOnExit.5", newLocation.codeIndex() <= location.codeIndex());
}
private void reloadClasses() {
// Gets the old class
ReferenceType oldType = getMainClass();
// Create and install class unload and class prepare event requests
ClassUnloadRequest unloadRequest =
fVM.eventRequestManager().createClassUnloadRequest();
unloadRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
unloadRequest.enable();
ClassPrepareRequest loadRequest =
fVM.eventRequestManager().createClassPrepareRequest();
loadRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
loadRequest.enable();
// Prepare to receive the class unload event
EventWaiter unloadEventWaiter = new EventWaiter(unloadRequest, true);
fEventReader.addEventListener(unloadEventWaiter);
// Prepare to receive the class prepare event
EventWaiter loadEventWaiter = new EventWaiter(loadRequest, true);
fEventReader.addEventListener(loadEventWaiter);
// Reload classes
org.eclipse.jdi.hcr.VirtualMachine vm =
(org.eclipse.jdi.hcr.VirtualMachine) fVM;
int result =
vm.classesHaveChanged(
new String[] { "org.eclipse.debug.jdi.tests.program.MainClass" });
assertEquals("reloadClasses.1", org.eclipse.jdi.hcr.VirtualMachine.RELOAD_SUCCESS, result);
// Wait for the class unload event to come in
ClassUnloadEvent unloadEvent =
(ClassUnloadEvent) waitForEvent(unloadEventWaiter, 10000);
// Wait 10s max
assertNotNull("reloadClasses.2", unloadEvent);
fEventReader.removeEventListener(unloadEventWaiter);
fVM.eventRequestManager().deleteEventRequest(unloadRequest);
assertEquals(
"reloadClasses.3",
"org.eclipse.debug.jdi.tests.program.MainClass",
unloadEvent.className());
// Wait for the class prepare event to come in
ClassPrepareEvent loadEvent =
(ClassPrepareEvent) waitForEvent(loadEventWaiter, 10000);
// Wait 10s max
assertNotNull("reloadClasses.4", loadEvent);
fEventReader.removeEventListener(loadEventWaiter);
fVM.eventRequestManager().deleteEventRequest(loadRequest);
ReferenceType newType = loadEvent.referenceType();
assertEquals(
"reloadClasses.5",
"org.eclipse.debug.jdi.tests.program.MainClass",
newType.name());
assertFalse("reloadClasses.6", oldType.equals(newType));
}
/**
* Use case 1:
* . get a thread and suspend it
* . get hot code replacement capabilities
* . drop the top a frame
* . reload some classes
* . request reeenter on exit
* . resume thread
* . get step event
* . get class file version for some classes
*/
public void testJDIUseCase1() {
// Get the suspended thread
ThreadReference thread = getThread();
assertTrue("1", thread.isSuspended());
assertEquals("2", 1, thread.suspendCount());
org.eclipse.jdi.hcr.ThreadReference hcrThread =
(org.eclipse.jdi.hcr.ThreadReference) thread;
org.eclipse.jdi.hcr.VirtualMachine vm =
(org.eclipse.jdi.hcr.VirtualMachine) fVM;
// Drop the top a frame
try {
if (vm.canDoReturn()) {
dropTopFrame(thread, hcrThread);
}
} catch (UnsupportedOperationException e) {
//not using a VM that supports the extension
return;
}
// Reload classes
if (vm.canReloadClasses()) {
reloadClasses();
}
// Reenter on exit
if (vm.canReenterOnExit()) {
reenterOnExit(thread);
}
}
/**
* Make sure the test leaves the VM in the same state it found it.
*/
@Override
public void localTearDown() {
waitUntilReady();
}
}