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 /**
20  * @author Anton V. Karnachuk
21  */
22 
23 /**
24  * Created on 11.04.2005
25  */
26 package org.apache.harmony.jpda.tests.jdwp.Events;
27 
28 import org.apache.harmony.jpda.tests.framework.Breakpoint;
29 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
30 import org.apache.harmony.jpda.tests.framework.jdwp.Location;
31 import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent;
32 import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
33 import org.apache.harmony.jpda.tests.framework.jdwp.TaggedObject;
34 import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
35 
36 
37 
38 /**
39  * JDWP Unit test for caught EXCEPTION event.
40  */
41 public class ExceptionCaughtTest extends ExceptionBaseTest {
42 
getDebuggeeClassName()43     protected String getDebuggeeClassName() {
44         return ExceptionCaughtDebuggee.class.getName();
45     }
46 
47     /**
48      * This testcase is for caught EXCEPTION event thrown from Java code. It tests
49      * the reported exception object.
50      * <BR>It runs ExceptionDebuggee that throws and catches a DebuggeeException with
51      * native and interpreter frames in between. It verifies the following:
52      * <ul>
53      * <li>the requested EXCEPTION event occurs</li>
54      * <li>the reported exception object is not null</li>
55      * <li>the reported exception object is instance of expected class with expected tag</li>
56      * </ul>
57      */
testExceptionEvent_ExceptionObject_FromJava()58     public void testExceptionEvent_ExceptionObject_FromJava() {
59         runExceptionObjectTest(false);
60     }
61 
62     /**
63      * This testcase is for caught EXCEPTION event thrown from native code. It tests
64      * the reported exception object.
65      * <BR>It runs ExceptionDebuggee that throws an exception from a native method
66      * and catches it. It verifies the following:
67      * <ul>
68      * <li>the requested EXCEPTION event occurs</li>
69      * <li>the reported exception object is not null</li>
70      * <li>the reported exception object is instance of expected class with expected tag</li>
71      * </ul>
72      */
testExceptionEvent_ExceptionObject_FromNative()73     public void testExceptionEvent_ExceptionObject_FromNative() {
74         runExceptionObjectTest(true);
75     }
76 
77     /**
78      * This testcase is for caught EXCEPTION event thrown from Java code. It tests
79      * the reported throw location.
80      * <BR>It runs ExceptionDebuggee that throws and catches a DebuggeeException with
81      * native and interpreter frames in between. It verifies the following:
82      * <ul>
83      * <li>the requested EXCEPTION event occurs</li>
84      * <li>the reported thread is not null</li>
85      * <li>the reported throw location is not null</li>
86      * <li>the reported throw location is equal to location of the top stack frame</li>
87      * </ul>
88      */
testExceptionEvent_ThrowLocation_FromJava()89     public void testExceptionEvent_ThrowLocation_FromJava() {
90         runThrowLocationTest(false);
91     }
92 
93     /**
94      * This testcase is for caught EXCEPTION event thrown from native code. It tests
95      * the reported throw location.
96      * <BR>It runs ExceptionDebuggee that throws an exception from a native method
97      * and catches it. It verifies the following:
98      * <ul>
99      * <li>the requested EXCEPTION event occurs</li>
100      * <li>the reported thread is not null</li>
101      * <li>the reported throw location is not null</li>
102      * <li>the reported throw location is equal to location of the top stack frame</li>
103      * </ul>
104      */
testExceptionEvent_ThrowLocation_FromNative()105     public void testExceptionEvent_ThrowLocation_FromNative() {
106         runThrowLocationTest(true);
107     }
108 
109     /**
110      * This testcase is for caught EXCEPTION event thrown from Java code. It tests
111      * the reported catch location.
112      * <BR>It runs ExceptionDebuggee that throws and catches a DebuggeeException with
113      * native and interpreter frames in between. It verifies the following:
114      * <ul>
115      * <li>the requested EXCEPTION event occurs</li>
116      * <li>the reported thread is not null</li>
117      * <li>the reported catch location is not null</li>
118      * <li>the reported catch location is different than the top stack frame</li>
119      * </ul>
120      */
testExceptionEvent_CatchLocation_FromJava()121     public void testExceptionEvent_CatchLocation_FromJava() {
122         runCatchLocationTest(false);
123     }
124 
125     /**
126      * This testcase is for caught EXCEPTION event thrown from native code. It tests
127      * the reported catch location.
128      * <BR>It runs ExceptionDebuggee that throws an exception from a native method
129      * and catches it. It verifies the following:
130      * <ul>
131      * <li>the requested EXCEPTION event occurs</li>
132      * <li>the reported thread is not null</li>
133      * <li>the reported catch location is not null</li>
134      * <li>the reported catch location is different than the top stack frame</li>
135      * </ul>
136      */
testExceptionEvent_CatchLocation_FromNative()137     public void testExceptionEvent_CatchLocation_FromNative() {
138         runCatchLocationTest(true);
139     }
140 
141     /**
142      * Requests and receives EXCEPTION event then checks reported exception object.
143      */
runExceptionObjectTest(boolean fromNative)144     private void runExceptionObjectTest(boolean fromNative) {
145         printTestLog("STARTED...");
146 
147         ParsedEvent.Event_EXCEPTION exceptionEvent = requestAndReceiveExceptionEvent(fromNative);
148         TaggedObject returnedException = exceptionEvent.getException();
149 
150         // assert that exception ObjectID is not null
151         printTestLog("returnedException.objectID = " + returnedException.objectID);
152         assertTrue("Returned exception object is null.", returnedException.objectID != 0);
153 
154         // assert that exception tag is OBJECT
155         printTestLog("returnedException.tag = " + returnedException.objectID);
156         assertEquals("Returned exception tag is not OBJECT.",
157                 JDWPConstants.Tag.OBJECT_TAG, returnedException.tag);
158 
159         // assert that exception class is the expected one
160         long typeID = getObjectReferenceType(returnedException.objectID);
161         String returnedExceptionSignature = getClassSignature(typeID);
162         printTestLog("returnedExceptionSignature = |" + returnedExceptionSignature+"|");
163         assertString("Invalid signature of returned exception,",
164                 getExpectedExceptionSignature(fromNative), returnedExceptionSignature);
165 
166         // resume debuggee
167         printTestLog("resume debuggee...");
168         debuggeeWrapper.vmMirror.resume();
169     }
170 
171     /**
172      * Requests and receives EXCEPTION event then checks reported throw location.
173      */
runThrowLocationTest(boolean fromNative)174     private void runThrowLocationTest(boolean fromNative) {
175         printTestLog("STARTED...");
176 
177         ParsedEvent.Event_EXCEPTION exceptionEvent = requestAndReceiveExceptionEvent(fromNative);
178         long returnedThread = exceptionEvent.getThreadID();
179         Location throwLocation = exceptionEvent.getLocation();
180 
181         // assert that exception thread is not null
182         printTestLog("returnedThread = " + returnedThread);
183         assertTrue("Returned exception ThreadID is null,", returnedThread != 0);
184 
185         // assert that exception location is not null
186         printTestLog("returnedExceptionLoc = " + throwLocation);
187         assertNotNull("Returned exception location is null,", throwLocation);
188 
189         // assert that top stack frame location is not null
190         Location topFrameLoc = getTopFrameLocation(returnedThread);
191         printTestLog("topFrameLoc = " + topFrameLoc);
192         assertNotNull("Returned top stack frame location is null,", topFrameLoc);
193 
194         // assert that locations of exception and top frame are equal
195         assertEquals("Different exception and top frame location tag,", topFrameLoc, throwLocation);
196 
197         // check throw location's method
198         String expectedThrowLocationClassSignature =
199                 getThrowLocationMethodClassSignature(fromNative);
200         String expectedThrowLocationMethodName = getThrowLocationMethodName(fromNative);
201         long debuggeeClassID = getClassIDBySignature(expectedThrowLocationClassSignature);
202         long debuggeeThrowMethodID = getMethodID(debuggeeClassID, expectedThrowLocationMethodName);
203         if (debuggeeClassID != throwLocation.classID ||
204             debuggeeThrowMethodID != throwLocation.methodID) {
205             StringBuilder builder = new StringBuilder("Invalid method for throw location:");
206             builder.append(" expected ");
207             builder.append(expectedThrowLocationClassSignature);
208             builder.append('.');
209             builder.append(expectedThrowLocationMethodName);
210             builder.append(" but got ");
211             builder.append(dumpLocation(throwLocation));
212             fail(builder.toString());
213         }
214 
215         // resume debuggee
216         printTestLog("resume debuggee...");
217         debuggeeWrapper.vmMirror.resume();
218     }
219 
220     /**
221      * Requests and receives EXCEPTION event then checks reported catch location.
222      */
runCatchLocationTest(boolean fromNative)223     private void runCatchLocationTest(boolean fromNative) {
224         printTestLog("STARTED...");
225 
226         ParsedEvent.Event_EXCEPTION exceptionEvent = requestAndReceiveExceptionEvent(fromNative);
227         long returnedThread = exceptionEvent.getThreadID();
228 
229         // assert that exception thread is not null
230         printTestLog("returnedThread = " + returnedThread);
231         assertTrue("Returned exception ThreadID is null,", returnedThread != 0);
232 
233         // assert that exception catch location is not null
234         Location catchLocation = exceptionEvent.getCatchLocation();
235         printTestLog("returnedExceptionLoc = " + catchLocation);
236         assertNotNull("Returned exception catch location is null,", catchLocation);
237 
238         // assert that top stack frame location is not null
239         Location topFrameLoc = getTopFrameLocation(returnedThread);
240         printTestLog("topFrameLoc = " + topFrameLoc);
241         assertNotNull("Returned top stack frame location is null,", topFrameLoc);
242         assertFalse("Same throw and catch locations", catchLocation.equals(topFrameLoc));
243 
244         // check catch location's method
245         String expectedCatchLocationClassSignature = getDebuggeeClassSignature();
246         String expectedCatchLocationMethodName = getCatchLocationMethodName(fromNative);
247         long debuggeeClassID = getClassIDBySignature(expectedCatchLocationClassSignature);
248         long debuggeeThrowMethodID = getMethodID(debuggeeClassID, expectedCatchLocationMethodName);
249         if (debuggeeClassID != catchLocation.classID ||
250             debuggeeThrowMethodID != catchLocation.methodID) {
251             StringBuilder builder = new StringBuilder("Invalid method for catch location:");
252             builder.append(" expected ");
253             builder.append(expectedCatchLocationClassSignature);
254             builder.append('.');
255             builder.append(expectedCatchLocationMethodName);
256             builder.append(" but got ");
257             builder.append(dumpLocation(catchLocation));
258             fail(builder.toString());
259         }
260 
261         // resume debuggee
262         printTestLog("resume debuggee...");
263         debuggeeWrapper.vmMirror.resume();
264     }
265 
266     /**
267      * Requests and receives EXCEPTION event then checks the received event kind,
268      * the received event request ID and the thread state.
269      * @return the exception event
270      */
requestAndReceiveExceptionEvent(boolean fromNative)271     private ParsedEvent.Event_EXCEPTION requestAndReceiveExceptionEvent(boolean fromNative) {
272         synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
273 
274         boolean isCatch = true;
275         boolean isUncatch = true;
276         printTestLog("=> setException(...)...");
277         String exceptionSignature = getExpectedExceptionSignature(fromNative);
278         ReplyPacket replyPacket = debuggeeWrapper.vmMirror.setException(exceptionSignature,
279                 isCatch, isUncatch);
280         int requestID = replyPacket.getNextValueAsInt();
281         assertAllDataRead(replyPacket);
282 
283         printTestLog("setException(...) DONE");
284 
285         if (!fromNative) {
286             // We want native and interpreter frames between the throw and the catch
287             // to check that we properly compute the catch location. We insert a native
288             // frame by using reflection in the code. To insert an interpreter frame,
289             // we install a breakpoint.
290             // Note: we use a Count modifier to avoid suspending on the breakpoint. This
291             // prevents from handling a suspend/resume sequence.
292             Breakpoint breakpoint = new Breakpoint(getDebuggeeClassSignature(),
293                     "throwDebuggeeExceptionWithTransition", 0);
294             debuggeeWrapper.vmMirror.setCountableBreakpoint(JDWPConstants.TypeTag.CLASS,
295                     breakpoint, JDWPConstants.SuspendPolicy.ALL, 10);
296         }
297 
298         printTestLog("send to Debuggee SGNL_CONTINUE...");
299 
300         String signalToSend = getSignalMessage(fromNative);
301         synchronizer.sendMessage(signalToSend);
302 
303         return receiveAndCheckExceptionEvent(requestID);
304     }
305 
getExpectedExceptionSignature(boolean fromNative)306     static String getExpectedExceptionSignature(boolean fromNative) {
307         Class<?> c = fromNative ? NullPointerException.class : DebuggeeException.class;
308         return getClassSignature(c);
309     }
310 
getThrowLocationMethodName(boolean fromNative)311     static String getThrowLocationMethodName(boolean fromNative) {
312         return fromNative ? "arraycopy" : "throwDebuggeeException";
313     }
314 
getThrowLocationMethodClassSignature(boolean fromNative)315     String getThrowLocationMethodClassSignature(boolean fromNative) {
316         return fromNative ? getClassSignature(System.class) : getDebuggeeClassSignature();
317     }
318 
getCatchLocationMethodName(boolean fromNative)319     static String getCatchLocationMethodName(boolean fromNative) {
320         return fromNative ? "testThrowAndCatchExceptionFromNative"
321                 : "testThrowAndCatchDebuggeeExceptionFromJava";
322     }
323 
getSignalMessage(boolean fromNative)324     static String getSignalMessage(boolean fromNative) {
325         return fromNative ? ExceptionCaughtDebuggee.TEST_EXCEPTION_FROM_NATIVE_METHOD
326                 : JPDADebuggeeSynchronizer.SGNL_CONTINUE;
327     }
328 }
329