/******************************************************************************* * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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) { assertTrue("dropTopFrame.1", false); } // 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); assertTrue("dropTopFrame.2", !finallyBlocksSkipped); // Wait for the event to come in Event event = waitForEvent(waiter, 10000); // Wait 10s max assertTrue("dropTopFrame.3", event != null); 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) { assertTrue("dropTopFrame.5", false); } 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) { assertTrue("reenterOnExit.1", false); } // 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 assertTrue("reenterOnExit.2", event != null); 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) { assertTrue("reenterOnExit.3", false); } assertTrue("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 assertTrue("reloadClasses.2", unloadEvent != null); 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 assertTrue("reloadClasses.4", loadEvent != null); fEventReader.removeEventListener(loadEventWaiter); fVM.eventRequestManager().deleteEventRequest(loadRequest); ReferenceType newType = loadEvent.referenceType(); assertEquals( "reloadClasses.5", "org.eclipse.debug.jdi.tests.program.MainClass", newType.name()); assertTrue("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(); } }