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 */ 18package org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand; 19 20import org.apache.harmony.jpda.tests.framework.LogWriter; 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.Location; 26import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; 27import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase; 28import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper; 29import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer; 30import org.apache.harmony.jpda.tests.share.JPDATestOptions; 31 32/** 33 * Base class for debuggers that are used in tests for DebuggerOnDemand functionality. 34 */ 35public abstract class LaunchedDebugger extends JDWPTestCase { 36 37 // synchronization channel between debugger and debuggee 38 protected JPDADebuggeeSynchronizer synchronizer; 39 40 // synchronization channel between debugger and test 41 protected JPDADebuggeeSynchronizer testSynchronizer; 42 43 // name of tested debuggee class 44 public static final String DEBUGGEE_CLASS_NAME = 45 "org/apache/harmony/jpda/tests/jdwp/DebuggerOnDemand/OnthowDebuggerLaunchDebuggee"; 46 47 // signature of the tested debuggee class 48 public static final String DEBUGGEE_CLASS_SIGNATURE = "L" + DEBUGGEE_CLASS_NAME + ";"; 49 50 /** 51 * This method is invoked right before attaching to debuggee VM. 52 * It forces to use attaching connector and fixed transport address. 53 */ 54/* 55 protected void beforeDebuggeeStart(JDWPOnDemandDebuggeeWrapper debugeeWrapper) { 56 settings.setAttachConnectorKind(); 57 if (settings.getTransportAddress() == null) { 58 settings.setTransportAddress(JPDADebuggerOnDemandOptions.DEFAULT_ATTACHING_ADDRESS); 59 } 60 logWriter.println("DEBUGGER: Use ATTACH connector kind"); 61 62 super.beforeDebuggeeStart(debuggeeWrapper); 63 } 64 */ 65 66 /** 67 * Overrides inherited method to resume debuggee VM and then to establish 68 * sync connection with debuggee and server. 69 */ 70 protected void internalSetUp() throws Exception { 71 72 // estabslish synch connection with test 73 logWriter.println("Establish synch connection between debugger and test"); 74 JPDADebuggerOnDemandOptions debuggerSettings = new JPDADebuggerOnDemandOptions(); 75 testSynchronizer = new JPDADebuggerOnDemandSynchronizer(logWriter, debuggerSettings); 76 testSynchronizer.startClient(); 77 logWriter.println("Established synch connection between debugger and test"); 78 79 // handle JDWP connection with debuggee 80 super.internalSetUp(); 81 82 // establish synch connection with debuggee 83 logWriter.println("Establish synch connection between debugger and debuggee"); 84 synchronizer = new JPDADebuggeeSynchronizer(logWriter, settings);; 85 synchronizer.startClient(); 86 logWriter.println("Established synch connection between debugger and debuggee"); 87 } 88 89 /** 90 * Creates wrapper for debuggee process. 91 */ 92 protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() { 93 return new JPDADebuggerOnDemandDebuggeeWrapper(settings, logWriter); 94 } 95 96 /** 97 * Receives initial EXCEPTION event if debuggee is suspended on event. 98 */ 99 protected void receiveInitialEvent() { 100 if (settings.isDebuggeeSuspend()) { 101 initialEvent = 102 debuggeeWrapper.vmMirror.receiveCertainEvent(JDWPConstants.EventKind.EXCEPTION); 103 logWriter.println("Received inital EXCEPTION event"); 104 debuggeeWrapper.resume(); 105 logWriter.println("Resumed debuggee VM"); 106 } 107 } 108 109 /** 110 * Overrides inherited method to close sync connection upon exit. 111 */ 112 protected void internalTearDown() { 113 // close synch connection with debuggee 114 if (synchronizer != null) { 115 synchronizer.stop(); 116 logWriter.println("Closed synch connection between debugger and debuggee"); 117 } 118 119 // close synch connection with test 120 if (testSynchronizer != null) { 121 testSynchronizer.stop(); 122 logWriter.println("Closed synch connection between debugger and test"); 123 } 124 125 // close connections with debuggee 126 super.internalTearDown(); 127 } 128 129 protected String getDebuggeeClassName() { 130 return null; 131 } 132 133 /////////////////////////////////////////////////////////////////// 134 135 /** 136 * This class contains information about frame of a java thread. 137 */ 138 public class FrameInfo { 139 long frameID; 140 Location location; 141 142 public FrameInfo(long frameID, Location location) { 143 super(); 144 this.frameID = frameID; 145 this.location = location; 146 } 147 148 /** 149 * @return Returns the frameID. 150 */ 151 public long getFrameID() { 152 return frameID; 153 } 154 /** 155 * @return Returns the location. 156 */ 157 public Location getLocation() { 158 return location; 159 } 160 } 161 162 protected FrameInfo[] jdwpGetFrames(long threadID, int startFrame, int length) { 163 CommandPacket packet = new CommandPacket( 164 JDWPCommands.ThreadReferenceCommandSet.CommandSetID, 165 JDWPCommands.ThreadReferenceCommandSet.FramesCommand); 166 packet.setNextValueAsThreadID(threadID); 167 packet.setNextValueAsInt(startFrame); 168 packet.setNextValueAsInt(length); 169 170 ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); 171 if (!checkReplyPacketWithoutFail(reply, "ThreadReference::FramesCommand command")) { 172 throw new TestErrorException("Error during performing ThreadReference::Frames command"); 173 } 174 175 int frames = reply.getNextValueAsInt(); 176 FrameInfo[] frameInfos = new FrameInfo[frames]; 177 for (int i = 0; i < frames; i++) { 178 long frameID = reply.getNextValueAsLong(); 179 Location location = reply.getNextValueAsLocation(); 180 frameInfos[i] = new FrameInfo(frameID, location); 181 } 182 return frameInfos; 183 } 184 185 protected long getClassIDBySignature(String signature) { 186 logWriter.println("=> Getting reference type ID for class: " + signature); 187 CommandPacket packet = new CommandPacket( 188 JDWPCommands.VirtualMachineCommandSet.CommandSetID, 189 JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand); 190 packet.setNextValueAsString(signature); 191 ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); 192 if (!checkReplyPacketWithoutFail(reply, "VirtualMachine::ClassesBySignature command")) { 193 throw new TestErrorException("Error during performing VirtualMachine::ClassesBySignature command"); 194 } 195 int classes = reply.getNextValueAsInt(); 196 logWriter.println("=> Returned number of classes: " + classes); 197 long classID = 0; 198 for (int i = 0; i < classes; i++) { 199 reply.getNextValueAsByte(); 200 classID = reply.getNextValueAsReferenceTypeID(); 201 reply.getNextValueAsInt(); 202 // we need the only class, even if there were multiply ones 203 break; 204 } 205 assertTrue("VirtualMachine::ClassesBySignature command returned invalid classID:<" + 206 classID + "> for signature " + signature, classID > 0); 207 return classID; 208 } 209 210 protected void printStackFrame(int NumberOfFrames, FrameInfo[] frameInfos) { 211 for (int i = 0; i < NumberOfFrames; i++) { 212 logWriter.println(" "); 213 logWriter 214 .println("=> #" + i + " frameID=" + frameInfos[i].frameID); 215 String methodName = ""; 216 try { 217 methodName = getMethodName(frameInfos[i].location.classID, 218 frameInfos[i].location.methodID); 219 } catch (TestErrorException e) { 220 throw new TestErrorException(e); 221 } 222 logWriter.println("=> method name=" + methodName); 223 } 224 logWriter.println(" "); 225 } 226 227 protected String getMethodName(long classID, long methodID) { 228 CommandPacket packet = new CommandPacket( 229 JDWPCommands.ReferenceTypeCommandSet.CommandSetID, 230 JDWPCommands.ReferenceTypeCommandSet.MethodsCommand); 231 packet.setNextValueAsClassID(classID); 232 ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); 233 if (!checkReplyPacketWithoutFail(reply, "ReferenceType::Methods command")) { 234 throw new TestErrorException("Error during performing ReferenceType::Method command"); 235 } 236 int methods = reply.getNextValueAsInt(); 237 for (int i = 0; i < methods; i++) { 238 long mid = reply.getNextValueAsMethodID(); 239 String name = reply.getNextValueAsString(); 240 reply.getNextValueAsString(); 241 reply.getNextValueAsInt(); 242 if (mid == methodID) { 243 return name; 244 } 245 } 246 return "unknown"; 247 } 248 249 ////////////////////////////////////////////////////////////////////////////////////// 250 251 /** 252 * This class provides functionality to establish synch connection between debugger and test. 253 */ 254 protected static class JPDADebuggerOnDemandSynchronizer extends JPDADebuggeeSynchronizer { 255 256 public JPDADebuggerOnDemandSynchronizer(LogWriter logWriter, JPDADebuggerOnDemandOptions settings) { 257 super(logWriter, settings); 258 this.settings = settings; 259 } 260 261 public int getSyncPortNumber() { 262 return ((JPDADebuggerOnDemandOptions)settings).getSyncDebuggerPortNumber(); 263 } 264 } 265 266 /** 267 * This class provides customization of DebuggeeWrapper that attaches to already launched debuggee. 268 */ 269 protected static class JPDADebuggerOnDemandDebuggeeWrapper extends JDWPUnitDebuggeeWrapper { 270 271 public JPDADebuggerOnDemandDebuggeeWrapper(JPDATestOptions settings, LogWriter logWriter) { 272 super(settings, logWriter); 273 } 274 275 /** 276 * Attaches to already launched debuggee process and 277 * establishes JDWP connection. 278 */ 279 public void start() { 280 try { 281 transport = createTransportWrapper(); 282 openConnection(); 283 logWriter.println("Established connection"); 284 } catch (Exception e) { 285 throw new TestErrorException(e); 286 } 287 } 288 289 /** 290 * Closes all connections but does not wait for debuggee process to exit. 291 */ 292 public void stop() { 293 disposeConnection(); 294 closeConnection(); 295 } 296 } 297 298 /** 299 * This class provides additional options to debuggers, that are used in DebuggerOnDemand tests. 300 * Currently the following additional options are supported: 301 * <ul> 302 * <li><i>jpda.settings.syncDebuggerPort</i> 303 * - port number for sync connection between debugger and test 304 * </ul> 305 * 306 */ 307 protected static class JPDADebuggerOnDemandOptions extends JPDATestOptions { 308 309 public static final String DEFAULT_SYNC_DEBUGGER_PORT = "9899"; 310 311 /** 312 * Returns port number string for sync connection between debugger and test. 313 */ 314 public String getSyncDebuggerPortString() { 315 return getProperty("jpda.settings.syncDebuggerPort", null); 316 } 317 318 /** 319 * Returns port number for sync connection between debugger and test. 320 */ 321 public int getSyncDebuggerPortNumber() { 322 String buf = getSyncDebuggerPortString(); 323 if (buf == null) { 324 buf = DEFAULT_SYNC_DEBUGGER_PORT; 325 } 326 327 try { 328 return Integer.parseInt(buf); 329 } catch (NumberFormatException e) { 330 throw new TestErrorException(e); 331 } 332 } 333 } 334} 335