001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018: package org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand;
019:
020: import org.apache.harmony.jpda.tests.framework.LogWriter;
021: import org.apache.harmony.jpda.tests.framework.TestErrorException;
022: import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
023: import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
024: import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
025: import org.apache.harmony.jpda.tests.framework.jdwp.Location;
026: import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
027: import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
028: import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
029: import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
030: import org.apache.harmony.jpda.tests.share.JPDATestOptions;
031:
032: /**
033: * Base class for debuggers that are used in tests for DebuggerOnDemand functionality.
034: */
035: public abstract class LaunchedDebugger extends JDWPTestCase {
036:
037: // synchronization channel between debugger and debuggee
038: protected JPDADebuggeeSynchronizer synchronizer;
039:
040: // synchronization channel between debugger and test
041: protected JPDADebuggeeSynchronizer testSynchronizer;
042:
043: // name of tested debuggee class
044: public static final String DEBUGGEE_CLASS_NAME = "org/apache/harmony/jpda/tests/jdwp/DebuggerOnDemand/OnthowDebuggerLaunchDebuggee";
045:
046: // signature of the tested debuggee class
047: public static final String DEBUGGEE_CLASS_SIGNATURE = "L"
048: + DEBUGGEE_CLASS_NAME + ";";
049:
050: /**
051: * This method is invoked right before attaching to debuggee VM.
052: * It forces to use attaching connector and fixed transport address.
053: */
054: /*
055: protected void beforeDebuggeeStart(JDWPOnDemandDebuggeeWrapper debugeeWrapper) {
056: settings.setAttachConnectorKind();
057: if (settings.getTransportAddress() == null) {
058: settings.setTransportAddress(JPDADebuggerOnDemandOptions.DEFAULT_ATTACHING_ADDRESS);
059: }
060: logWriter.println("DEBUGGER: Use ATTACH connector kind");
061:
062: super.beforeDebuggeeStart(debuggeeWrapper);
063: }
064: */
065:
066: /**
067: * Overrides inherited method to resume debuggee VM and then to establish
068: * sync connection with debuggee and server.
069: */
070: protected void internalSetUp() throws Exception {
071:
072: // estabslish synch connection with test
073: logWriter
074: .println("Establish synch connection between debugger and test");
075: JPDADebuggerOnDemandOptions debuggerSettings = new JPDADebuggerOnDemandOptions();
076: testSynchronizer = new JPDADebuggerOnDemandSynchronizer(
077: logWriter, debuggerSettings);
078: testSynchronizer.startClient();
079: logWriter
080: .println("Established synch connection between debugger and test");
081:
082: // handle JDWP connection with debuggee
083: super .internalSetUp();
084:
085: // establish synch connection with debuggee
086: logWriter
087: .println("Establish synch connection between debugger and debuggee");
088: synchronizer = new JPDADebuggeeSynchronizer(logWriter, settings);
089: ;
090: synchronizer.startClient();
091: logWriter
092: .println("Established synch connection between debugger and debuggee");
093: }
094:
095: /**
096: * Creates wrapper for debuggee process.
097: */
098: protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
099: return new JPDADebuggerOnDemandDebuggeeWrapper(settings,
100: logWriter);
101: }
102:
103: /**
104: * Receives initial EXCEPTION event if debuggee is suspended on event.
105: */
106: protected void receiveInitialEvent() {
107: if (settings.isDebuggeeSuspend()) {
108: initialEvent = debuggeeWrapper.vmMirror
109: .receiveCertainEvent(JDWPConstants.EventKind.EXCEPTION);
110: logWriter.println("Received inital EXCEPTION event");
111: debuggeeWrapper.resume();
112: logWriter.println("Resumed debuggee VM");
113: }
114: }
115:
116: /**
117: * Overrides inherited method to close sync connection upon exit.
118: */
119: protected void internalTearDown() {
120: // close synch connection with debuggee
121: if (synchronizer != null) {
122: synchronizer.stop();
123: logWriter
124: .println("Closed synch connection between debugger and debuggee");
125: }
126:
127: // close synch connection with test
128: if (testSynchronizer != null) {
129: testSynchronizer.stop();
130: logWriter
131: .println("Closed synch connection between debugger and test");
132: }
133:
134: // close connections with debuggee
135: super .internalTearDown();
136: }
137:
138: protected String getDebuggeeClassName() {
139: return null;
140: }
141:
142: ///////////////////////////////////////////////////////////////////
143:
144: /**
145: * This class contains information about frame of a java thread.
146: */
147: public class FrameInfo {
148: long frameID;
149: Location location;
150:
151: public FrameInfo(long frameID, Location location) {
152: super ();
153: this .frameID = frameID;
154: this .location = location;
155: }
156:
157: /**
158: * @return Returns the frameID.
159: */
160: public long getFrameID() {
161: return frameID;
162: }
163:
164: /**
165: * @return Returns the location.
166: */
167: public Location getLocation() {
168: return location;
169: }
170: }
171:
172: protected FrameInfo[] jdwpGetFrames(long threadID, int startFrame,
173: int length) {
174: CommandPacket packet = new CommandPacket(
175: JDWPCommands.ThreadReferenceCommandSet.CommandSetID,
176: JDWPCommands.ThreadReferenceCommandSet.FramesCommand);
177: packet.setNextValueAsThreadID(threadID);
178: packet.setNextValueAsInt(startFrame);
179: packet.setNextValueAsInt(length);
180:
181: ReplyPacket reply = debuggeeWrapper.vmMirror
182: .performCommand(packet);
183: if (!checkReplyPacketWithoutFail(reply,
184: "ThreadReference::FramesCommand command")) {
185: throw new TestErrorException(
186: "Error during performing ThreadReference::Frames command");
187: }
188:
189: int frames = reply.getNextValueAsInt();
190: FrameInfo[] frameInfos = new FrameInfo[frames];
191: for (int i = 0; i < frames; i++) {
192: long frameID = reply.getNextValueAsLong();
193: Location location = reply.getNextValueAsLocation();
194: frameInfos[i] = new FrameInfo(frameID, location);
195: }
196: return frameInfos;
197: }
198:
199: protected long getClassIDBySignature(String signature) {
200: logWriter.println("=> Getting reference type ID for class: "
201: + signature);
202: CommandPacket packet = new CommandPacket(
203: JDWPCommands.VirtualMachineCommandSet.CommandSetID,
204: JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand);
205: packet.setNextValueAsString(signature);
206: ReplyPacket reply = debuggeeWrapper.vmMirror
207: .performCommand(packet);
208: if (!checkReplyPacketWithoutFail(reply,
209: "VirtualMachine::ClassesBySignature command")) {
210: throw new TestErrorException(
211: "Error during performing VirtualMachine::ClassesBySignature command");
212: }
213: int classes = reply.getNextValueAsInt();
214: logWriter.println("=> Returned number of classes: " + classes);
215: long classID = 0;
216: for (int i = 0; i < classes; i++) {
217: reply.getNextValueAsByte();
218: classID = reply.getNextValueAsReferenceTypeID();
219: reply.getNextValueAsInt();
220: // we need the only class, even if there were multiply ones
221: break;
222: }
223: assertTrue(
224: "VirtualMachine::ClassesBySignature command returned invalid classID:<"
225: + classID + "> for signature " + signature,
226: classID > 0);
227: return classID;
228: }
229:
230: protected void printStackFrame(int NumberOfFrames,
231: FrameInfo[] frameInfos) {
232: for (int i = 0; i < NumberOfFrames; i++) {
233: logWriter.println(" ");
234: logWriter.println("=> #" + i + " frameID="
235: + frameInfos[i].frameID);
236: String methodName = "";
237: try {
238: methodName = getMethodName(
239: frameInfos[i].location.classID,
240: frameInfos[i].location.methodID);
241: } catch (TestErrorException e) {
242: throw new TestErrorException(e);
243: }
244: logWriter.println("=> method name=" + methodName);
245: }
246: logWriter.println(" ");
247: }
248:
249: protected String getMethodName(long classID, long methodID) {
250: CommandPacket packet = new CommandPacket(
251: JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
252: JDWPCommands.ReferenceTypeCommandSet.MethodsCommand);
253: packet.setNextValueAsClassID(classID);
254: ReplyPacket reply = debuggeeWrapper.vmMirror
255: .performCommand(packet);
256: if (!checkReplyPacketWithoutFail(reply,
257: "ReferenceType::Methods command")) {
258: throw new TestErrorException(
259: "Error during performing ReferenceType::Method command");
260: }
261: int methods = reply.getNextValueAsInt();
262: for (int i = 0; i < methods; i++) {
263: long mid = reply.getNextValueAsMethodID();
264: String name = reply.getNextValueAsString();
265: reply.getNextValueAsString();
266: reply.getNextValueAsInt();
267: if (mid == methodID) {
268: return name;
269: }
270: }
271: return "unknown";
272: }
273:
274: //////////////////////////////////////////////////////////////////////////////////////
275:
276: /**
277: * This class provides functionality to establish synch connection between debugger and test.
278: */
279: protected static class JPDADebuggerOnDemandSynchronizer extends
280: JPDADebuggeeSynchronizer {
281:
282: public JPDADebuggerOnDemandSynchronizer(LogWriter logWriter,
283: JPDADebuggerOnDemandOptions settings) {
284: super (logWriter, settings);
285: this .settings = settings;
286: }
287:
288: public int getSyncPortNumber() {
289: return ((JPDADebuggerOnDemandOptions) settings)
290: .getSyncDebuggerPortNumber();
291: }
292: }
293:
294: /**
295: * This class provides customization of DebuggeeWrapper that attaches to already launched debuggee.
296: */
297: protected static class JPDADebuggerOnDemandDebuggeeWrapper extends
298: JDWPUnitDebuggeeWrapper {
299:
300: public JPDADebuggerOnDemandDebuggeeWrapper(
301: JPDATestOptions settings, LogWriter logWriter) {
302: super (settings, logWriter);
303: }
304:
305: /**
306: * Attaches to already launched debuggee process and
307: * establishes JDWP connection.
308: */
309: public void start() {
310: try {
311: transport = createTransportWrapper();
312: openConnection();
313: logWriter.println("Established connection");
314: } catch (Exception e) {
315: throw new TestErrorException(e);
316: }
317: }
318:
319: /**
320: * Closes all connections but does not wait for debuggee process to exit.
321: */
322: public void stop() {
323: disposeConnection();
324: closeConnection();
325: }
326: }
327:
328: /**
329: * This class provides additional options to debuggers, that are used in DebuggerOnDemand tests.
330: * Currently the following additional options are supported:
331: * <ul>
332: * <li><i>jpda.settings.syncDebuggerPort</i>
333: * - port number for sync connection between debugger and test
334: * </ul>
335: *
336: */
337: protected static class JPDADebuggerOnDemandOptions extends
338: JPDATestOptions {
339:
340: public static final String DEFAULT_SYNC_DEBUGGER_PORT = "9899";
341:
342: /**
343: * Returns port number string for sync connection between debugger and test.
344: */
345: public String getSyncDebuggerPortString() {
346: return getProperty("jpda.settings.syncDebuggerPort", null);
347: }
348:
349: /**
350: * Returns port number for sync connection between debugger and test.
351: */
352: public int getSyncDebuggerPortNumber() {
353: String buf = getSyncDebuggerPortString();
354: if (buf == null) {
355: buf = DEFAULT_SYNC_DEBUGGER_PORT;
356: }
357:
358: try {
359: return Integer.parseInt(buf);
360: } catch (NumberFormatException e) {
361: throw new TestErrorException(e);
362: }
363: }
364: }
365: }
|