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