| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.jpda.tests.jdwp.share; |
| |
| import org.apache.harmony.jpda.tests.framework.TestErrorException; |
| import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; |
| import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; |
| import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; |
| import org.apache.harmony.jpda.tests.framework.jdwp.exceptions.TimeoutException; |
| import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodSuspendedTwiceDebuggee; |
| import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodWithSuspensionDebuggee; |
| import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer; |
| |
| import java.io.IOException; |
| |
| /** |
| * Base class for tests checking invoke command with thread suspended more than once before the |
| * invoke. |
| */ |
| public abstract class JDWPInvokeMethodSuspendedTwiceTestCase extends JDWPSyncTestCase { |
| |
| @Override |
| protected final String getDebuggeeClassName() { |
| return InvokeMethodSuspendedTwiceDebuggee.class.getName(); |
| } |
| |
| /** |
| * This methods runs the {@link InvokeMethodSuspendedTwiceDebuggee} then sets up a breakpoint |
| * to suspend the main thread only and signal it to continue. |
| * When the debuggee hits the breakpoint and we receive the event, we clear the breakpoint and |
| * suspend the thread with ThreadReference.Suspend command so its suspend count is greater |
| * than 1. |
| * Then, we send an invoke command for this thread and check we do not receive any reply (as |
| * the thread is still suspended) by catching a {@link TimeoutException}. |
| * Next, we resume the thread and check we do receive the reply with no timeout. |
| * |
| * @param invokedMethodName |
| * the name of the method to invoke |
| */ |
| protected void runInvokeMethodTest(String invokedMethodName) { |
| // Wait for debuggee to start. |
| synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY); |
| |
| long classID = getClassIDBySignature(getDebuggeeClassSignature()); |
| long invokeMethodID = getMethodID(classID, invokedMethodName); |
| |
| // Set breakpoint with EVENT_THREAD suspend policy. We will invoke the method in the thread |
| // suspended on this breakpoint. |
| int breakpointEventThread = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID, |
| InvokeMethodWithSuspensionDebuggee.BREAKPOINT_EVENT_THREAD_METHOD_NAME, |
| JDWPConstants.SuspendPolicy.EVENT_THREAD); |
| |
| // Tell the debuggee to continue. |
| synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE); |
| |
| // Wait for breakpoint and get id of suspended thread. |
| long eventThreadOne = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointEventThread); |
| int suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); |
| assertEquals("Invalid suspend count:", 1, suspendCount); |
| |
| // Clear breakpoint |
| debuggeeWrapper.vmMirror.clearBreakpoint(breakpointEventThread); |
| |
| // Suspend thread again so it's suspend count > 1 |
| debuggeeWrapper.vmMirror.suspendThread(eventThreadOne); |
| suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); |
| assertEquals("Invalid suspend count:", 2, suspendCount); |
| |
| // Send command but does not read the reply yet. That invoked method starts another thread |
| // that is going to hit a breakpoint and suspend all threads, including the thread invoking |
| // the method. The invoke can only complete when that new thread terminates, which requires |
| // we send a VirtualMachine.Resume command. |
| final int invoke_options = JDWPConstants.InvokeOptions.INVOKE_SINGLE_THREADED; |
| CommandPacket invokeMethodCommand = buildInvokeCommand(eventThreadOne, classID, |
| invokeMethodID, invoke_options); |
| |
| String commandName = getInvokeCommandName(); |
| logWriter.println("Send " + commandName); |
| int invokeMethodCommandID = -1; |
| try { |
| invokeMethodCommandID = debuggeeWrapper.vmMirror.sendCommand(invokeMethodCommand); |
| } catch (IOException e) { |
| logWriter.printError("Failed to send " + commandName, e); |
| fail(); |
| } |
| |
| // Check that we do not receive any reply. |
| ReplyPacket invokeMethodReply = null; |
| logWriter.println("Receiving reply for command " + invokeMethodCommandID + " ..."); |
| try { |
| invokeMethodReply = debuggeeWrapper.vmMirror.receiveReply(invokeMethodCommandID); |
| fail("#FAILURE: received reply too early (error " + |
| JDWPConstants.Error.getName(invokeMethodReply.getErrorCode()) + ")"); |
| } catch (TimeoutException e) { |
| logWriter.println("OK, did not receive reply for command " + invokeMethodCommandID); |
| } catch (InterruptedException e) { |
| throw new TestErrorException(e); |
| } catch (IOException e) { |
| throw new TestErrorException(e); |
| } |
| |
| // At this point, the event thread #1 should have been resumed once for the invoke. But |
| // it still suspended by us. |
| suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); |
| assertEquals("Invalid suspend count:", 1, suspendCount); |
| |
| // Test that sending another invoke command in the same thread returns an error. |
| CommandPacket anotherInvokeMethodCommand = buildInvokeCommand(eventThreadOne, classID, |
| invokeMethodID, invoke_options); |
| ReplyPacket anotherInvokeMethodReply = |
| debuggeeWrapper.vmMirror.performCommand(anotherInvokeMethodCommand); |
| // Note: the RI returns INVALID_THREAD but ALREADY_INVOKING is more explicit. |
| assertTrue("Another invoke should return an error", |
| anotherInvokeMethodReply.getErrorCode() != JDWPConstants.Error.NONE); |
| // checkReplyPacket(anotherInvokeMethodReply, "Invalid error code", |
| // JDWPConstants.Error.ALREADY_INVOKING); |
| |
| // Send a ThreadReference.Resume to resume event thread so it can invoke the method. |
| logWriter.println("Resume event thread"); |
| debuggeeWrapper.vmMirror.resumeThread(eventThreadOne); |
| |
| // Now we can read the invoke reply. |
| invokeMethodReply = null; |
| try { |
| logWriter.println("Receiving reply for command " + invokeMethodCommandID + " ..."); |
| invokeMethodReply = debuggeeWrapper.vmMirror.receiveReply(invokeMethodCommandID); |
| } catch (Exception e) { |
| throw new TestErrorException("Did not receive invoke reply", e); |
| } |
| checkReplyPacket(invokeMethodReply, commandName + " command"); |
| logWriter.println("Received reply for command " + invokeMethodCommandID + " OK"); |
| |
| checkInvokeReply(invokeMethodReply); |
| |
| // The invoke is complete but the thread is still suspended: let's resume it now. |
| suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); |
| assertEquals("Invalid suspend count:", 1, suspendCount); |
| |
| logWriter.println("Resume event thread #1"); |
| debuggeeWrapper.vmMirror.resumeThread(eventThreadOne); |
| } |
| |
| /** |
| * Builds the packed for the tested JDWP command. |
| * |
| * @param threadId |
| * the id of the thread that will invoke the method |
| * @param classID |
| * the class ID of the invoked method |
| * @param methodId |
| * the ID of the invoke method |
| * @param invokeOptions |
| * options for the invoke |
| * @return a command |
| */ |
| protected abstract CommandPacket buildInvokeCommand(long threadId, long classID, |
| long methodId, int invokeOptions); |
| |
| /** |
| * Returns the name of the command returned by {@link #buildInvokeCommand} for printing. |
| * |
| * @return the name of the invoke command sent to the debuggee |
| */ |
| protected abstract String getInvokeCommandName(); |
| |
| /** |
| * Checks the reply for the tested JDWP command. |
| * |
| * @param reply the reply of the invoke |
| */ |
| protected abstract void checkInvokeReply(ReplyPacket reply); |
| |
| } |