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