/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdi.internal.spy; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.HashMap; import java.util.Map; import com.ibm.icu.text.MessageFormat; /** * This class can be used to spy all JDWP packets. It should be configured 'in * between' the debugger application and the VM (or J9 debug proxy). Its * parameters are: 1) The port number to which the debugger application * connects; 2) The name of the host on which the VM or proxy waits for a JDWP * connection; 3) The port number on which the VM or proxy waits for a JDWP * connection; 4) The file where the trace is written to. * * Note that if this program is used for tracing JDWP activity of Leapfrog, the * 'debug remote program' option must be used, and the J9 proxy must first be * started up by hand on the port to which Leapfrog will connect. The J9 proxy * that is started up by Leapfrog is not used and will return immediately. */ public class TcpipSpy extends Thread { private static final byte[] handshakeBytes = "JDWP-Handshake".getBytes(); //$NON-NLS-1$ private boolean fVMtoDebugger; private DataInputStream fDataIn; private DataOutputStream fDataOut; private static VerbosePacketStream out = new VerbosePacketStream(System.out); private static Map<Integer, JdwpConversation> fPackets = new HashMap<>(); private static int fFieldIDSize; private static int fMethodIDSize; private static int fObjectIDSize; private static int fReferenceTypeIDSize; private static int fFrameIDSize; private static boolean fHasSizes; public TcpipSpy(boolean VMtoDebugger, InputStream in, OutputStream out) { fVMtoDebugger = VMtoDebugger; fDataIn = new DataInputStream(new BufferedInputStream(in)); fDataOut = new DataOutputStream(new BufferedOutputStream(out)); fHasSizes = false; } public static void main(String[] args) { int inPort = 0; String serverHost = null; int outPort = 0; String outputFile = null; try { inPort = Integer.parseInt(args[0]); serverHost = args[1]; outPort = Integer.parseInt(args[2]); if (args.length > 3) { outputFile = args[3]; } } catch (Exception e) { out.println("usage: TcpipSpy <client port> <server host> <server port> [<output file>]"); //$NON-NLS-1$ System.exit(-1); } if (outputFile != null) { File file = new File(outputFile); out.println(MessageFormat .format("Writing output to {0}", new Object[] { file.getAbsolutePath() })); //$NON-NLS-1$ try { out = new VerbosePacketStream(new BufferedOutputStream( new FileOutputStream(file))); } catch (FileNotFoundException e) { out.println(MessageFormat .format("Could not open {0}. Using stdout instead", new Object[] { file.getAbsolutePath() })); //$NON-NLS-1$ } } out.println(); try (ServerSocket serverSock = new ServerSocket(inPort); Socket inSock = serverSock.accept(); Socket outSock = new Socket(InetAddress.getByName(serverHost), outPort);){ new TcpipSpy(false, inSock.getInputStream(), outSock.getOutputStream()).start(); new TcpipSpy(true, outSock.getInputStream(), inSock.getOutputStream()).start(); } catch (Exception e) { out.println(e); } } @Override public void run() { try { // Skip handshake. int handshakeLength; handshakeLength = handshakeBytes.length; while (handshakeLength-- > 0) { int b = fDataIn.read(); fDataOut.write(b); } fDataOut.flush(); // Print all packages. while (true) { JdwpPacket p = JdwpPacket.read(fDataIn); // we need to store conversation only for command send by the // debugger, // as there is no answer from the debugger to VM commands. if (!(fVMtoDebugger && (p.getFlags() & JdwpPacket.FLAG_REPLY_PACKET) == 0)) { store(p); } out.print(p, fVMtoDebugger); out.flush(); p.write(fDataOut); fDataOut.flush(); } } catch (EOFException e) { } catch (SocketException e) { } catch (IOException e) { out.println(MessageFormat.format( "Caught exception: {0}", new Object[] { e.toString() })); //$NON-NLS-1$ e.printStackTrace(out); } finally { try { fDataIn.close(); fDataOut.close(); } catch (IOException e) { } out.flush(); } } public static JdwpCommandPacket getCommand(int id) { JdwpConversation conversation = fPackets .get(new Integer(id)); if (conversation != null) return conversation.getCommand(); return null; } protected static void store(JdwpPacket packet) { int id = packet.getId(); JdwpConversation conversation = fPackets .get(new Integer(id)); if (conversation == null) { conversation = new JdwpConversation(id); fPackets.put(new Integer(id), conversation); } if ((packet.getFlags() & JdwpPacket.FLAG_REPLY_PACKET) != 0) { conversation.setReply((JdwpReplyPacket) packet); } else { conversation.setCommand((JdwpCommandPacket) packet); } } public static int getCommand(JdwpPacket packet) throws UnableToParseDataException { JdwpCommandPacket command = null; if (packet instanceof JdwpCommandPacket) { command = (JdwpCommandPacket) packet; } else { command = getCommand(packet.getId()); if (command == null) { throw new UnableToParseDataException( "This packet is marked as reply, but there is no command with the same id.", null); //$NON-NLS-1$ } } return command.getCommand(); } public static boolean hasSizes() { return fHasSizes; } public static void setHasSizes(boolean value) { fHasSizes = value; } public static void setFieldIDSize(int fieldIDSize) { fFieldIDSize = fieldIDSize; } public static int getFieldIDSize() { return fFieldIDSize; } public static void setMethodIDSize(int methodIDSize) { fMethodIDSize = methodIDSize; } public static int getMethodIDSize() { return fMethodIDSize; } public static void setObjectIDSize(int objectIDSize) { fObjectIDSize = objectIDSize; } public static int getObjectIDSize() { return fObjectIDSize; } public static void setReferenceTypeIDSize(int referenceTypeIDSize) { fReferenceTypeIDSize = referenceTypeIDSize; } public static int getReferenceTypeIDSize() { return fReferenceTypeIDSize; } public static void setFrameIDSize(int frameIDSize) { fFrameIDSize = frameIDSize; } public static int getFrameIDSize() { return fFrameIDSize; } }