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.StackFrame;
20 
21 import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
22 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
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.framework.jdwp.Value;
26 import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * Base class for testing StackFrame.GetValues and StackFrame.SetValues commands.
33  */
34 public class JDWPStackFrameAccessTest extends JDWPStackFrameTestCase {
35     @Override
getDebuggeeClassName()36     protected final String getDebuggeeClassName() {
37         return StackTrace002Debuggee.class.getName();
38     }
39 
40     static class VariableInfo {
41         private final String variableName;
42         private final Value initialValue;
43         private final Value newValue;
44 
VariableInfo(String variableName, Value initialValue, Value newValue)45         VariableInfo(String variableName, Value initialValue, Value newValue) {
46             this.variableName = variableName;
47             this.initialValue = initialValue;
48             this.newValue = newValue;
49         }
50 
getVariableName()51         public String getVariableName() {
52             return variableName;
53         }
54 
getInitialValue()55         public Value getInitialValue() {
56             return initialValue;
57         }
58 
getNewValue()59         public Value getNewValue() {
60             return newValue;
61         }
62     }
63 
64     static class MethodInfo {
65         private final String methodName;
66         private final List<VariableInfo> variables = new ArrayList<VariableInfo>();
67 
MethodInfo(String methodName)68         MethodInfo(String methodName) {
69             this.methodName = methodName;
70         }
71 
getMethodName()72         public String getMethodName() {
73             return methodName;
74         }
75 
getVariables()76         public List<? extends VariableInfo> getVariables() {
77             return variables;
78         }
79 
addVariable(String variableName, Value initialValue, Value newValue)80         public void addVariable(String variableName, Value initialValue, Value newValue) {
81             variables.add(new VariableInfo(variableName, initialValue, newValue));
82         }
83 
addVariable(String variableName, Value initialValue)84         public void addVariable(String variableName, Value initialValue) {
85             addVariable(variableName, initialValue, null);
86         }
87     }
88 
89     static class StackFrameTester {
90         // TODO remove when we no longer need breakpoint to suspend.
91         private final String breakpointMethodName;
92         private final String signalValue;
93         private final List<MethodInfo> testedMethods = new ArrayList<MethodInfo>();
94 
StackFrameTester(String breakpointMethodName, String signalValue)95         public StackFrameTester(String breakpointMethodName, String signalValue) {
96             this.breakpointMethodName = breakpointMethodName;
97             this.signalValue = signalValue;
98         }
99 
getBreakpointMethodName()100         public String getBreakpointMethodName() {
101             return breakpointMethodName;
102         }
103 
getSignalValue()104         public String getSignalValue() {
105             return signalValue;
106         }
107 
getTestedMethods()108         public List<? extends MethodInfo> getTestedMethods() {
109             return testedMethods;
110         }
111 
addTestMethod(String methodName)112         public MethodInfo addTestMethod(String methodName) {
113             MethodInfo methodInfo = new MethodInfo(methodName);
114             testedMethods.add(methodInfo);
115             return methodInfo;
116         }
117     }
118 
119     @Override
internalSetUp()120     protected void internalSetUp() throws Exception {
121         super.internalSetUp();
122 
123         printTestLog("STARTED");
124 
125         // Wait for debuggee to start.
126         synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
127     }
128 
129     @Override
internalTearDown()130     protected void internalTearDown() {
131         // Resume debuggee.
132         debuggeeWrapper.vmMirror.resume();
133 
134         printTestLog("FINISHED");
135 
136         super.internalTearDown();
137     }
138 
runStackFrameTest(StackFrameTester tester)139     protected void runStackFrameTest(StackFrameTester tester) {
140         // Get variable information.
141         long classID = getClassIDBySignature(getDebuggeeClassSignature());
142 
143         // Install breakpoint.
144         int breakpointRequestID = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID,
145                 tester.getBreakpointMethodName());
146 
147         // Signal debuggee with a custom message to execute the right method.
148         synchronizer.sendMessage(tester.getSignalValue());
149 
150         // Wait for 1st breakpoint hit.
151         long eventThreadID = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointRequestID);
152 
153         // Check every local variable of every method.
154         checkStackFrame(classID, eventThreadID, tester, true);
155 
156         // Resume debuggee.
157         debuggeeWrapper.vmMirror.resume();
158 
159         // Wait for 2nd breakpoint hit.
160         eventThreadID = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointRequestID);
161 
162         // Remove the breakpoint.
163         debuggeeWrapper.vmMirror.clearBreakpoint(breakpointRequestID);
164 
165         // Check every local variable of every method.
166         checkStackFrame(classID, eventThreadID, tester, false);
167     }
168 
169     /**
170      * Checks the stack frame contains the values we expect during the test.
171      *
172      * @param classID
173      *          the debuggee class ID
174      * @param eventThreadID
175      *          the thread ID of the event thread
176      * @param tester
177      *          an instance holding test logic
178      * @param firstSuspension
179      *          true if the execution is suspended by the first breakpoint, false otherwise.
180      */
checkStackFrame(long classID, long eventThreadID, StackFrameTester tester, boolean firstSuspension)181     private void checkStackFrame(long classID, long eventThreadID, StackFrameTester tester,
182                                  boolean firstSuspension) {
183         for (MethodInfo methodInfo : tester.getTestedMethods()) {
184             String testMethodName = methodInfo.getMethodName();
185             long testMethodID = getMethodID(classID, testMethodName);
186 
187             VarInfo[] variables = jdwpGetVariableTable(classID, testMethodID);
188             assertNotNull("No variable table for method " + testMethodName, variables);
189 
190             // Find the frame for the tested method.
191             FrameInfo testMethodFrame = getFrameInfo(eventThreadID, classID, testMethodID);
192             assertNotNull("Cannot find frame for method " + testMethodName, testMethodFrame);
193             logWriter.println("Found frame for " + testMethodName + ": " + testMethodFrame.frameID);
194 
195             // Test all variables.
196             List<? extends VariableInfo> testedVariables = methodInfo.getVariables();
197             assertTrue("Not enough variables in variable table",
198                     variables.length >= testedVariables.size());
199             for (VariableInfo variableInfo : testedVariables) {
200                 String variableName = variableInfo.getVariableName();
201 
202                 VarInfo testVarInfo = getVariableInfo(variables, variableName);
203                 assertNotNull("No variable info for \"" + variableName + "\"", testVarInfo);
204 
205                 logWriter.println("Checking value for variable \"" + variableName + "\"");
206 
207                 Value initialValue = variableInfo.getInitialValue();
208                 Value newValue = variableInfo.getNewValue();
209 
210                 // TODO specialize between GetValues and SetValues tests.
211                 if (firstSuspension) {
212                     if (newValue != null) {
213                         // Sets the new value in the tested variable.
214                         setVariableValue(eventThreadID, testMethodFrame.frameID,
215                                          testVarInfo.getSlot(), newValue);
216                     } else {
217                         // Check the value is the expected one.
218                         Value expected = initialValue;
219                         Value actual = getVariableValue(eventThreadID, testMethodFrame.frameID,
220                                 testVarInfo.getSlot(), expected.getTag());
221                         assertNotNull("No value for variable \"" + variableName + "\"", actual);
222                         assertEquals("Incorrect value variable \"" + variableName + "\"",
223                                 expected, actual);
224                     }
225                 } else {
226                     if (newValue != null) {
227                         // Check the value is the expected one.
228                         Value expected = newValue;
229                         Value actual = getVariableValue(eventThreadID, testMethodFrame.frameID,
230                                 testVarInfo.getSlot(), expected.getTag());
231                         assertNotNull("No value for variable \"" + variableName + "\"", actual);
232                         assertEquals("Incorrect value variable \"" + variableName + "\"",
233                                 expected, actual);
234                     } else {
235                         // Nothing to do.
236                     }
237                 }
238             }
239         }
240     }
241 
242     /**
243      * Base class for checking stack frame operations.
244      */
245     static abstract class StackFrameChecker {
246         static class VariableNameAndTag {
VariableNameAndTag(String testVariableName, byte testVariableJdwpTag)247             public VariableNameAndTag(String testVariableName, byte testVariableJdwpTag) {
248                 this.testVariableName = testVariableName;
249                 this.testVariableJdwpTag = testVariableJdwpTag;
250             }
251 
252             /**
253              * Returns the name of the variable (in the tested method) for which
254              * we want to check the value.
255              */
getTestVariableName()256             public String getTestVariableName() {
257                 return testVariableName;
258             }
259 
260             /**
261              * Returns the JDWP tag of the tested variable. This matches the
262              * declared type of the variable in the tested method.
263              *
264              * Note: it can be different from the value's tag we retrieve from
265              * the stack in the case of Object variable (like String).
266              */
getTestVariableJdwpTag()267             public byte getTestVariableJdwpTag() {
268                 return testVariableJdwpTag;
269             }
270 
271             private final String testVariableName;
272             private final byte testVariableJdwpTag;
273 
274         }
275 
StackFrameChecker(String breakpointMethodName, String testMethodName, String testVariableName, byte testVariableJdwpTag)276         protected StackFrameChecker(String breakpointMethodName, String testMethodName,
277                 String testVariableName, byte testVariableJdwpTag) {
278             this(breakpointMethodName, testMethodName);
279             addTestedVariable(testVariableName, testVariableJdwpTag);
280         }
281 
StackFrameChecker(String breakpointMethodName, String testMethodName)282         protected StackFrameChecker(String breakpointMethodName, String testMethodName) {
283             this.breakpointMethodName = breakpointMethodName;
284             this.testMethodName = testMethodName;
285             this.testedVariables = new ArrayList<VariableNameAndTag>();
286         }
287 
288         /**
289          * Returns the name of the method where a breakpoint is installed
290          * to suspend the debuggee.
291          */
getBreakpointMethodName()292         public String getBreakpointMethodName() {
293             return breakpointMethodName;
294         }
295 
296         /**
297          * Returns the name of the method calling the "breakpoint" method.
298          */
getTestMethodName()299         public String getTestMethodName() {
300             return testMethodName;
301         }
302 
addTestedVariable(String name, byte tag)303         public int addTestedVariable(String name, byte tag) {
304             testedVariables.add(new VariableNameAndTag(name, tag));
305             return testedVariables.size() - 1;
306         }
307 
getTestedVariables()308         public List<? extends VariableNameAndTag> getTestedVariables() {
309             return testedVariables;
310         }
311 
getTestVariableName(int idx)312         protected String getTestVariableName(int idx) {
313             return getTestedVariables().get(idx).getTestVariableName();
314         }
315 
316         private final String breakpointMethodName;
317         private final String testMethodName;
318         private final List<VariableNameAndTag> testedVariables;
319     }
320 
321     /**
322      * Returns the {@link VarInfo} of the given variable in the given method.
323      *
324      * @param classID
325      *          the ID of the declaring class of the method
326      * @param methodID
327      *          the ID of the method
328      * @param variableName
329      *          the name of the variable we look for
330      */
getVariableInfo(long classID, long methodID, String variableName)331     protected VarInfo getVariableInfo(long classID, long methodID, String variableName) {
332         VarInfo[] variables = jdwpGetVariableTable(classID, methodID);
333         return getVariableInfo(variables, variableName);
334     }
335 
getVariableInfo(VarInfo[] variables, String variableName)336     protected VarInfo getVariableInfo(VarInfo[] variables, String variableName) {
337         for (VarInfo variable : variables) {
338             if (variable.name.equals(variableName)) {
339                 return variable;
340             }
341         }
342         return null;
343     }
344 
345     /**
346      * Returns the {@link FrameInfo} of the most recent frame matching the given method.
347      *
348      * @param threadID
349      *          the ID of the thread where to look for the frame
350      * @param classID
351      *          the ID of the declaring class of the method
352      * @param methodID
353      *          the ID of the method
354      */
getFrameInfo(long threadID, long classID, long methodID)355     protected FrameInfo getFrameInfo(long threadID, long classID, long methodID) {
356         int frameCount = jdwpGetFrameCount(threadID);
357 
358         // There should be at least 2 frames: the breakpoint method and its caller.
359         assertTrue("Not enough frames", frameCount > 2);
360 
361         FrameInfo[] frames = jdwpGetFrames(threadID, 0, frameCount);
362         for (FrameInfo frameInfo : frames) {
363             if (frameInfo.location.classID == classID &&
364                     frameInfo.location.methodID == methodID) {
365                 return frameInfo;
366             }
367         }
368         return null;
369     }
370 
371     /**
372      * Returns the value of a local variable in the stack.
373      *
374      * @param threadID
375      *          the ID of the thread of the stack
376      * @param frameID
377      *          the ID of the frame of the stack
378      * @param slot
379      *          the slot of the variable in the stack
380      * @param tag
381      *          the type of the value
382      */
getVariableValue(long threadID, long frameID, int slot, byte tag)383     protected Value getVariableValue(long threadID, long frameID, int slot, byte tag) {
384         logWriter.println(" Send StackFrame::GetValues: threadID=" + threadID +
385                           ", frameID=" + frameID + ", slot=" + slot +
386                           ", tag=" + JDWPConstants.Tag.getName(tag));
387 
388         // Send StackFrame::GetValues command.
389         CommandPacket packet = new CommandPacket(
390                 JDWPCommands.StackFrameCommandSet.CommandSetID,
391                 JDWPCommands.StackFrameCommandSet.GetValuesCommand);
392         packet.setNextValueAsThreadID(threadID);
393         packet.setNextValueAsFrameID(frameID);
394         packet.setNextValueAsInt(1);
395         packet.setNextValueAsInt(slot);
396         packet.setNextValueAsByte(tag);
397 
398         // Check reply has no error.
399         ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
400         checkReplyPacket(reply, "StackFrame::GetValues command");
401 
402         // Check we have 1 value.
403         int numberOfValues = reply.getNextValueAsInt();
404         assertEquals("Incorrect number of values", 1, numberOfValues);
405 
406         // Check the value tag is correct.
407         Value value = reply.getNextValueAsValue();
408         logWriter.println(" Received value " + value);
409         assertEquals("Invalid value tag", tag, value.getTag());
410 
411         assertAllDataRead(reply);
412         return value;
413     }
414 
415     /**
416      * Sets the value of a local variable in the stack.
417      *
418      * @param threadID
419      *          the ID of the thread of the stack
420      * @param frameID
421      *          the ID of the frame of the stack
422      * @param slot
423      *          the slot of the variable in the stack
424      * @param newValue
425      *          the new value to set
426      */
setVariableValue(long threadID, long frameID, int slot, Value newValue)427     protected void setVariableValue(long threadID, long frameID, int slot, Value newValue) {
428         logWriter.println(" Send StackFrame::SetValues: threadID=" + threadID +
429                           ", frameID=" + frameID + ", slot=" + slot +
430                           ", value=" + newValue);
431 
432         // Send StackFrame::SetValues command.
433         CommandPacket packet = new CommandPacket(JDWPCommands.StackFrameCommandSet.CommandSetID,
434                                                  JDWPCommands.StackFrameCommandSet.SetValuesCommand);
435         packet.setNextValueAsThreadID(threadID);
436         packet.setNextValueAsFrameID(frameID);
437         packet.setNextValueAsInt(1);
438         packet.setNextValueAsInt(slot);
439         packet.setNextValueAsValue(newValue);
440 
441         // Check reply has no error.
442         ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
443         checkReplyPacket(reply, "StackFrame::SetValues command");
444     }
445 
446     /**
447      * Returns the value of the static field identified by the given name in
448      * the given class.
449      *
450      * @param classID
451      *          the ID of the declaring class of the static field
452      * @param fieldName
453      *          the name of the static field in the class.
454      */
getStaticFieldValue(long classID, String fieldName)455     protected Value getStaticFieldValue(long classID, String fieldName) {
456         long fieldID = debuggeeWrapper.vmMirror.getFieldID(classID, fieldName);
457         long[] fieldIDs = new long[]{ fieldID };
458         Value[] fieldValues = debuggeeWrapper.vmMirror.getReferenceTypeValues(classID, fieldIDs);
459         assertNotNull("No field values", fieldValues);
460         assertEquals("Invalid field values count", fieldValues.length, 1);
461         return fieldValues[0];
462     }
463 }
464