Home | History | Annotate | Download | only in share
      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
     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      */
     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      */
    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      */
    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      */
    176     protected abstract void checkInvokeReply(ReplyPacket reply);
    177 
    178 }
    179