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