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