001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdi.internal.spy;
011:
012: import java.io.BufferedInputStream;
013: import java.io.BufferedOutputStream;
014: import java.io.DataInputStream;
015: import java.io.DataOutputStream;
016: import java.io.EOFException;
017: import java.io.File;
018: import java.io.FileNotFoundException;
019: import java.io.FileOutputStream;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.OutputStream;
023: import java.net.InetAddress;
024: import java.net.ServerSocket;
025: import java.net.Socket;
026: import java.net.SocketException;
027: import com.ibm.icu.text.MessageFormat;
028: import java.util.HashMap;
029: import java.util.Map;
030:
031: /**
032: * This class can be used to spy all JDWP packets. It should be configured 'in between' the debugger
033: * application and the VM (or J9 debug proxy).
034: * Its parameters are:
035: * 1) The port number to which the debugger application connects;
036: * 2) The name of the host on which the VM or proxy waits for a JDWP connection;
037: * 3) The port number on which the VM or proxy waits for a JDWP connection;
038: * 4) The file where the trace is written to.
039: *
040: * Note that if this program is used for tracing JDWP activity of Leapfrog, the
041: * 'debug remote program' option must be used, and the J9 proxy must first be started up by hand
042: * on the port to which Leapfrog will connect.
043: * The J9 proxy that is started up by Leapfrog is not used and will return immediately.
044: */
045: public class TcpipSpy extends Thread {
046:
047: private static final byte[] handshakeBytes = "JDWP-Handshake".getBytes(); //$NON-NLS-1$
048: private boolean fVMtoDebugger;
049: private DataInputStream fDataIn;
050: private DataOutputStream fDataOut;
051:
052: private static VerbosePacketStream out = new VerbosePacketStream(
053: System.out);
054: private static Map fPackets = new HashMap();
055:
056: private static int fFieldIDSize;
057: private static int fMethodIDSize;
058: private static int fObjectIDSize;
059: private static int fReferenceTypeIDSize;
060: private static int fFrameIDSize;
061: private static boolean fHasSizes;
062:
063: public TcpipSpy(boolean VMtoDebugger, InputStream in,
064: OutputStream out) {
065: fVMtoDebugger = VMtoDebugger;
066: fDataIn = new DataInputStream(new BufferedInputStream(in));
067: fDataOut = new DataOutputStream(new BufferedOutputStream(out));
068: fHasSizes = false;
069: }
070:
071: public static void main(String[] args) {
072: int inPort = 0;
073: String serverHost = null;
074: int outPort = 0;
075: String outputFile = null;
076: try {
077: inPort = Integer.parseInt(args[0]);
078: serverHost = args[1];
079: outPort = Integer.parseInt(args[2]);
080: if (args.length > 3) {
081: outputFile = args[3];
082: }
083: } catch (Exception e) {
084: out
085: .println("usage: TcpipSpy <client port> <server host> <server port> [<output file>]"); //$NON-NLS-1$
086: System.exit(-1);
087: }
088:
089: if (outputFile != null) {
090: File file = new File(outputFile);
091: out
092: .println(MessageFormat
093: .format(
094: "Writing output to {0}", new String[] { file.getAbsolutePath() })); //$NON-NLS-1$
095: try {
096: out = new VerbosePacketStream(new BufferedOutputStream(
097: new FileOutputStream(file)));
098: } catch (FileNotFoundException e) {
099: out
100: .println(MessageFormat
101: .format(
102: "Could not open {0}. Using stdout instead", new String[] { file.getAbsolutePath() })); //$NON-NLS-1$
103: }
104: }
105: out.println();
106: try {
107: ServerSocket serverSock = new ServerSocket(inPort);
108: Socket inSock = serverSock.accept();
109: Socket outSock = new Socket(InetAddress
110: .getByName(serverHost), outPort);
111: new TcpipSpy(false, inSock.getInputStream(), outSock
112: .getOutputStream()).start();
113: new TcpipSpy(true, outSock.getInputStream(), inSock
114: .getOutputStream()).start();
115: } catch (Exception e) {
116: out.println(e);
117: }
118: }
119:
120: public void run() {
121: try {
122: // Skip handshake.
123: int handshakeLength;
124:
125: handshakeLength = handshakeBytes.length;
126: while (handshakeLength-- > 0) {
127: int b = fDataIn.read();
128: fDataOut.write(b);
129: }
130: fDataOut.flush();
131:
132: // Print all packages.
133: while (true) {
134: JdwpPacket p = JdwpPacket.read(fDataIn);
135: // we need to store conversation only for command send by the debugger,
136: // as there is no answer from the debugger to VM commands.
137: if (!(fVMtoDebugger && (p.getFlags() & JdwpPacket.FLAG_REPLY_PACKET) == 0)) {
138: store(p);
139: }
140: out.print(p, fVMtoDebugger);
141: out.flush();
142: p.write(fDataOut);
143: fDataOut.flush();
144: }
145: } catch (EOFException e) {
146: } catch (SocketException e) {
147: } catch (IOException e) {
148: out
149: .println(MessageFormat
150: .format(
151: "Caught exception: {0}", new String[] { e.toString() })); //$NON-NLS-1$
152: e.printStackTrace(out);
153: } finally {
154: try {
155: fDataIn.close();
156: fDataOut.close();
157: } catch (IOException e) {
158: }
159: out.flush();
160: }
161: }
162:
163: public static JdwpCommandPacket getCommand(int id) {
164: JdwpConversation conversation = (JdwpConversation) fPackets
165: .get(new Integer(id));
166: if (conversation != null)
167: return conversation.getCommand();
168: return null;
169: }
170:
171: protected static void store(JdwpPacket packet) {
172: int id = packet.getId();
173: JdwpConversation conversation = (JdwpConversation) fPackets
174: .get(new Integer(id));
175: if (conversation == null) {
176: conversation = new JdwpConversation(id);
177: fPackets.put(new Integer(id), conversation);
178: }
179:
180: if ((packet.getFlags() & JdwpPacket.FLAG_REPLY_PACKET) != 0) {
181: conversation.setReply((JdwpReplyPacket) packet);
182: } else {
183: conversation.setCommand((JdwpCommandPacket) packet);
184: }
185: }
186:
187: public static int getCommand(JdwpPacket packet)
188: throws UnableToParseDataException {
189: JdwpCommandPacket command = null;
190: if (packet instanceof JdwpCommandPacket) {
191: command = (JdwpCommandPacket) packet;
192: } else {
193: command = getCommand(packet.getId());
194: if (command == null) {
195: throw new UnableToParseDataException(
196: "This packet is marked as reply, but there is no command with the same id.", null); //$NON-NLS-1$
197: }
198: }
199: return command.getCommand();
200: }
201:
202: public static boolean hasSizes() {
203: return fHasSizes;
204: }
205:
206: public static void setHasSizes(boolean value) {
207: fHasSizes = value;
208: }
209:
210: public static void setFieldIDSize(int fieldIDSize) {
211: fFieldIDSize = fieldIDSize;
212: }
213:
214: public static int getFieldIDSize() {
215: return fFieldIDSize;
216: }
217:
218: public static void setMethodIDSize(int methodIDSize) {
219: fMethodIDSize = methodIDSize;
220: }
221:
222: public static int getMethodIDSize() {
223: return fMethodIDSize;
224: }
225:
226: public static void setObjectIDSize(int objectIDSize) {
227: fObjectIDSize = objectIDSize;
228: }
229:
230: public static int getObjectIDSize() {
231: return fObjectIDSize;
232: }
233:
234: public static void setReferenceTypeIDSize(int referenceTypeIDSize) {
235: fReferenceTypeIDSize = referenceTypeIDSize;
236: }
237:
238: public static int getReferenceTypeIDSize() {
239: return fReferenceTypeIDSize;
240: }
241:
242: public static void setFrameIDSize(int frameIDSize) {
243: fFrameIDSize = frameIDSize;
244: }
245:
246: public static int getFrameIDSize() {
247: return fFrameIDSize;
248: }
249: }
|