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