001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.cldchi.tools.memoryprofiler.jdwp;
028:
029: import java.io.*;
030: import java.net.Socket;
031: import java.util.Enumeration;
032: import java.util.Vector;
033: import java.net.*;
034:
035: /**
036: * The <code>BackEndTest</code> class implemtens common debugger features
037: * such as:<br>
038: * <ul>
039: * <li>Connects to DebugAgent (either KdpProxy or Java), sends Handshake
040: * command and waits for VM_INIT event.
041: * <li>Contains a number of helper functions to work with JDWP
042: * </ul>
043: * This class encapsulates a huge amount of direct work with JDWP.<p>
044: * It's a good idea to place all common JDWP operations here.
045: *
046: * Notice that all <code>BackEndTest</code> methods are declared as static so
047: * you must never create instances of this class
048: *
049: * @see jdwp.jdwp
050: * @see jdwp.Reply
051: * @see jdwp.Tools
052: * @see jdwp.Packet
053: * @see jdwp.Command
054: * @see jdwp.Transport
055: * @see jdwp.ByteBuffer
056: * @see jdwp.BoundException
057: * @see jdwp.BreakpointInfo
058: * @see jdwp.SocketTransport
059: * @see jdwp.MethodDescriptor
060: *
061: */
062: class BackEndTest extends jdwp implements VMConnection {
063:
064: /**
065: * Debug time variable. To see a bit more verbose output
066: * turn it to <code>true</code>. In non-debug mode <b>must</b>
067: * be <code>false</code> otherwise identity of J2SE and KVM outputs
068: * is not guarantered.
069: */
070: public static boolean verbose = false;
071: public boolean is_connected = false;
072: /**
073: * This object executes all low-level work with JDWP
074: * (connection establishing/terminating, data exchange etc).
075: */
076: public SocketTransport debug;
077:
078: public void connect(String hostName, int port)
079: throws ConnectException {
080: try {
081: openConnections(hostName, port);
082: is_connected = true;
083: } catch (DebugeeException e) {
084: throw new ConnectException(e.getMessage());
085: } catch (IOException e) {
086: throw new ConnectException(e.getMessage());
087: }
088: }
089:
090: /**
091: * Connects to VM being debugged.
092: *
093: * @param debugee_server host name where VM being debugged is started
094: * @param debugee_port TCP/IP port for connection with VM being debugged
095: *
096: * @throws ConnectException connection establishing failed
097: * @throws DebugeeException VM being debugged sent JDWP package with
098: * incorrect content
099: */
100: private void openConnections(String debugee_server, int debugee_port)
101: throws ConnectException, DebugeeException {
102:
103: try {
104: Tools.wait(1000);
105: debug = new SocketTransport();
106:
107: debug.attachToServer(debugee_server, debugee_port);
108: debug.Handshake();
109: } catch (Exception e) {
110: System.err.println("* Exception.\n");
111: throw new ConnectException("Can't create JDWP connection.");
112: }
113:
114: System.out.println("* JDWP connection is installed.");
115: getIDSizes();
116: }
117:
118: /**
119: * Closes connection. This function is executed prior to
120: * KJDB finishing.
121: */
122: public void closeConnections() {
123: if (debug != null) {
124: try {
125: debug.done();
126: } catch (Exception e) {
127: }
128: ;
129: debug = null;
130: }
131: is_connected = false;
132: }
133:
134: /**
135: * Debug time method. Rewrite it if you'd like to see debug output
136: * not in the standard output
137: *
138: * @param s <code>String</code> to be printed
139: */
140: public static void print(String s) {
141: if (s.equals(""))
142: return;
143: System.out.println(s);
144: }
145:
146: /**
147: * Debug time method. Prints the content of the vector to standard
148: * output.
149: *
150: * @param v <code>java.util.Vector</code> object to be printed
151: */
152: public static void print(Vector v) throws Exception {
153: System.out.println(Tools.listVector(v));
154: }
155:
156: /**
157: * Debug time method. Prints the content of JDWP package in specified manner.
158: *
159: * @param p JDWP package to be printed
160: * @param how mask for proper representing of package content. For example,
161: * the mask <code>i(o)</code> will protected parsed as follows: first
162: * is <code>int</code> that determines the number of
163: * object references followed. All the follwed elements are object
164: * references
165: */
166: public static void print(Packet p, String how) throws Exception {
167: print(p.parse(how));
168: }
169:
170: /**
171: * This method sends JDWP command and does not wait for answer. It's
172: * used internally by other <code>BackEndTest</code> methods. Also
173: * it's a good idea to use this method in some cases when answer is not
174: * guaranteered (for example, if you send <code>VirtualMachine/Resume</code>
175: * JDWP command you may receive no answer because of debugging session
176: * finishing).
177: *
178: * @param c command to be sent
179: * @return command with ID field filled
180: */
181: public Command sendCommand(Command c) throws DebugeeException {
182: try {
183: debug.sendCommand(c);
184: } catch (IOException e) {
185: throw new DebugeeException(e.getMessage());
186: }
187: return c;
188: }
189:
190: /**
191: * Prints all JDWP replies that were not demanded yet. This method is
192: * invoked when no expected reply is received that is considered as
193: * fatal error.
194: */
195: public void printReplies() throws IOException {
196:
197: debug.receive();
198:
199: if (debug.Replies.size() == 0)
200: return;
201:
202: print("\nReplies:\n");
203: for (int i = 0; i < debug.Replies.size(); i++)
204: print("\n" + (Reply) debug.Replies.elementAt(i) + "\n");
205: }
206:
207: /**
208: * Sends JDWP command and waits for reply. If reply is not received in
209: * 100 seconds <code>DebugeeException</code> is raised. So large timeout
210: * is choosen because of possibility of using extremely slow
211: * or busy systems where VM being debugged is run. This method is
212: * used internally by other <code>BackEndTest</code> methods. Also
213: * it's a good idea to explicity invoke this method when non-zero
214: * error code in considered as correct or allowable answer.
215: *
216: * @param c JDWP command to be sent
217: * @return received reply packet
218: * @throws DebugeeException the command can't be sent or reply packet is
219: * not received in 100 seconds
220: * @throws IOException a generic I/O error occured
221: */
222: public Reply checkReply(Command c) throws IOException,
223: DebugeeException {
224: sendCommand(c);
225: Reply r = debug.receiveReply(c.getID(), 100000);
226: if (r.getErrorCode() == Reply.errNotAvailable) {
227: print("\nCommand:\n" + c);
228: printReplies();
229: throw new DebugeeException("Reply packet is not received.");
230: }
231: return r;
232: }
233:
234: /**
235: * Sends JDWP command and waits for reply. The method expects that
236: * JDWP reply packet will contain expected error code otherwise
237: * <code>DebugeeException</code> is raised. Currently this method is used
238: * internally by othe methods of <code>BackEndTest</code>.
239: *
240: * @param c JDWP command to be sent
241: * @param ErrorCode expected error code in reply packet
242: * @param Description if unexpected error code (in particular,
243: * <code>NONE</code>)is received this string will be included in
244: * the error message
245: * @return received reply packet
246: *
247: * @see #checkReplyF(Command, int, String)
248: */
249: public Reply checkReply(Command c, int ErrorCode, String Description)
250: throws IOException, DebugeeException {
251: Reply r = checkReply(c);
252: if (r.getErrorCode() != ErrorCode) {
253: print("\nCommand:\n" + c + "\nReply:\n" + r);
254: String m = "Unexpected error code";
255: if (!Description.equals(""))
256: Description = "Unexpected error code (" + Description
257: + ").";
258: else
259: Description = "Unexpected error code.";
260: throw new DebugeeException(Description);
261: }
262: return r;
263: }
264:
265: /**
266: * Determines the sizes (in bytes ) of field IDs,
267: * method IDs, object reference IDs and reference type IDs.
268: * These values are VM specific and are obtained via
269: * VirtualMachine/IDSizes JDWP command. This method is invoked by
270: * <code>openConnections(String, int)</code> method immediately after
271: * establishing JDWP connection.
272: *
273: * @see #openConnections(String, int)
274: */
275: public void getIDSizes() throws DebugeeException {
276: try {
277: Command c;
278: Reply r;
279: c = new Command(0x0107);
280: r = checkReply(c);
281: r.resetDataParser();
282: fieldIDSize = r.getInt();
283: methodIDSize = r.getInt();
284: objectIDSize = r.getInt();
285: referenceTypeIDSize = r.getInt();
286: frameIDSize = r.getInt();
287: } catch (Exception e) {
288: throw new DebugeeException(
289: "Exception occured in IDSizes function");
290: }
291: }
292:
293: //VMConnection implementation
294: public VMReply sendReplyCommand(int command, int[] params)
295: throws DebugeeException {
296: try {
297: Command cmd = new Command(command);
298: for (int i = 0; i < params.length; i++) {
299: cmd.addInt(params[i]);
300: }
301: Reply reply = checkReply(cmd);
302: reply.resetDataParser();
303: return reply;
304: } catch (IOException e) {
305: throw new DebugeeException(e.getMessage());
306: }
307: }
308:
309: //VMConnection implementation
310: public VMReply sendReplyCommand(int command)
311: throws DebugeeException {
312: try {
313: Reply reply = checkReply(new Command(command));
314: reply.resetDataParser();
315: return reply;
316: } catch (IOException e) {
317: throw new DebugeeException(e.getMessage());
318: }
319: }
320:
321: public void sendCommand(int command) throws DebugeeException {
322: sendCommand(new Command(command));
323: }
324:
325: public boolean isConnected() {
326: return is_connected;
327: }
328: }
|