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