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 19package org.apache.harmony.jpda.tests.jdwp.StackFrame; 20 21import org.apache.harmony.jpda.tests.framework.TestErrorException; 22import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; 23import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands; 24import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; 25import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; 26import org.apache.harmony.jpda.tests.framework.jdwp.Value; 27import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer; 28 29import java.util.ArrayList; 30import java.util.List; 31 32/** 33 * Base class for testing StackFrame.GetValues and StackFrame.SetValues commands. 34 */ 35public class JDWPStackFrameAccessTest extends JDWPStackFrameTestCase { 36 @Override 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 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 69 public String getVariableName() { 70 return variableName; 71 } 72 73 public Value getValueOnFirstSuspension() { 74 return valueOnFirstSuspension; 75 } 76 77 public Value getValueToSet() { 78 return valueToSet; 79 } 80 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 90 MethodInfo(String methodName) { 91 this.methodName = methodName; 92 } 93 94 public String getMethodName() { 95 return methodName; 96 } 97 98 public List<? extends VariableInfo> getVariables() { 99 return variables; 100 } 101 102 public void addVariable(String variableName, Value initialValue, Value setValue, 103 Value newValue) { 104 variables.add(new VariableInfo(variableName, initialValue, setValue, newValue)); 105 } 106 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 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 128 public StackFrameTester(String signalValue) { 129 this.signalValue = signalValue; 130 } 131 132 public String getSignalValue() { 133 return signalValue; 134 } 135 136 public List<? extends MethodInfo> getTestedMethods() { 137 return testedMethods; 138 } 139 140 public MethodInfo addTestMethod(String methodName) { 141 MethodInfo methodInfo = new MethodInfo(methodName); 142 testedMethods.add(methodInfo); 143 return methodInfo; 144 } 145 } 146 147 @Override 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 158 protected void internalTearDown() { 159 printTestLog("FINISHED"); 160 161 super.internalTearDown(); 162 } 163 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 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 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 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 */ 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 { 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 */ 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 */ 314 public byte getTestVariableJdwpTag() { 315 return testVariableJdwpTag; 316 } 317 318 private final String testVariableName; 319 private final byte testVariableJdwpTag; 320 321 } 322 323 protected StackFrameChecker(String breakpointMethodName, String testMethodName, 324 String testVariableName, byte testVariableJdwpTag) { 325 this(breakpointMethodName, testMethodName); 326 addTestedVariable(testVariableName, testVariableJdwpTag); 327 } 328 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 */ 339 public String getBreakpointMethodName() { 340 return breakpointMethodName; 341 } 342 343 /** 344 * Returns the name of the method calling the "breakpoint" method. 345 */ 346 public String getTestMethodName() { 347 return testMethodName; 348 } 349 350 public int addTestedVariable(String name, byte tag) { 351 testedVariables.add(new VariableNameAndTag(name, tag)); 352 return testedVariables.size() - 1; 353 } 354 355 public List<? extends VariableNameAndTag> getTestedVariables() { 356 return testedVariables; 357 } 358 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 */ 378 protected VarInfo getVariableInfo(long classID, long methodID, String variableName) { 379 VarInfo[] variables = jdwpGetVariableTable(classID, methodID); 380 return getVariableInfo(variables, variableName); 381 } 382 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 */ 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 */ 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 */ 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 */ 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