blob: a704468d82da8b5a0d901e9d195167c213cb7ea6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
/**
* 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(Integer.valueOf(id));
if (conversation != null) {
return conversation.getCommand();
}
return null;
}
protected static void store(JdwpPacket packet) {
int id = packet.getId();
JdwpConversation conversation = fPackets
.get(Integer.valueOf(id));
if (conversation == null) {
conversation = new JdwpConversation(id);
fPackets.put(Integer.valueOf(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;
}
}