1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.harmony.jpda.tests.jdwp.share; 20 21 import org.apache.harmony.jpda.tests.framework.TestErrorException; 22 import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; 23 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; 24 import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; 25 import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodWithSuspensionDebuggee; 26 import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer; 27 28 import java.io.IOException; 29 30 /** 31 * Base class for tests checking invoke command with thread suspension. 32 */ 33 public abstract class JDWPInvokeMethodWithSuspensionTestCase extends JDWPSyncTestCase { 34 35 @Override getDebuggeeClassName()36 protected final String getDebuggeeClassName() { 37 return InvokeMethodWithSuspensionDebuggee.class.getName(); 38 } 39 40 /** 41 * This methods runs the {@link InvokeMethodWithSuspensionDebuggee} then sets up the 42 * following breakpoints: 43 * - breakpoint #1 to suspend the current thread only when the debuggee starts 44 * - breakpoint #2 to suspend all threads from a new thread started by a method called through 45 * JDWP. 46 * When we receive the event for breakpoint #1, we issue a request to invoke a method (or a 47 * constructor) in the event thread (suspended on the breakpoint). The event thread starts 48 * another child thread and loops as long as the child is running. However, we do not read the 49 * reply of the invoke yet. 50 * Next, we wait for the new thread to hit breakpoint #2. We resume all threads in the debuggee 51 * and read the reply of the invoke. 52 * Finally, we resume the thread that completed the invoke. 53 * 54 * @param invokedMethodName 55 * the name of the method to invoke 56 */ runInvokeMethodTest(String invokedMethodName)57 protected void runInvokeMethodTest(String invokedMethodName) { 58 // Wait for debuggee to start. 59 synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY); 60 61 long classID = getClassIDBySignature(getDebuggeeClassSignature()); 62 long invokeMethodID = getMethodID(classID, invokedMethodName); 63 64 // Set breakpoint with EVENT_THREAD suspend policy so only the event thread is suspended. 65 // We will invoke the method in this thread. 66 int breakpointEventThread = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID, 67 InvokeMethodWithSuspensionDebuggee.BREAKPOINT_EVENT_THREAD_METHOD_NAME, 68 JDWPConstants.SuspendPolicy.EVENT_THREAD); 69 70 // Set breakpoint with ALL suspend policy to suspend all threads. The thread started 71 // during the invoke will suspend all threads, including the thread executing the invoke. 72 int breakpointAllThreads = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID, 73 InvokeMethodWithSuspensionDebuggee.BREAKPOINT_ALL_THREADS_METHOD_NAME, 74 JDWPConstants.SuspendPolicy.ALL); 75 76 // Tell the debuggee to continue. 77 synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE); 78 79 // Wait for breakpoint and get id of suspended thread. 80 long eventThreadOne = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointEventThread); 81 int suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); 82 assertEquals("Invalid suspend count:", 1, suspendCount); 83 84 // Send command but does not read reply now. That invoked method starts another thread 85 // that is going to hit a breakpoint and suspend all threads, including the thread invoking 86 // the method. The invoke can only complete when that new thread terminates, which requires 87 // we send a VirtualMachine.Resume command. 88 final int invoke_options = 0; // resume/suspend all threads before/after the invoke. 89 CommandPacket invokeMethodCommand = buildInvokeCommand(eventThreadOne, classID, 90 invokeMethodID, invoke_options); 91 92 String commandName = getInvokeCommandName(); 93 logWriter.println("Send " + commandName); 94 int invokeMethodCommandID = -1; 95 try { 96 invokeMethodCommandID = debuggeeWrapper.vmMirror.sendCommand(invokeMethodCommand); 97 } catch (IOException e) { 98 logWriter.printError("Failed to send " + commandName, e); 99 fail(); 100 } 101 102 // Wait for 2nd breakpoint to hit. 103 long eventThreadTwo = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointAllThreads); 104 suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadTwo); 105 assertEquals("Invalid suspend count:", 1, suspendCount); 106 107 // At this point, the event thread #1 must have been suspended by event thread #2. Since 108 // the invoke has resumed it too, its suspend count must remain 1. 109 suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); 110 assertEquals("Invalid suspend count:", 1, suspendCount); 111 112 // Test that sending another invoke command in the same thread returns an error. 113 CommandPacket anotherInvokeMethodCommand = buildInvokeCommand(eventThreadOne, classID, 114 invokeMethodID, invoke_options); 115 ReplyPacket anotherInvokeMethodReply = 116 debuggeeWrapper.vmMirror.performCommand(anotherInvokeMethodCommand); 117 // The RI returns INVALID_THREAD error while ART returns ALREADY_INVOKING error which is 118 // more accurate. We only test we get an error so we can run the test with both runtimes. 119 assertTrue("Expected an error", 120 anotherInvokeMethodReply.getErrorCode() != JDWPConstants.Error.NONE); 121 122 // Send a VirtualMachine.Resume to resume all threads. This will unblock the event thread 123 // with the invoke in-progress. 124 logWriter.println("Resume all threads"); 125 resumeDebuggee(); 126 127 // Now we can read the invoke reply. 128 ReplyPacket invokeMethodReply = null; 129 try { 130 logWriter.println("Receiving reply for command " + invokeMethodCommandID + " ..."); 131 invokeMethodReply = debuggeeWrapper.vmMirror.receiveReply(invokeMethodCommandID); 132 } catch (Exception e) { 133 throw new TestErrorException("Did not receive invoke reply", e); 134 } 135 checkReplyPacket(invokeMethodReply, commandName + " command"); 136 logWriter.println("Received reply for command " + invokeMethodCommandID + " OK"); 137 138 checkInvokeReply(invokeMethodReply); 139 140 // The invoke is complete but the thread is still suspended: let's resume it now. 141 suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne); 142 assertEquals("Invalid suspend count:", 1, suspendCount); 143 144 logWriter.println("Resume event thread #1"); 145 debuggeeWrapper.vmMirror.resumeThread(eventThreadOne); 146 } 147 148 /** 149 * Builds the packed for the tested JDWP command. 150 * 151 * @param threadId 152 * the id of the thread that will invoke the method 153 * @param classID 154 * the class ID of the invoked method 155 * @param methodId 156 * the ID of the invoke method 157 * @param invokeOptions 158 * options for the invoke 159 * @return a command 160 */ buildInvokeCommand(long threadId, long classID, long methodId, int invokeOptions)161 protected abstract CommandPacket buildInvokeCommand(long threadId, long classID, 162 long methodId, int invokeOptions); 163 164 /** 165 * Returns the name of the command returned by {@link #buildInvokeCommand} for printing. 166 * 167 * @return the name of the invoke command sent to the debuggee 168 */ getInvokeCommandName()169 protected abstract String getInvokeCommandName(); 170 171 /** 172 * Checks the reply for the tested JDWP command. 173 * 174 * @param reply the reply of the invoke 175 */ checkInvokeReply(ReplyPacket reply)176 protected abstract void checkInvokeReply(ReplyPacket reply); 177 178 } 179