├── .gitignore ├── README.md ├── commands ├── pom.xml └── src │ ├── main │ ├── java │ │ └── kaap │ │ │ └── veiko │ │ │ └── debuggerforker │ │ │ └── commands │ │ │ ├── Command.java │ │ │ ├── CommandBase.java │ │ │ ├── CommandStream.java │ │ │ ├── CommandVisitor.java │ │ │ ├── MutableCommandPacket.java │ │ │ ├── MutableReplyPacket.java │ │ │ ├── UnknownCommand.java │ │ │ ├── commandsets │ │ │ ├── CommandIdentifier.java │ │ │ ├── event │ │ │ │ ├── CompositeEventCommand.java │ │ │ │ └── events │ │ │ │ │ ├── BreakPointEvent.java │ │ │ │ │ ├── ClassPrepareEvent.java │ │ │ │ │ ├── ClassUnloadEvent.java │ │ │ │ │ ├── ExceptionEvent.java │ │ │ │ │ ├── FieldAccessEvent.java │ │ │ │ │ ├── FieldModificationEvent.java │ │ │ │ │ ├── MethodEntryEvent.java │ │ │ │ │ ├── MethodExitEvent.java │ │ │ │ │ ├── MethodExitWithReturnValueEvent.java │ │ │ │ │ ├── MonitorContendedEnterEvent.java │ │ │ │ │ ├── MonitorContendedEnteredEvent.java │ │ │ │ │ ├── MonitorWaitEvent.java │ │ │ │ │ ├── MonitorWaitedEvent.java │ │ │ │ │ ├── SingleStepEvent.java │ │ │ │ │ ├── ThreadDeathEvent.java │ │ │ │ │ ├── ThreadStartEvent.java │ │ │ │ │ ├── VirtualMachineEvent.java │ │ │ │ │ ├── VmDeathEvent.java │ │ │ │ │ └── VmStartEvent.java │ │ │ ├── eventrequest │ │ │ │ ├── ClearAllBreakpointsCommand.java │ │ │ │ ├── ClearEventRequestCommand.java │ │ │ │ ├── ClearEventRequestReply.java │ │ │ │ ├── SetEventRequestCommand.java │ │ │ │ ├── SetEventRequestReply.java │ │ │ │ └── filters │ │ │ │ │ └── EventRequestFilter.java │ │ │ └── virtualmachine │ │ │ │ ├── DisposeCommand.java │ │ │ │ ├── DisposeReply.java │ │ │ │ ├── HoldEventsCommand.java │ │ │ │ ├── IdSizesReply.java │ │ │ │ ├── ReleaseEventsCommand.java │ │ │ │ ├── ResumeCommand.java │ │ │ │ └── ResumeReply.java │ │ │ ├── parser │ │ │ ├── CommandDataReader.java │ │ │ ├── CommandParser.java │ │ │ └── CommandType.java │ │ │ └── util │ │ │ └── CommandDataUtil.java │ └── kotlin │ │ └── kaap │ │ └── veiko │ │ └── debuggerforker │ │ └── commands │ │ └── commandsets │ │ └── eventrequest │ │ └── filters │ │ ├── ClassExcludeEventRequestFilter.kt │ │ ├── ClassMatchEventRequestFilter.kt │ │ ├── ClassOnlyEventRequestFilter.kt │ │ ├── ConditionalEventRequestFilter.kt │ │ ├── CountEventRequestFilter.kt │ │ ├── ExceptionOnlyEventRequestFilter.kt │ │ ├── FieldOnlyEventRequestFilter.kt │ │ ├── InstanceOnlyEventRequestFilter.kt │ │ ├── LocationOnlyEventRequestFilter.kt │ │ ├── SourceNameMatchEventRequestFilter.kt │ │ ├── StepEventRequestFilter.kt │ │ └── ThreadOnlyEventRequestFilter.kt │ └── test │ └── java │ └── kaap │ └── veiko │ └── debuggerforker │ └── commands │ └── commandsets │ ├── CommandTestBase.java │ ├── TestBase.java │ ├── event │ └── events │ │ ├── BreakPointEventTest.java │ │ ├── ClassPrepareEventTest.java │ │ ├── ClassUnloadEventTest.java │ │ ├── EventTestBase.java │ │ ├── ExceptionEventTest.java │ │ ├── FieldAccessEventTest.java │ │ ├── FieldModificationEventTest.java │ │ ├── MethodEntryEventTest.java │ │ ├── MethodExitEventTest.java │ │ ├── MethodExitWithReturnValueEventTest.java │ │ ├── MonitorContendedEnterEventTest.java │ │ ├── MonitorContendedEnteredEventTest.java │ │ ├── MonitorWaitEventTest.java │ │ ├── MonitorWaitedEventTest.java │ │ ├── SingleStepEventTest.java │ │ ├── ThreadDeathEventTest.java │ │ ├── ThreadStartEventTest.java │ │ ├── VmDeathEventTest.java │ │ └── VmStartEventTest.java │ └── virtualmachine │ ├── DisposeCommandTest.java │ ├── DisposeReplyTest.java │ ├── HoldEventsCommandTest.java │ ├── IdSizesReplyTest.java │ └── ReleaseEventsCommandTest.java ├── connections ├── pom.xml └── src │ └── main │ └── java │ └── kaap │ └── veiko │ └── debuggerforker │ └── connections │ ├── ConnectorBase.java │ ├── DebuggerConnector.java │ └── VMConnector.java ├── integration-tests ├── pom.xml └── src │ ├── main │ ├── java │ │ └── ee │ │ │ └── veikokaap │ │ │ └── debugproxy │ │ │ └── testframework │ │ │ ├── DebuggerProcess.java │ │ │ ├── EventRequestIdentifier.java │ │ │ ├── JvmProcess.java │ │ │ ├── SuspendManager.java │ │ │ └── utils │ │ │ ├── BreakpointLocation.java │ │ │ ├── BreakpointUtil.java │ │ │ └── MarkerFinder.java │ └── kotlin │ │ └── ee │ │ └── veikokaap │ │ └── debugproxy │ │ └── testframework │ │ ├── AsyncTester.kt │ │ └── SuspendManagerAsyncTester.kt │ └── test │ ├── java │ └── ee │ │ └── veikokaap │ │ └── debugproxy │ │ └── tests │ │ ├── LoopBreakpointClass.java │ │ ├── SimpleBreakpointClass.java │ │ └── StepBreakpointClass.java │ └── kotlin │ └── ee │ └── veikokaap │ └── debugproxy │ └── tests │ ├── doubledebugger │ ├── SimpleDoubleDebuggerTests.kt │ └── StepDoubleDebuggerTests.kt │ ├── singledebugger │ ├── LoopSingleDebuggerTests.kt │ ├── SimpleSingleDebuggerTests.kt │ └── StepSingleDebuggerTests.kt │ └── testFunctions.kt ├── packet ├── pom.xml └── src │ └── main │ └── java │ └── kaap │ └── veiko │ └── debuggerforker │ └── packet │ ├── CommandPacket.java │ ├── DebuggerPacketStream.java │ ├── Packet.java │ ├── PacketSource.java │ ├── PacketStream.java │ ├── PacketVisitor.java │ ├── ReplyPacket.java │ ├── VirtualMachinePacketStream.java │ ├── internal │ ├── ByteBufferDataReader.java │ ├── ByteBufferDataWriter.java │ ├── DebuggerPacketTransformer.java │ ├── PacketBase.java │ ├── PacketParser.java │ ├── PacketReader.java │ ├── PacketStreamBase.java │ ├── PacketTransformer.java │ ├── PacketWriter.java │ └── VmPacketTransformer.java │ └── utils │ └── ByteBufferUtil.java ├── pom.xml ├── server-logic ├── pom.xml └── src │ └── main │ ├── java │ └── kaap │ │ └── veiko │ │ └── debuggerforker │ │ ├── DebugProxyServer.java │ │ ├── DebuggerProxy.java │ │ ├── Main.java │ │ ├── ProxyCommandStream.java │ │ ├── handlers │ │ ├── CommandHandler.java │ │ ├── RequestHandler.java │ │ └── RequestIdentifier.java │ │ └── managers │ │ ├── ChannelInputOutputManager.java │ │ └── CommandStreamInputOutputManager.java │ └── resources │ └── logback.xml └── types ├── pom.xml └── src └── main └── java └── kaap └── veiko └── debuggerforker └── types ├── DataReader.java ├── DataType.java ├── DataWriter.java ├── VMInformation.java └── jdwp ├── ArrayId.java ├── ArrayTypeId.java ├── ClassId.java ├── ClassLoaderId.java ├── ClassObjectId.java ├── DataTypeBase.java ├── EventKind.java ├── FieldId.java ├── FrameId.java ├── IdSizes.java ├── InterfaceId.java ├── Location.java ├── MethodId.java ├── ObjectId.java ├── ReferenceTypeId.java ├── StringId.java ├── TaggedObjectId.java ├── ThreadGroupId.java ├── ThreadId.java ├── Type.java ├── UntaggedValue.java └── Value.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /target 3 | /out 4 | *.iml 5 | *.iws 6 | *.ipr 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The code part of my bachelour thesis "Java Virtual Machine multi-debugger proxy server". 2 | 3 | This is the source code of my thesis. 4 | The TeX source code of my thesis is available at https://github.com/veikokaap/bachelor-thesis 5 | The final result is available at https://comserv.cs.ut.ee/ati_thesis/datasheet.php?id=61837&year=2018&language=en 6 | 7 | ## Status 8 | 9 | The main part of the proxy server works. 10 | It is possible to attach the proxy server to a single JVM and then to attach multiple debbugers to the proxy server. 11 | Adding/removing breakpoints mostly works, but this is definitely not a ready product to be actually used for debugging. 12 | As part of the thesis, the most important part was to prove that it is actually even possible to create such a functionality, 13 | but I currently simply don't have the time to finish it. 14 | 15 | There is also a bit of code here that I would like to refactor and that I'm not really proud of that I hope to refactor in the future. 16 | 17 | ## Building 18 | 19 | Run `mvn clean package -DskipTests` to just build the server without running any tests. 20 | -------------------------------------------------------------------------------- /commands/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jvm-debugger-forker 7 | kaap.veiko 8 | 0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | commands 13 | 14 | 15 | 16 | kaap.veiko 17 | types 18 | ${parent.version} 19 | 20 | 21 | kaap.veiko 22 | packet 23 | ${parent.version} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/Command.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import kaap.veiko.debuggerforker.packet.Packet; 4 | import kaap.veiko.debuggerforker.packet.PacketSource; 5 | import kaap.veiko.debuggerforker.types.DataWriter; 6 | 7 | public interface Command { 8 | int getCommandSetId(); 9 | 10 | int getCommandId(); 11 | 12 | boolean isReply(); 13 | 14 | void visit(CommandVisitor visitor); 15 | 16 | T getPacket(); 17 | 18 | PacketSource getSource(); 19 | 20 | void writeCommand(DataWriter writer); 21 | } 22 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/CommandBase.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import kaap.veiko.debuggerforker.commands.parser.CommandType; 4 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 5 | import kaap.veiko.debuggerforker.packet.Packet; 6 | import kaap.veiko.debuggerforker.packet.PacketSource; 7 | 8 | public abstract class CommandBase implements Command { 9 | 10 | private final T packet; 11 | private final CommandIdentifier commandIdentifier; 12 | 13 | protected CommandBase(T packet, CommandIdentifier commandIdentifier) { 14 | this.packet = packet; 15 | this.commandIdentifier = commandIdentifier; 16 | } 17 | 18 | @Override 19 | public T getPacket() { 20 | return packet; 21 | } 22 | 23 | @Override 24 | public PacketSource getSource() { 25 | return packet.getSource(); 26 | } 27 | 28 | @Override 29 | public int getCommandSetId() { 30 | return commandIdentifier.getCommandSetId(); 31 | } 32 | 33 | @Override 34 | public int getCommandId() { 35 | return commandIdentifier.getCommandId(); 36 | } 37 | 38 | @Override 39 | public boolean isReply() { 40 | return commandIdentifier.getType() == CommandType.REPLY; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/CommandStream.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.SelectionKey; 5 | import java.nio.channels.Selector; 6 | import java.nio.channels.SocketChannel; 7 | import java.util.Optional; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import kaap.veiko.debuggerforker.commands.parser.CommandParser; 13 | import kaap.veiko.debuggerforker.packet.PacketSource; 14 | import kaap.veiko.debuggerforker.packet.PacketStream; 15 | import kaap.veiko.debuggerforker.types.VMInformation; 16 | 17 | public class CommandStream implements AutoCloseable { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(CommandStream.class); 20 | 21 | private final PacketStream packetStream; 22 | private final VMInformation vmInformation; 23 | private final CommandParser commandParser; 24 | 25 | public CommandStream(PacketStream packetStream, VMInformation vmInformation) { 26 | this.packetStream = packetStream; 27 | this.vmInformation = vmInformation; 28 | this.commandParser = new CommandParser(vmInformation); 29 | } 30 | 31 | public Command read() throws IOException { 32 | return Optional.ofNullable(packetStream.read()) 33 | .map(commandParser::parse) 34 | .orElse(null); 35 | } 36 | 37 | public void write(Command command) throws IOException { 38 | if (command != null && command.getPacket() != null) { 39 | packetStream.write(command.getPacket()); 40 | } 41 | } 42 | 43 | public SocketChannel getSocketChannel() { 44 | return packetStream.getSocketChannel(); 45 | } 46 | 47 | public PacketSource getSource() { 48 | return packetStream.getSource(); 49 | } 50 | 51 | public void close() { 52 | packetStream.close(); 53 | } 54 | 55 | public boolean isClosed() { 56 | return packetStream.isClosed(); 57 | } 58 | 59 | @Override 60 | public boolean equals(Object o) { 61 | if (this == o) { 62 | return true; 63 | } 64 | if (o == null || getClass() != o.getClass()) { 65 | return false; 66 | } 67 | 68 | CommandStream that = (CommandStream) o; 69 | 70 | return packetStream.equals(that.packetStream); 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return packetStream.hashCode(); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return "CommandStream{" + 81 | "packetStream=" + packetStream + 82 | '}'; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/CommandVisitor.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import kaap.veiko.debuggerforker.commands.commandsets.event.CompositeEventCommand; 4 | import kaap.veiko.debuggerforker.commands.commandsets.eventrequest.ClearAllBreakpointsCommand; 5 | import kaap.veiko.debuggerforker.commands.commandsets.eventrequest.ClearEventRequestCommand; 6 | import kaap.veiko.debuggerforker.commands.commandsets.eventrequest.ClearEventRequestReply; 7 | import kaap.veiko.debuggerforker.commands.commandsets.eventrequest.SetEventRequestCommand; 8 | import kaap.veiko.debuggerforker.commands.commandsets.eventrequest.SetEventRequestReply; 9 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.DisposeCommand; 10 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.DisposeReply; 11 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.HoldEventsCommand; 12 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.IdSizesReply; 13 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.ReleaseEventsCommand; 14 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.ResumeCommand; 15 | import kaap.veiko.debuggerforker.commands.commandsets.virtualmachine.ResumeReply; 16 | 17 | public interface CommandVisitor { 18 | void visit(CompositeEventCommand command); 19 | 20 | void visit(ClearAllBreakpointsCommand command); 21 | 22 | void visit(ClearEventRequestCommand command); 23 | 24 | void visit(SetEventRequestCommand command); 25 | 26 | void visit(SetEventRequestReply command); 27 | 28 | void visit(DisposeCommand command); 29 | 30 | void visit(IdSizesReply command); 31 | 32 | void visit(DisposeReply disposeReply); 33 | 34 | void visit(UnknownCommand packetCommand); 35 | 36 | void visit(HoldEventsCommand holdEventsCommand); 37 | 38 | void visit(ReleaseEventsCommand releaseEventsCommand); 39 | 40 | void visit(ResumeCommand resumeCommand); 41 | 42 | void visit(ClearEventRequestReply clearEventRequestReply); 43 | 44 | void visit(ResumeReply resumeReply); 45 | } 46 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/MutableCommandPacket.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 4 | import kaap.veiko.debuggerforker.packet.CommandPacket; 5 | 6 | public class MutableCommandPacket extends CommandPacket { 7 | 8 | private byte[] data; 9 | 10 | public static MutableCommandPacket create(int packetId, CommandIdentifier commandIdentifier) { 11 | return new MutableCommandPacket(packetId, commandIdentifier.getCommandSetId(), commandIdentifier.getCommandId()); 12 | } 13 | 14 | private MutableCommandPacket(int id, short commandSetId, short commandId) { 15 | super(id, commandSetId, commandId, new byte[0], null); 16 | this.data = super.getData(); 17 | } 18 | 19 | public void setData(byte[] data) { 20 | this.data = data; 21 | } 22 | 23 | @Override 24 | public byte[] getData() { 25 | return data; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/MutableReplyPacket.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 4 | 5 | public class MutableReplyPacket extends ReplyPacket { 6 | 7 | private byte[] data; 8 | 9 | public static MutableReplyPacket create(int packetId) { 10 | return new MutableReplyPacket(packetId, (short) 0); 11 | } 12 | 13 | private MutableReplyPacket(int id, short errorCode) { 14 | super(id, errorCode, new byte[0], null); 15 | this.data = super.getData(); 16 | } 17 | 18 | public void setData(byte[] data) { 19 | this.data = data; 20 | } 21 | 22 | @Override 23 | public byte[] getData() { 24 | return data; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/UnknownCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands; 2 | 3 | import kaap.veiko.debuggerforker.packet.Packet; 4 | import kaap.veiko.debuggerforker.packet.PacketSource; 5 | import kaap.veiko.debuggerforker.types.DataWriter; 6 | 7 | public class UnknownCommand implements Command { 8 | 9 | private final Packet packet; 10 | private final int commandId; 11 | private final int commandSetId; 12 | private final boolean reply; 13 | 14 | public UnknownCommand(Packet packet, int commandSetId, int commandId, boolean reply) { 15 | this.packet = packet; 16 | this.commandSetId = commandSetId; 17 | this.commandId = commandId; 18 | this.reply = reply; 19 | } 20 | 21 | @Override 22 | public int getCommandSetId() { 23 | return commandSetId; 24 | } 25 | 26 | @Override 27 | public int getCommandId() { 28 | return commandId; 29 | } 30 | 31 | @Override 32 | public boolean isReply() { 33 | return reply; 34 | } 35 | 36 | @Override 37 | public void visit(CommandVisitor visitor) { 38 | visitor.visit(this); 39 | } 40 | 41 | @Override 42 | public Packet getPacket() { 43 | return packet; 44 | } 45 | 46 | @Override 47 | public PacketSource getSource() { 48 | return packet.getSource(); 49 | } 50 | 51 | @Override 52 | public void writeCommand(DataWriter writer) { 53 | for (byte b : packet.getData()) { 54 | writer.writeByte(b); 55 | } 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "UnknownCommand{" + 61 | "packet=" + packet + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/CommandIdentifier.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets; 2 | 3 | import static kaap.veiko.debuggerforker.commands.parser.CommandType.COMMAND; 4 | import static kaap.veiko.debuggerforker.commands.parser.CommandType.REPLY; 5 | 6 | import java.io.IOException; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | import kaap.veiko.debuggerforker.commands.parser.CommandType; 12 | 13 | public enum CommandIdentifier { 14 | 15 | DISPOSE_COMMAND(1, 6, COMMAND), 16 | DISPOSE_REPLY(1, 6, REPLY), 17 | ID_SIZES_REPLY(1, 7, REPLY), 18 | RESUME_COMMAND(1, 9, COMMAND), 19 | RESUME_REPLY(1, 9, REPLY), 20 | HOLD_EVENTS_COMMAND(1, 15, COMMAND), 21 | RELEASE_EVENTS_COMMAND(1, 16, COMMAND), 22 | 23 | SET_EVENT_REQUEST_COMMAND(15, 1, COMMAND), 24 | SET_EVENT_REQUEST_REPLY(15, 1, REPLY), 25 | CLEAR_EVENT_REQUEST_COMMAND(15, 2, COMMAND), 26 | CLEAR_EVENT_REQUEST_REPLY(15, 2, REPLY), 27 | CLEAR_ALL_BREAKPOINTS_COMMAND(15, 3, COMMAND), 28 | 29 | COMPOSITE_EVENT_COMMAND(64, 100, COMMAND); 30 | 31 | private final short commandSetId; 32 | private final short commandId; 33 | private final CommandType type; 34 | 35 | CommandIdentifier(int commandSetId, int commandId, CommandType type) { 36 | this.commandSetId = (short) commandSetId; 37 | this.commandId = (short) commandId; 38 | this.type = type; 39 | } 40 | 41 | public static CommandIdentifier of(short commandSet, short command, boolean reply) throws IOException { 42 | List identifiers = Arrays.stream(values()) 43 | .filter(ci -> ci.getCommandSetId() == commandSet) 44 | .filter(ci -> ci.getCommandId() == command) 45 | .filter(ci -> reply ? ci.getType().equals(REPLY) : ci.getType().equals(COMMAND)) 46 | .collect(Collectors.toList()); 47 | 48 | if (identifiers.isEmpty()) { 49 | throw new IOException("Couldn't find the identifier for commandSet=" + commandSet + ", command=" + command + ", reply=" + reply + "."); 50 | } 51 | 52 | return identifiers.get(0); 53 | } 54 | 55 | public short getCommandSetId() { 56 | return commandSetId; 57 | } 58 | 59 | public short getCommandId() { 60 | return commandId; 61 | } 62 | 63 | public CommandType getType() { 64 | return type; 65 | } 66 | 67 | public short getFlags() { 68 | if (type == REPLY) { 69 | return -128; 70 | } 71 | else { 72 | return 0; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/CompositeEventCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event; 2 | 3 | import java.util.List; 4 | 5 | import kaap.veiko.debuggerforker.commands.CommandBase; 6 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 7 | import kaap.veiko.debuggerforker.commands.MutableCommandPacket; 8 | import kaap.veiko.debuggerforker.commands.commandsets.event.events.VirtualMachineEvent; 9 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 10 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 11 | import kaap.veiko.debuggerforker.commands.util.CommandDataUtil; 12 | import kaap.veiko.debuggerforker.packet.CommandPacket; 13 | import kaap.veiko.debuggerforker.types.DataWriter; 14 | import kaap.veiko.debuggerforker.types.VMInformation; 15 | 16 | public class CompositeEventCommand extends CommandBase { 17 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.COMPOSITE_EVENT_COMMAND; 18 | 19 | private final byte suspendPolicy; 20 | private final List events; 21 | 22 | public static CompositeEventCommand create(int packetId, byte suspendPolicy, List events, VMInformation vmInformation) { 23 | MutableCommandPacket packet = MutableCommandPacket.create(packetId, COMMAND_IDENTIFIER); 24 | CompositeEventCommand command = new CompositeEventCommand(packet, suspendPolicy, events); 25 | packet.setData(CommandDataUtil.getCommandDataBytes(command, vmInformation)); 26 | 27 | return command; 28 | } 29 | 30 | public static CompositeEventCommand read(CommandDataReader reader) { 31 | byte suspendPolicy = reader.readByte(); 32 | List events = VirtualMachineEvent.readList(reader); 33 | 34 | return new CompositeEventCommand((CommandPacket) reader.getPacket(), suspendPolicy, events); 35 | } 36 | 37 | private CompositeEventCommand(CommandPacket packet, byte suspendPolicy, List events) { 38 | super(packet, CommandIdentifier.COMPOSITE_EVENT_COMMAND); 39 | this.suspendPolicy = suspendPolicy; 40 | this.events = events; 41 | } 42 | 43 | @Override 44 | public void writeCommand(DataWriter writer) { 45 | writer.writeByte(suspendPolicy); 46 | VirtualMachineEvent.writeList(writer, events); 47 | } 48 | 49 | public byte getSuspendPolicy() { 50 | return suspendPolicy; 51 | } 52 | 53 | public List getEvents() { 54 | return events; 55 | } 56 | 57 | @Override 58 | public void visit(CommandVisitor visitor) { 59 | visitor.visit(this); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "CompositeEventCommand{" + 65 | "suspendPolicy=" + suspendPolicy + 66 | ", events=" + events + 67 | '}'; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/BreakPointEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | 11 | @AutoValue 12 | public abstract class BreakPointEvent extends VirtualMachineEvent { 13 | public abstract int getRequestId(); 14 | public abstract ThreadId getThread(); 15 | public abstract Location getLocation(); 16 | 17 | public EventKind getEventKind() { 18 | return EventKind.BREAKPOINT; 19 | } 20 | 21 | public static BreakPointEvent create(int requestId, ThreadId thread, Location location) { 22 | return new AutoValue_BreakPointEvent(requestId, thread, location); 23 | } 24 | 25 | public static BreakPointEvent read(DataReader reader) { 26 | return create( 27 | reader.readInt(), 28 | ThreadId.read(reader), 29 | Location.read(reader) 30 | ); 31 | } 32 | 33 | public void write(DataWriter writer) { 34 | writer.writeType(getEventKind()); 35 | writer.writeInt(getRequestId()); 36 | writer.writeType(getThread()); 37 | writer.writeType(getLocation()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ClassPrepareEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | 11 | @AutoValue 12 | public abstract class ClassPrepareEvent extends VirtualMachineEvent { 13 | public abstract int getRequestId(); 14 | public abstract ThreadId getThread(); 15 | public abstract byte getRefTypeTag(); 16 | public abstract ReferenceTypeId getTypeId(); 17 | public abstract String getSignature(); 18 | public abstract int getStatus(); 19 | 20 | public EventKind getEventKind() { 21 | return EventKind.CLASS_PREPARE; 22 | } 23 | 24 | public static ClassPrepareEvent create(int requestId, ThreadId thread, byte refTypeTag, ReferenceTypeId typeId, String signature, int status) { 25 | return new AutoValue_ClassPrepareEvent(requestId, thread, refTypeTag, typeId, signature, status); 26 | } 27 | 28 | public static ClassPrepareEvent read(DataReader reader) { 29 | return create( 30 | reader.readInt(), 31 | ThreadId.read(reader), 32 | reader.readByte(), 33 | ReferenceTypeId.read(reader), 34 | reader.readString(), 35 | reader.readInt() 36 | ); 37 | } 38 | 39 | @Override 40 | public void write(DataWriter writer) { 41 | writer.writeType(getEventKind()); 42 | writer.writeInt(getRequestId()); 43 | writer.writeType(getThread()); 44 | writer.writeByte(getRefTypeTag()); 45 | writer.writeType(getTypeId()); 46 | writer.writeString(getSignature()); 47 | writer.writeInt(getStatus()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ClassUnloadEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | 9 | @AutoValue 10 | public abstract class ClassUnloadEvent extends VirtualMachineEvent { 11 | public abstract int getRequestId(); 12 | public abstract String getSignature(); 13 | 14 | public EventKind getEventKind() { 15 | return EventKind.CLASS_UNLOAD; 16 | } 17 | 18 | public static ClassUnloadEvent create(int requestId, String signature) { 19 | return new AutoValue_ClassUnloadEvent(requestId, signature); 20 | } 21 | 22 | public static ClassUnloadEvent read(DataReader reader) { 23 | return create( 24 | reader.readInt(), 25 | reader.readString() 26 | ); 27 | } 28 | 29 | @Override 30 | public void write(DataWriter writer) { 31 | writer.writeType(getEventKind()); 32 | writer.writeInt(getRequestId()); 33 | writer.writeString(getSignature()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ExceptionEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 10 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 11 | 12 | @AutoValue 13 | public abstract class ExceptionEvent extends VirtualMachineEvent { 14 | public abstract int getRequestId(); 15 | public abstract ThreadId getThread(); 16 | public abstract Location getLocation(); 17 | public abstract TaggedObjectId getException(); 18 | public abstract Location getCatchLocation(); 19 | 20 | public EventKind getEventKind() { 21 | return EventKind.EXCEPTION; 22 | } 23 | 24 | public static ExceptionEvent create(int requestId, ThreadId thread, Location location, TaggedObjectId exception, Location catchLocation) { 25 | return new AutoValue_ExceptionEvent(requestId, thread, location, exception, catchLocation); 26 | } 27 | 28 | public static ExceptionEvent read(DataReader reader) { 29 | return create( 30 | reader.readInt(), 31 | ThreadId.read(reader), 32 | Location.read(reader), 33 | TaggedObjectId.read(reader), 34 | Location.read(reader) 35 | ); 36 | } 37 | 38 | @Override 39 | public void write(DataWriter writer) { 40 | writer.writeType(getEventKind()); 41 | writer.writeInt(getRequestId()); 42 | writer.writeType(getThread()); 43 | writer.writeType(getLocation()); 44 | writer.writeType(getException()); 45 | writer.writeType(getCatchLocation()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/FieldAccessEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.FieldId; 9 | import kaap.veiko.debuggerforker.types.jdwp.Location; 10 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId; 11 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 12 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 13 | 14 | @AutoValue 15 | public abstract class FieldAccessEvent extends VirtualMachineEvent { 16 | public abstract int getRequestId(); 17 | public abstract ThreadId getThread(); 18 | public abstract Location getLocation(); 19 | public abstract byte getRefTypeTag(); 20 | public abstract ReferenceTypeId getTypeId(); 21 | public abstract FieldId getFieldId(); 22 | public abstract TaggedObjectId getObjectId(); 23 | 24 | public EventKind getEventKind() { 25 | return EventKind.FIELD_ACCESS; 26 | } 27 | 28 | public static FieldAccessEvent create(int requestId, ThreadId thread, Location location, byte refTypeTag, ReferenceTypeId typeId, FieldId fieldId, TaggedObjectId objectId) { 29 | return new AutoValue_FieldAccessEvent(requestId, thread, location, refTypeTag, typeId, fieldId, objectId); 30 | } 31 | 32 | public static FieldAccessEvent read(DataReader reader) { 33 | return create( 34 | reader.readInt(), 35 | ThreadId.read(reader), 36 | Location.read(reader), 37 | reader.readByte(), 38 | ReferenceTypeId.read(reader), 39 | FieldId.read(reader), 40 | TaggedObjectId.read(reader) 41 | ); 42 | } 43 | 44 | @Override 45 | public void write(DataWriter writer) { 46 | writer.writeType(getEventKind()); 47 | writer.writeInt(getRequestId()); 48 | writer.writeType(getThread()); 49 | writer.writeType(getLocation()); 50 | writer.writeByte(getRefTypeTag()); 51 | writer.writeType(getTypeId()); 52 | writer.writeType(getFieldId()); 53 | writer.writeType(getObjectId()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/FieldModificationEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.FieldId; 9 | import kaap.veiko.debuggerforker.types.jdwp.Location; 10 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId; 11 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 12 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 13 | import kaap.veiko.debuggerforker.types.jdwp.Value; 14 | 15 | @AutoValue 16 | public abstract class FieldModificationEvent extends VirtualMachineEvent { 17 | public abstract int getRequestId(); 18 | public abstract ThreadId getThread(); 19 | public abstract Location getLocation(); 20 | public abstract byte getRefTypeTag(); 21 | public abstract ReferenceTypeId getTypeId(); 22 | public abstract FieldId getFieldId(); 23 | public abstract TaggedObjectId getObjectId(); 24 | public abstract Value getValueToBe(); 25 | 26 | public EventKind getEventKind() { 27 | return EventKind.FIELD_MODIFICATION; 28 | } 29 | 30 | public static FieldModificationEvent create(int requestId, ThreadId thread, Location location, byte refTypeTag, ReferenceTypeId typeId, FieldId fieldId, TaggedObjectId objectId, Value valueToBe) { 31 | return new AutoValue_FieldModificationEvent(requestId, thread, location, refTypeTag, typeId, fieldId, objectId, valueToBe); 32 | } 33 | 34 | public static FieldModificationEvent read(DataReader reader) { 35 | return create( 36 | reader.readInt(), 37 | ThreadId.read(reader), 38 | Location.read(reader), 39 | reader.readByte(), 40 | ReferenceTypeId.read(reader), 41 | FieldId.read(reader), 42 | TaggedObjectId.read(reader), 43 | Value.read(reader) 44 | ); 45 | } 46 | 47 | @Override 48 | public void write(DataWriter writer) { 49 | writer.writeType(getEventKind()); 50 | writer.writeInt(getRequestId()); 51 | writer.writeType(getThread()); 52 | writer.writeType(getLocation()); 53 | writer.writeByte(getRefTypeTag()); 54 | writer.writeType(getTypeId()); 55 | writer.writeType(getFieldId()); 56 | writer.writeType(getObjectId()); 57 | writer.writeType(getValueToBe()); 58 | } 59 | } -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MethodEntryEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | 11 | @AutoValue 12 | public abstract class MethodEntryEvent extends VirtualMachineEvent { 13 | public abstract int getRequestId(); 14 | public abstract ThreadId getThread(); 15 | public abstract Location getLocation(); 16 | 17 | public EventKind getEventKind() { 18 | return EventKind.METHOD_ENTRY; 19 | } 20 | 21 | public static MethodEntryEvent create(int requestId, ThreadId thread, Location location) { 22 | return new AutoValue_MethodEntryEvent(requestId, thread, location); 23 | } 24 | 25 | public static MethodEntryEvent read(DataReader reader) { 26 | return create( 27 | reader.readInt(), 28 | ThreadId.read(reader), 29 | Location.read(reader) 30 | ); 31 | } 32 | 33 | @Override 34 | public void write(DataWriter writer) { 35 | writer.writeType(getEventKind()); 36 | writer.writeInt(getRequestId()); 37 | writer.writeType(getThread()); 38 | writer.writeType(getLocation()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MethodExitEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | 11 | @AutoValue 12 | public abstract class MethodExitEvent extends VirtualMachineEvent { 13 | public abstract int getRequestId(); 14 | public abstract ThreadId getThread(); 15 | public abstract Location getLocation(); 16 | 17 | public EventKind getEventKind() { 18 | return EventKind.METHOD_EXIT; 19 | } 20 | 21 | public static MethodExitEvent create(int requestId, ThreadId thread, Location location) { 22 | return new AutoValue_MethodExitEvent(requestId, thread, location); 23 | } 24 | 25 | public static MethodExitEvent read(DataReader reader) { 26 | return create( 27 | reader.readInt(), 28 | ThreadId.read(reader), 29 | Location.read(reader) 30 | ); 31 | } 32 | 33 | @Override 34 | public void write(DataWriter writer) { 35 | writer.writeType(getEventKind()); 36 | writer.writeInt(getRequestId()); 37 | writer.writeType(getThread()); 38 | writer.writeType(getLocation()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MethodExitWithReturnValueEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | import kaap.veiko.debuggerforker.types.jdwp.Value; 11 | 12 | @AutoValue 13 | public abstract class MethodExitWithReturnValueEvent extends VirtualMachineEvent { 14 | public abstract int getRequestId(); 15 | public abstract ThreadId getThread(); 16 | public abstract Location getLocation(); 17 | public abstract Value getValue(); 18 | 19 | public EventKind getEventKind() { 20 | return EventKind.METHOD_EXIT_WITH_RETURN_VALUE; 21 | } 22 | 23 | public static MethodExitWithReturnValueEvent create(int requestId, ThreadId thread, Location location, Value value) { 24 | return new AutoValue_MethodExitWithReturnValueEvent(requestId, thread, location, value); 25 | } 26 | 27 | public static MethodExitWithReturnValueEvent read(DataReader reader) { 28 | return create( 29 | reader.readInt(), 30 | ThreadId.read(reader), 31 | Location.read(reader), 32 | Value.read(reader) 33 | ); 34 | } 35 | 36 | @Override 37 | public void write(DataWriter writer) { 38 | writer.writeType(getEventKind()); 39 | writer.writeInt(getRequestId()); 40 | writer.writeType(getThread()); 41 | writer.writeType(getLocation()); 42 | writer.writeType(getValue()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorContendedEnterEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 10 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 11 | 12 | @AutoValue 13 | public abstract class MonitorContendedEnterEvent extends VirtualMachineEvent { 14 | public abstract int getRequestId(); 15 | public abstract ThreadId getThread(); 16 | public abstract TaggedObjectId getObjectId(); 17 | public abstract Location getLocation(); 18 | 19 | public EventKind getEventKind() { 20 | return EventKind.MONITOR_CONTENDED_ENTER; 21 | } 22 | 23 | public static MonitorContendedEnterEvent create(int requestId, ThreadId thread, TaggedObjectId objectId, Location location) { 24 | return new AutoValue_MonitorContendedEnterEvent(requestId, thread, objectId, location); 25 | } 26 | 27 | public static MonitorContendedEnterEvent read(DataReader reader) { 28 | return create( 29 | reader.readInt(), 30 | ThreadId.read(reader), 31 | TaggedObjectId.read(reader), 32 | Location.read(reader) 33 | ); 34 | } 35 | 36 | @Override 37 | public void write(DataWriter writer) { 38 | writer.writeType(getEventKind()); 39 | writer.writeInt(getRequestId()); 40 | writer.writeType(getThread()); 41 | writer.writeType(getObjectId()); 42 | writer.writeType(getLocation()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorContendedEnteredEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 10 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 11 | 12 | @AutoValue 13 | public abstract class MonitorContendedEnteredEvent extends VirtualMachineEvent { 14 | public abstract int getRequestId(); 15 | public abstract ThreadId getThread(); 16 | public abstract TaggedObjectId getObjectId(); 17 | public abstract Location getLocation(); 18 | 19 | public EventKind getEventKind() { 20 | return EventKind.MONITOR_CONTENDED_ENTERED; 21 | } 22 | 23 | public static MonitorContendedEnteredEvent create(int requestId, ThreadId thread, TaggedObjectId objectId, Location location) { 24 | return new AutoValue_MonitorContendedEnteredEvent(requestId, thread, objectId, location); 25 | } 26 | 27 | public static MonitorContendedEnteredEvent read(DataReader reader) { 28 | return create( 29 | reader.readInt(), 30 | ThreadId.read(reader), 31 | TaggedObjectId.read(reader), 32 | Location.read(reader) 33 | ); 34 | } 35 | 36 | @Override 37 | public void write(DataWriter writer) { 38 | writer.writeType(getEventKind()); 39 | writer.writeInt(getRequestId()); 40 | writer.writeType(getThread()); 41 | writer.writeType(getObjectId()); 42 | writer.writeType(getLocation()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorWaitEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 10 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 11 | 12 | @AutoValue 13 | public abstract class MonitorWaitEvent extends VirtualMachineEvent { 14 | public abstract int getRequestId(); 15 | public abstract ThreadId getThread(); 16 | public abstract TaggedObjectId getObjectId(); 17 | public abstract Location getLocation(); 18 | public abstract long getTimeout(); 19 | 20 | public EventKind getEventKind() { 21 | return EventKind.MONITOR_WAIT; 22 | } 23 | 24 | public static MonitorWaitEvent create(int requestId, ThreadId thread, TaggedObjectId objectId, Location location, long timeout) { 25 | return new AutoValue_MonitorWaitEvent(requestId, thread, objectId, location, timeout); 26 | } 27 | 28 | public static MonitorWaitEvent read(DataReader reader) { 29 | return create( 30 | reader.readInt(), 31 | ThreadId.read(reader), 32 | TaggedObjectId.read(reader), 33 | Location.read(reader), 34 | reader.readLong() 35 | ); 36 | } 37 | 38 | @Override 39 | public void write(DataWriter writer) { 40 | writer.writeType(getEventKind()); 41 | writer.writeInt(getRequestId()); 42 | writer.writeType(getThread()); 43 | writer.writeType(getObjectId()); 44 | writer.writeType(getLocation()); 45 | writer.writeLong(getTimeout()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorWaitedEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 10 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 11 | 12 | @AutoValue 13 | public abstract class MonitorWaitedEvent extends VirtualMachineEvent { 14 | public abstract int getRequestId(); 15 | public abstract ThreadId getThread(); 16 | public abstract TaggedObjectId getObjectId(); 17 | public abstract Location getLocation(); 18 | public abstract boolean getTimedOut(); 19 | 20 | public EventKind getEventKind() { 21 | return EventKind.MONITOR_WAITED; 22 | } 23 | 24 | public static MonitorWaitedEvent create(int requestId, ThreadId thread, TaggedObjectId objectId, Location location, boolean timedOut) { 25 | return new AutoValue_MonitorWaitedEvent(requestId, thread, objectId, location, timedOut); 26 | } 27 | 28 | public static MonitorWaitedEvent read(DataReader reader) { 29 | return create( 30 | reader.readInt(), 31 | ThreadId.read(reader), 32 | TaggedObjectId.read(reader), 33 | Location.read(reader), 34 | reader.readBoolean() 35 | ); 36 | } 37 | 38 | @Override 39 | public void write(DataWriter writer) { 40 | writer.writeType(getEventKind()); 41 | writer.writeInt(getRequestId()); 42 | writer.writeType(getThread()); 43 | writer.writeType(getObjectId()); 44 | writer.writeType(getLocation()); 45 | writer.writeBoolean(getTimedOut()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/SingleStepEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.Location; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | 11 | @AutoValue 12 | public abstract class SingleStepEvent extends VirtualMachineEvent { 13 | public abstract int getRequestId(); 14 | public abstract ThreadId getThread(); 15 | public abstract Location getLocation(); 16 | 17 | public EventKind getEventKind() { 18 | return EventKind.SINGLE_STEP; 19 | } 20 | 21 | public static SingleStepEvent create(int requestId, ThreadId thread, Location location) { 22 | return new AutoValue_SingleStepEvent(requestId, thread, location); 23 | } 24 | 25 | public static SingleStepEvent read(DataReader reader) { 26 | return create( 27 | reader.readInt(), 28 | ThreadId.read(reader), 29 | Location.read(reader) 30 | ); 31 | } 32 | 33 | @Override 34 | public void write(DataWriter writer) { 35 | writer.writeType(getEventKind()); 36 | writer.writeInt(getRequestId()); 37 | writer.writeType(getThread()); 38 | writer.writeType(getLocation()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ThreadDeathEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 9 | 10 | @AutoValue 11 | public abstract class ThreadDeathEvent extends VirtualMachineEvent { 12 | public abstract int getRequestId(); 13 | public abstract ThreadId getThread(); 14 | 15 | public EventKind getEventKind() { 16 | return EventKind.THREAD_DEATH; 17 | } 18 | 19 | public static ThreadDeathEvent create(int requestId, ThreadId thread) { 20 | return new AutoValue_ThreadDeathEvent(requestId, thread); 21 | } 22 | 23 | public static ThreadDeathEvent read(DataReader reader) { 24 | return create( 25 | reader.readInt(), 26 | ThreadId.read(reader) 27 | ); 28 | } 29 | 30 | @Override 31 | public void write(DataWriter writer) { 32 | writer.writeType(getEventKind()); 33 | writer.writeInt(getRequestId()); 34 | writer.writeType(getThread()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ThreadStartEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 9 | 10 | @AutoValue 11 | public abstract class ThreadStartEvent extends VirtualMachineEvent { 12 | public abstract int getRequestId(); 13 | public abstract ThreadId getThread(); 14 | 15 | public EventKind getEventKind() { 16 | return EventKind.THREAD_START; 17 | } 18 | 19 | public static ThreadStartEvent create(int requestId, ThreadId thread) { 20 | return new AutoValue_ThreadStartEvent(requestId, thread); 21 | } 22 | 23 | public static ThreadStartEvent read(DataReader reader) { 24 | return create( 25 | reader.readInt(), 26 | ThreadId.read(reader) 27 | ); 28 | } 29 | 30 | @Override 31 | public void write(DataWriter writer) { 32 | writer.writeType(getEventKind()); 33 | writer.writeInt(getRequestId()); 34 | writer.writeType(getThread()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/VmDeathEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | 9 | @AutoValue 10 | public abstract class VmDeathEvent extends VirtualMachineEvent { 11 | public abstract int getRequestId(); 12 | 13 | public EventKind getEventKind() { 14 | return EventKind.VM_DEATH; 15 | } 16 | 17 | public static VmDeathEvent create(int requestId) { 18 | return new AutoValue_VmDeathEvent(requestId); 19 | } 20 | 21 | public static VmDeathEvent read(DataReader reader) { 22 | return create(reader.readInt()); 23 | } 24 | 25 | @Override 26 | public void write(DataWriter writer) { 27 | writer.writeType(getEventKind()); 28 | writer.writeInt(getRequestId()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/VmStartEvent.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import com.google.auto.value.AutoValue; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataWriter; 7 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 8 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 9 | 10 | @AutoValue 11 | public abstract class VmStartEvent extends VirtualMachineEvent { 12 | public abstract int getRequestId(); 13 | public abstract ThreadId getThread(); 14 | 15 | public EventKind getEventKind() { 16 | return EventKind.VM_START; 17 | } 18 | 19 | public static VmStartEvent create(int requestId, ThreadId thread) { 20 | return new AutoValue_VmStartEvent(requestId, thread); 21 | } 22 | 23 | public static VmStartEvent read(DataReader reader) { 24 | return create( 25 | reader.readInt(), 26 | ThreadId.read(reader) 27 | ); 28 | } 29 | 30 | @Override 31 | public void write(DataWriter writer) { 32 | writer.writeType(getEventKind()); 33 | writer.writeInt(getRequestId()); 34 | writer.writeType(getThread()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/ClearAllBreakpointsCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableCommandPacket; 6 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 7 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 8 | import kaap.veiko.debuggerforker.packet.CommandPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class ClearAllBreakpointsCommand extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.CLEAR_ALL_BREAKPOINTS_COMMAND; 13 | 14 | public static ClearAllBreakpointsCommand create(int packetId) { 15 | MutableCommandPacket packet = MutableCommandPacket.create(packetId, COMMAND_IDENTIFIER); 16 | return new ClearAllBreakpointsCommand(packet); 17 | } 18 | 19 | public static ClearAllBreakpointsCommand read(CommandDataReader reader) { 20 | return new ClearAllBreakpointsCommand((CommandPacket) reader.getPacket()); 21 | } 22 | 23 | private ClearAllBreakpointsCommand(CommandPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | @Override 28 | public void writeCommand(DataWriter writer) { 29 | } 30 | 31 | @Override 32 | public void visit(CommandVisitor visitor) { 33 | visitor.visit(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "ClearAllBreakpointsCommand{}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/ClearEventRequestReply.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableReplyPacket; 6 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 7 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 8 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class ClearEventRequestReply extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.CLEAR_EVENT_REQUEST_REPLY; 13 | 14 | public static ClearEventRequestReply create(int packetId) { 15 | MutableReplyPacket packet = MutableReplyPacket.create(packetId); 16 | 17 | return new ClearEventRequestReply(packet); 18 | } 19 | 20 | public static ClearEventRequestReply read(CommandDataReader reader) { 21 | return new ClearEventRequestReply((ReplyPacket) reader.getPacket()); 22 | } 23 | 24 | private ClearEventRequestReply(ReplyPacket packet) { 25 | super(packet, COMMAND_IDENTIFIER); 26 | } 27 | 28 | @Override 29 | public void writeCommand(DataWriter writer) { 30 | } 31 | 32 | @Override 33 | public void visit(CommandVisitor visitor) { 34 | visitor.visit(this); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "ClearEventRequestReply{}"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/SetEventRequestReply.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableReplyPacket; 6 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 7 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 8 | import kaap.veiko.debuggerforker.commands.util.CommandDataUtil; 9 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 10 | import kaap.veiko.debuggerforker.types.DataWriter; 11 | import kaap.veiko.debuggerforker.types.VMInformation; 12 | 13 | public class SetEventRequestReply extends CommandBase { 14 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.SET_EVENT_REQUEST_REPLY; 15 | 16 | private final int requestId; 17 | 18 | public static SetEventRequestReply create(int packetId, int requestId, VMInformation vmInformation) { 19 | MutableReplyPacket packet = MutableReplyPacket.create(packetId); 20 | SetEventRequestReply command = new SetEventRequestReply(packet, requestId); 21 | packet.setData(CommandDataUtil.getCommandDataBytes(command, vmInformation)); 22 | 23 | return command; 24 | } 25 | 26 | public static SetEventRequestReply read(CommandDataReader reader) { 27 | int requestId = reader.readInt(); 28 | return new SetEventRequestReply((ReplyPacket) reader.getPacket(), requestId); 29 | } 30 | 31 | private SetEventRequestReply(ReplyPacket packet, int requestId) { 32 | super(packet, COMMAND_IDENTIFIER); 33 | this.requestId = requestId; 34 | } 35 | 36 | @Override 37 | public void writeCommand(DataWriter writer) { 38 | writer.writeInt(requestId); 39 | } 40 | 41 | public int getRequestId() { 42 | return requestId; 43 | } 44 | 45 | @Override 46 | public void visit(CommandVisitor visitor) { 47 | visitor.visit(this); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) { 53 | return true; 54 | } 55 | if (o == null || getClass() != o.getClass()) { 56 | return false; 57 | } 58 | 59 | SetEventRequestReply that = (SetEventRequestReply) o; 60 | 61 | return requestId == that.requestId; 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | return requestId; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "SetEventRequestReply{" + 72 | "requestId=" + requestId + 73 | '}'; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/EventRequestFilter.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.IntStream; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 12 | import kaap.veiko.debuggerforker.types.DataType; 13 | import kaap.veiko.debuggerforker.types.DataWriter; 14 | 15 | public abstract class EventRequestFilter implements DataType { 16 | 17 | private static Logger log = LoggerFactory.getLogger(EventRequestFilter.class); 18 | 19 | public static List readList(CommandDataReader reader) { 20 | int length = reader.readInt(); 21 | try { 22 | return IntStream.range(0, length) 23 | .mapToObj(i -> readNext(reader)) 24 | .collect(Collectors.toList()); 25 | } catch (RuntimeException e) { 26 | log.error("Failed to read EventRequestFilter list due to exception.", e); 27 | return Collections.emptyList(); 28 | } 29 | } 30 | 31 | private static EventRequestFilter readNext(CommandDataReader reader) throws RuntimeException { 32 | byte identifier = reader.readByte(); 33 | switch (identifier) { 34 | case CountEventRequestFilter.IDENTIFIER: 35 | return CountEventRequestFilter.read(reader); 36 | case ConditionalEventRequestFilter.IDENTIFIER: 37 | return ConditionalEventRequestFilter.read(reader); 38 | case ThreadOnlyEventRequestFilter.IDENTIFIER: 39 | return ThreadOnlyEventRequestFilter.read(reader); 40 | case ClassOnlyEventRequestFilter.IDENTIFIER: 41 | return ClassOnlyEventRequestFilter.read(reader); 42 | case ClassMatchEventRequestFilter.IDENTIFIER: 43 | return ClassMatchEventRequestFilter.read(reader); 44 | case ClassExcludeEventRequestFilter.IDENTIFIER: 45 | return ClassExcludeEventRequestFilter.read(reader); 46 | case LocationOnlyEventRequestFilter.IDENTIFIER: 47 | return LocationOnlyEventRequestFilter.read(reader); 48 | case ExceptionOnlyEventRequestFilter.IDENTIFIER: 49 | return ExceptionOnlyEventRequestFilter.read(reader); 50 | case FieldOnlyEventRequestFilter.IDENTIFIER: 51 | return FieldOnlyEventRequestFilter.read(reader); 52 | case StepEventRequestFilter.IDENTIFIER: 53 | return StepEventRequestFilter.read(reader); 54 | case InstanceOnlyEventRequestFilter.IDENTIFIER: 55 | return InstanceOnlyEventRequestFilter.read(reader); 56 | case SourceNameMatchEventRequestFilter.IDENTIFIER: 57 | return SourceNameMatchEventRequestFilter.read(reader); 58 | default: 59 | throw new RuntimeException("Failed to parse"); 60 | } 61 | } 62 | 63 | public static void writeList(DataWriter writer, List filters) { 64 | writer.writeInt(filters.size()); 65 | 66 | for (EventRequestFilter filter : filters) { 67 | writer.writeType(filter); 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/DisposeCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableCommandPacket; 6 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 7 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 8 | import kaap.veiko.debuggerforker.packet.CommandPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class DisposeCommand extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.DISPOSE_COMMAND; 13 | 14 | public static DisposeCommand create(int packetId) { 15 | MutableCommandPacket packet = MutableCommandPacket.create(packetId, COMMAND_IDENTIFIER); 16 | return new DisposeCommand(packet); 17 | } 18 | 19 | public static DisposeCommand read(CommandDataReader reader) { 20 | return new DisposeCommand((CommandPacket) reader.getPacket()); 21 | } 22 | 23 | private DisposeCommand(CommandPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | @Override 28 | public void writeCommand(DataWriter writer) { 29 | } 30 | 31 | @Override 32 | public void visit(CommandVisitor visitor) { 33 | visitor.visit(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "DisposeCommand{}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/DisposeReply.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableReplyPacket; 6 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 7 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 8 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class DisposeReply extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.DISPOSE_REPLY; 13 | 14 | public static DisposeReply create(int packetId) { 15 | MutableReplyPacket packet = MutableReplyPacket.create(packetId); 16 | return new DisposeReply(packet); 17 | } 18 | 19 | public static DisposeReply read(CommandDataReader reader) { 20 | return new DisposeReply((ReplyPacket) reader.getPacket()); 21 | } 22 | 23 | private DisposeReply(ReplyPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | protected CommandIdentifier getCommandIdentifier() { 28 | return COMMAND_IDENTIFIER; 29 | } 30 | 31 | @Override 32 | public void writeCommand(DataWriter writer) { 33 | } 34 | 35 | @Override 36 | public void visit(CommandVisitor visitor) { 37 | visitor.visit(this); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "DisposeReply{}"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/HoldEventsCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableCommandPacket; 6 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 7 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 8 | import kaap.veiko.debuggerforker.packet.CommandPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class HoldEventsCommand extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.HOLD_EVENTS_COMMAND; 13 | 14 | public static HoldEventsCommand create(int packetId) { 15 | MutableCommandPacket packet = MutableCommandPacket.create(packetId, COMMAND_IDENTIFIER); 16 | return new HoldEventsCommand(packet); 17 | } 18 | 19 | public static HoldEventsCommand read(CommandDataReader reader) { 20 | return new HoldEventsCommand((CommandPacket) reader.getPacket()); 21 | } 22 | 23 | private HoldEventsCommand(CommandPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | @Override 28 | public void writeCommand(DataWriter writer) { 29 | } 30 | 31 | @Override 32 | public void visit(CommandVisitor visitor) { 33 | visitor.visit(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "HoldEventsCommand{}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/IdSizesReply.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableReplyPacket; 6 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 7 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 8 | import kaap.veiko.debuggerforker.commands.util.CommandDataUtil; 9 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 10 | import kaap.veiko.debuggerforker.types.DataWriter; 11 | import kaap.veiko.debuggerforker.types.VMInformation; 12 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 13 | 14 | public class IdSizesReply extends CommandBase { 15 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.ID_SIZES_REPLY; 16 | 17 | private final IdSizes idSizes; 18 | 19 | public static IdSizesReply create(int packetId, IdSizes idSizes, VMInformation vmInformation) { 20 | MutableReplyPacket packet = MutableReplyPacket.create(packetId); 21 | IdSizesReply command = new IdSizesReply(packet, idSizes); 22 | packet.setData(CommandDataUtil.getCommandDataBytes(command, vmInformation)); 23 | 24 | return command; 25 | } 26 | 27 | public static IdSizesReply read(CommandDataReader reader) { 28 | IdSizes idSizes = IdSizes.read(reader); 29 | return new IdSizesReply((ReplyPacket) reader.getPacket(), idSizes); 30 | } 31 | 32 | private IdSizesReply(ReplyPacket packet, IdSizes idSizes) { 33 | super(packet, COMMAND_IDENTIFIER); 34 | this.idSizes = idSizes; 35 | } 36 | 37 | @Override 38 | public void writeCommand(DataWriter writer) { 39 | writer.writeType(idSizes); 40 | } 41 | 42 | @Override 43 | public void visit(CommandVisitor visitor) { 44 | visitor.visit(this); 45 | } 46 | 47 | public IdSizes getIdSizes() { 48 | return idSizes; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "IdSizesReply{" + 54 | "idSizes=" + idSizes + 55 | '}'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/ReleaseEventsCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableCommandPacket; 6 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 7 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 8 | import kaap.veiko.debuggerforker.packet.CommandPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class ReleaseEventsCommand extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.RELEASE_EVENTS_COMMAND; 13 | 14 | public static ReleaseEventsCommand create(int packetId) { 15 | MutableCommandPacket packet = MutableCommandPacket.create(packetId, COMMAND_IDENTIFIER); 16 | return new ReleaseEventsCommand(packet); 17 | } 18 | 19 | public static ReleaseEventsCommand read(CommandDataReader reader) { 20 | return new ReleaseEventsCommand((CommandPacket) reader.getPacket()); 21 | } 22 | 23 | private ReleaseEventsCommand(CommandPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | @Override 28 | public void writeCommand(DataWriter writer) { 29 | } 30 | 31 | @Override 32 | public void visit(CommandVisitor visitor) { 33 | visitor.visit(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "ReleaseEventsCommand{}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/ResumeCommand.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableCommandPacket; 6 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 7 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 8 | import kaap.veiko.debuggerforker.packet.CommandPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class ResumeCommand extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.RESUME_COMMAND; 13 | 14 | public static ResumeCommand create(int packetId) { 15 | MutableCommandPacket packet = MutableCommandPacket.create(packetId, COMMAND_IDENTIFIER); 16 | return new ResumeCommand(packet); 17 | } 18 | 19 | public static ResumeCommand read(CommandDataReader reader) { 20 | return new ResumeCommand((CommandPacket) reader.getPacket()); 21 | } 22 | 23 | private ResumeCommand(CommandPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | @Override 28 | public void writeCommand(DataWriter writer) { 29 | } 30 | 31 | @Override 32 | public void visit(CommandVisitor visitor) { 33 | visitor.visit(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "ResumeCommand{}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/ResumeReply.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import kaap.veiko.debuggerforker.commands.CommandBase; 4 | import kaap.veiko.debuggerforker.commands.CommandVisitor; 5 | import kaap.veiko.debuggerforker.commands.MutableReplyPacket; 6 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 7 | import kaap.veiko.debuggerforker.commands.parser.CommandDataReader; 8 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 9 | import kaap.veiko.debuggerforker.types.DataWriter; 10 | 11 | public class ResumeReply extends CommandBase { 12 | private static final CommandIdentifier COMMAND_IDENTIFIER = CommandIdentifier.RESUME_REPLY; 13 | 14 | public static ResumeReply create(int packetId) { 15 | MutableReplyPacket packet = MutableReplyPacket.create(packetId); 16 | return new ResumeReply(packet); 17 | } 18 | 19 | public static ResumeReply read(CommandDataReader reader) { 20 | return new ResumeReply((ReplyPacket) reader.getPacket()); 21 | } 22 | 23 | private ResumeReply(ReplyPacket packet) { 24 | super(packet, COMMAND_IDENTIFIER); 25 | } 26 | 27 | @Override 28 | public void writeCommand(DataWriter writer) { 29 | } 30 | 31 | @Override 32 | public void visit(CommandVisitor visitor) { 33 | visitor.visit(this); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "ResumeReply{}"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/parser/CommandParser.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.parser; 2 | 3 | import java.io.IOException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import kaap.veiko.debuggerforker.commands.Command; 8 | import kaap.veiko.debuggerforker.commands.UnknownCommand; 9 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 10 | import kaap.veiko.debuggerforker.packet.CommandPacket; 11 | import kaap.veiko.debuggerforker.packet.Packet; 12 | import kaap.veiko.debuggerforker.packet.PacketVisitor; 13 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 14 | import kaap.veiko.debuggerforker.types.VMInformation; 15 | 16 | public class CommandParser { 17 | private final static Logger log = LoggerFactory.getLogger(CommandParser.class); 18 | 19 | private final VMInformation vmInformation; 20 | private final CommandPacketFinder commandPacketFinder = new CommandPacketFinder(); 21 | 22 | public CommandParser(VMInformation vmInformation) { 23 | this.vmInformation = vmInformation; 24 | } 25 | 26 | public Command parse(Packet packet) { 27 | CommandPacket commandPacket = packet.visit(commandPacketFinder); 28 | 29 | CommandDataReader commandDataReader = new CommandDataReader(packet, vmInformation); 30 | try { 31 | CommandIdentifier identifier = CommandIdentifier.of(commandPacket.getCommandSetId(), commandPacket.getCommandId(), packet.isReply()); 32 | return commandDataReader.readCommand(identifier, packet); 33 | } 34 | catch (IOException e) { 35 | log.warn(e.getMessage()); 36 | return new UnknownCommand(packet, commandPacket.getCommandSetId(), commandPacket.getCommandId(), packet.isReply()); 37 | } 38 | } 39 | 40 | private class CommandPacketFinder implements PacketVisitor { 41 | @Override 42 | public CommandPacket visit(ReplyPacket replyPacket) { 43 | return replyPacket.getCommandPacket(); 44 | } 45 | 46 | @Override 47 | public CommandPacket visit(CommandPacket commandPacket) { 48 | return commandPacket; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/parser/CommandType.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.parser; 2 | 3 | public enum CommandType { 4 | REPLY, 5 | COMMAND 6 | } -------------------------------------------------------------------------------- /commands/src/main/java/kaap/veiko/debuggerforker/commands/util/CommandDataUtil.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.util; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import kaap.veiko.debuggerforker.commands.Command; 6 | import kaap.veiko.debuggerforker.packet.internal.ByteBufferDataWriter; 7 | import kaap.veiko.debuggerforker.types.DataWriter; 8 | import kaap.veiko.debuggerforker.types.VMInformation; 9 | 10 | public class CommandDataUtil { 11 | public static byte[] getCommandDataBytes(Command command, VMInformation vmInformation) { 12 | ByteBuffer buffer = ByteBuffer.allocate(1024); 13 | DataWriter writer = new ByteBufferDataWriter(buffer, vmInformation); 14 | command.writeCommand(writer); 15 | 16 | byte[] bytes = new byte[buffer.position()]; 17 | buffer.flip(); 18 | buffer.get(bytes); 19 | 20 | return bytes; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/ClassExcludeEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | 6 | data class ClassExcludeEventRequestFilter(val classPattern: String) : EventRequestFilter() { 7 | override fun write(writer: DataWriter?) = writer!!.run { 8 | writeByte(IDENTIFIER) 9 | writeString(classPattern) 10 | } 11 | 12 | companion object { 13 | const val IDENTIFIER: Byte = 6 14 | @JvmStatic fun read(reader: DataReader) = ClassExcludeEventRequestFilter(reader.readString()) 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/ClassMatchEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | 6 | data class ClassMatchEventRequestFilter(val classPattern: String) : EventRequestFilter() { 7 | override fun write(writer: DataWriter?) = writer!!.run { 8 | writeByte(IDENTIFIER) 9 | writeString(classPattern) 10 | } 11 | 12 | companion object { 13 | const val IDENTIFIER: Byte = 5 14 | @JvmStatic fun read(reader: DataReader) = ClassMatchEventRequestFilter(reader.readString()) 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/ClassOnlyEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId 6 | 7 | data class ClassOnlyEventRequestFilter(val clazz: ReferenceTypeId) : EventRequestFilter() { 8 | override fun write(writer: DataWriter?) = writer!!.run { 9 | writeByte(IDENTIFIER) 10 | writeType(clazz) 11 | } 12 | 13 | companion object { 14 | const val IDENTIFIER: Byte = 4 15 | @JvmStatic fun read(reader: DataReader) = ClassOnlyEventRequestFilter(ReferenceTypeId.read(reader)) 16 | } 17 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/ConditionalEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | 6 | data class ConditionalEventRequestFilter(val exprId: Int) : EventRequestFilter() { 7 | override fun write(writer: DataWriter?) = writer!!.run { 8 | writeByte(IDENTIFIER) 9 | writeInt(exprId) 10 | } 11 | 12 | companion object { 13 | const val IDENTIFIER: Byte = 2 14 | @JvmStatic fun read(reader: DataReader) = ConditionalEventRequestFilter(reader.readInt()) 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/CountEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | 6 | data class CountEventRequestFilter(val count: Int) : EventRequestFilter() { 7 | override fun write(writer: DataWriter?) = writer!!.run { 8 | writeByte(IDENTIFIER) 9 | writeInt(count) 10 | } 11 | 12 | companion object { 13 | const val IDENTIFIER: Byte = 1 14 | @JvmStatic fun read(reader: DataReader) = CountEventRequestFilter(reader.readInt()) 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/ExceptionOnlyEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId 6 | 7 | data class ExceptionOnlyEventRequestFilter( 8 | val exceptionOrNull: ReferenceTypeId, 9 | val caught: Boolean, 10 | val uncaught: Boolean 11 | ) : EventRequestFilter() { 12 | override fun write(writer: DataWriter?) = writer!!.run { 13 | writeByte(IDENTIFIER) 14 | writeType(exceptionOrNull) 15 | writeBoolean(caught) 16 | writeBoolean(uncaught) 17 | } 18 | 19 | companion object { 20 | const val IDENTIFIER: Byte = 8 21 | @JvmStatic fun read(reader: DataReader) = ExceptionOnlyEventRequestFilter( 22 | ReferenceTypeId.read(reader), 23 | reader.readBoolean(), 24 | reader.readBoolean() 25 | ) 26 | } 27 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/FieldOnlyEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.FieldId 6 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId 7 | 8 | data class FieldOnlyEventRequestFilter( 9 | val declaring: ReferenceTypeId, 10 | val fieldId: FieldId 11 | ) : EventRequestFilter() { 12 | override fun write(writer: DataWriter?) = writer!!.run { 13 | writeByte(IDENTIFIER) 14 | writeType(declaring) 15 | writeType(fieldId) 16 | } 17 | 18 | companion object { 19 | const val IDENTIFIER: Byte = 9 20 | @JvmStatic fun read(reader: DataReader) = FieldOnlyEventRequestFilter( 21 | ReferenceTypeId.read(reader), 22 | FieldId.read(reader) 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/InstanceOnlyEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.ObjectId 6 | 7 | data class InstanceOnlyEventRequestFilter(val instance: ObjectId) : EventRequestFilter() { 8 | override fun write(writer: DataWriter?) = writer!!.run { 9 | writeByte(IDENTIFIER) 10 | writeType(instance) 11 | } 12 | 13 | companion object { 14 | const val IDENTIFIER: Byte = 11 15 | @JvmStatic fun read(reader: DataReader) = InstanceOnlyEventRequestFilter(ObjectId.read(reader)) 16 | } 17 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/LocationOnlyEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.Location 6 | 7 | data class LocationOnlyEventRequestFilter(val location: Location) : EventRequestFilter() { 8 | override fun write(writer: DataWriter?) = writer!!.run { 9 | writeByte(IDENTIFIER) 10 | writeType(location) 11 | } 12 | 13 | companion object { 14 | const val IDENTIFIER: Byte = 7 15 | @JvmStatic fun read(reader: DataReader) = LocationOnlyEventRequestFilter(Location.read(reader)) 16 | } 17 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/SourceNameMatchEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | 6 | data class SourceNameMatchEventRequestFilter(val sourceNamePattern: String) : EventRequestFilter() { 7 | override fun write(writer: DataWriter?) = writer!!.run { 8 | writeByte(IDENTIFIER) 9 | writeString(sourceNamePattern) 10 | } 11 | 12 | companion object { 13 | const val IDENTIFIER: Byte = 12 14 | @JvmStatic fun read(reader: DataReader) = SourceNameMatchEventRequestFilter(reader.readString()) 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/StepEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId 6 | 7 | data class StepEventRequestFilter( 8 | val thread: ThreadId, 9 | val size: Int, 10 | val depth: Int 11 | ) : EventRequestFilter() { 12 | override fun write(writer: DataWriter?) = writer!!.run { 13 | writeByte(IDENTIFIER) 14 | writeType(thread) 15 | writeInt(size) 16 | writeInt(depth) 17 | } 18 | 19 | companion object { 20 | const val IDENTIFIER: Byte = 10 21 | @JvmStatic fun read(reader: DataReader) = StepEventRequestFilter( 22 | ThreadId.read(reader), 23 | reader.readInt(), 24 | reader.readInt() 25 | ) 26 | } 27 | } -------------------------------------------------------------------------------- /commands/src/main/kotlin/kaap/veiko/debuggerforker/commands/commandsets/eventrequest/filters/ThreadOnlyEventRequestFilter.kt: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.eventrequest.filters 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader 4 | import kaap.veiko.debuggerforker.types.DataWriter 5 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId 6 | 7 | data class ThreadOnlyEventRequestFilter(val thread: ThreadId) : EventRequestFilter() { 8 | override fun write(writer: DataWriter?) = writer!!.run { 9 | writeByte(IDENTIFIER) 10 | writeType(thread) 11 | } 12 | 13 | companion object { 14 | const val IDENTIFIER: Byte = 3 15 | @JvmStatic fun read(reader: DataReader) = ThreadOnlyEventRequestFilter(ThreadId.read(reader)) 16 | } 17 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/CommandTestBase.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | import kaap.veiko.debuggerforker.commands.Command; 8 | import kaap.veiko.debuggerforker.commands.parser.CommandType; 9 | import kaap.veiko.debuggerforker.packet.CommandPacket; 10 | import kaap.veiko.debuggerforker.packet.Packet; 11 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 12 | 13 | public abstract class CommandTestBase extends TestBase { 14 | 15 | protected int generateRandomPacketId() { 16 | return ThreadLocalRandom.current().nextInt(); 17 | } 18 | 19 | protected void assertCommandIdentifier(CommandIdentifier expectedIdentifier, Command actualCommand) { 20 | boolean expectedIsReply = expectedIdentifier.getType() == CommandType.REPLY; 21 | 22 | assertEquals(expectedIdentifier.getCommandId(), actualCommand.getCommandId()); 23 | assertEquals(expectedIdentifier.getCommandSetId(), actualCommand.getCommandSetId()); 24 | assertEquals(expectedIsReply, actualCommand.isReply()); 25 | } 26 | 27 | protected void assertEqualPackets(Packet expected, Packet actual) { 28 | if (expected instanceof CommandPacket && actual instanceof CommandPacket) { 29 | assertEqualCommandPackets((CommandPacket) expected, (CommandPacket)actual); 30 | } 31 | else if (expected instanceof ReplyPacket && actual instanceof ReplyPacket) { 32 | assertEqualReplyPackets((ReplyPacket) expected, (ReplyPacket)actual); 33 | } 34 | else { 35 | assertTrue("Packets are different types",false); 36 | } 37 | } 38 | 39 | protected void assertEqualReplyPackets(ReplyPacket expected, ReplyPacket actual) { 40 | assertPacketsBase(expected, actual); 41 | assertEquals(expected.getErrorCode(), actual.getErrorCode()); 42 | assertEquals(true, actual.isReply()); 43 | } 44 | 45 | protected void assertEqualCommandPackets(CommandPacket expected, CommandPacket actual) { 46 | assertPacketsBase(expected, actual); 47 | assertEquals(expected.getCommandId(), actual.getCommandId()); 48 | assertEquals(expected.getCommandSetId(), actual.getCommandSetId()); 49 | assertEquals(false, actual.isReply()); 50 | } 51 | 52 | private void assertPacketsBase(Packet expected, Packet actual) { 53 | assertEquals(expected.getId(), actual.getId()); 54 | assertEquals(expected.getLength(), actual.getLength()); 55 | assertEquals(expected.getFlags(), actual.getFlags()); 56 | assertEquals(expected.isReply(), actual.isReply()); 57 | assertArrayEquals(expected.getData(), actual.getData()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/TestBase.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets; 2 | 3 | import org.junit.Before; 4 | 5 | import kaap.veiko.debuggerforker.types.VMInformation; 6 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 7 | 8 | public abstract class TestBase { 9 | private final VMInformation vmInformation = new VMInformation(); 10 | 11 | @Before 12 | public void setup() { 13 | vmInformation.setIdSizes(new IdSizes(6,5,7,6,7)); 14 | } 15 | 16 | protected VMInformation getVmInformation() { 17 | return vmInformation; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/BreakPointEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.ClassId; 6 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 7 | import kaap.veiko.debuggerforker.types.jdwp.Location; 8 | import kaap.veiko.debuggerforker.types.jdwp.MethodId; 9 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 10 | 11 | public class BreakPointEventTest extends EventTestBase { 12 | @Test 13 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 14 | assertWrittenEventEqualsReadEvent(EventKind.BREAKPOINT, BreakPointEvent.create( 15 | randomInt(), 16 | randomThreadId(), 17 | randomLocation() 18 | )); 19 | } 20 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ClassPrepareEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | import kaap.veiko.debuggerforker.types.jdwp.ReferenceTypeId; 7 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 8 | 9 | public class ClassPrepareEventTest extends EventTestBase { 10 | @Test 11 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 12 | assertWrittenEventEqualsReadEvent(EventKind.CLASS_PREPARE, ClassPrepareEvent.create( 13 | randomInt(), 14 | randomThreadId(), 15 | randomByte(), 16 | randomReferenceTypeId(), 17 | randomString(), 18 | randomInt() 19 | )); 20 | } 21 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ClassUnloadEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class ClassUnloadEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.CLASS_UNLOAD, ClassUnloadEvent.create( 11 | randomInt(), 12 | randomString() 13 | )); 14 | } 15 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ExceptionEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.ClassId; 6 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 7 | import kaap.veiko.debuggerforker.types.jdwp.Location; 8 | import kaap.veiko.debuggerforker.types.jdwp.MethodId; 9 | import kaap.veiko.debuggerforker.types.jdwp.ObjectId; 10 | import kaap.veiko.debuggerforker.types.jdwp.TaggedObjectId; 11 | import kaap.veiko.debuggerforker.types.jdwp.ThreadId; 12 | 13 | public class ExceptionEventTest extends EventTestBase { 14 | @Test 15 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 16 | assertWrittenEventEqualsReadEvent(EventKind.EXCEPTION, ExceptionEvent.create( 17 | randomInt(), 18 | randomThreadId(), 19 | randomLocation(), 20 | randomTaggedObjectId(), 21 | randomLocation() 22 | )); 23 | } 24 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/FieldAccessEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class FieldAccessEventTest extends EventTestBase { 8 | 9 | @Test 10 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 11 | assertWrittenEventEqualsReadEvent(EventKind.FIELD_ACCESS, FieldAccessEvent.create( 12 | randomInt(), 13 | randomThreadId(), 14 | randomLocation(), 15 | randomByte(), 16 | randomReferenceTypeId(), 17 | randomFieldId(), 18 | randomTaggedObjectId() 19 | )); 20 | } 21 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/FieldModificationEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class FieldModificationEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.FIELD_MODIFICATION, FieldModificationEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomLocation(), 14 | randomByte(), 15 | randomReferenceTypeId(), 16 | randomFieldId(), 17 | randomTaggedObjectId(), 18 | randomValue() 19 | )); 20 | } 21 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MethodEntryEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MethodEntryEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.METHOD_ENTRY, MethodEntryEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomLocation() 14 | )); 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MethodExitEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MethodExitEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.METHOD_EXIT, MethodExitEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomLocation() 14 | )); 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MethodExitWithReturnValueEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MethodExitWithReturnValueEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.METHOD_EXIT_WITH_RETURN_VALUE, MethodExitWithReturnValueEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomLocation(), 14 | randomValue() 15 | )); 16 | } 17 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorContendedEnterEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MonitorContendedEnterEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.MONITOR_CONTENDED_ENTER, MonitorContendedEnterEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomTaggedObjectId(), 14 | randomLocation() 15 | )); 16 | } 17 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorContendedEnteredEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MonitorContendedEnteredEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.MONITOR_CONTENDED_ENTERED, MonitorContendedEnteredEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomTaggedObjectId(), 14 | randomLocation() 15 | )); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorWaitEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MonitorWaitEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.MONITOR_WAIT, MonitorWaitEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomTaggedObjectId(), 14 | randomLocation(), 15 | randomLong() 16 | )); 17 | } 18 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/MonitorWaitedEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class MonitorWaitedEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.MONITOR_WAITED, MonitorWaitedEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomTaggedObjectId(), 14 | randomLocation(), 15 | randomBoolean() 16 | )); 17 | } 18 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/SingleStepEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class SingleStepEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.SINGLE_STEP, SingleStepEvent.create( 11 | randomInt(), 12 | randomThreadId(), 13 | randomLocation() 14 | )); 15 | } 16 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ThreadDeathEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class ThreadDeathEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.THREAD_DEATH, ThreadDeathEvent.create( 11 | randomInt(), 12 | randomThreadId() 13 | )); 14 | } 15 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/ThreadStartEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class ThreadStartEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.THREAD_START, ThreadStartEvent.create( 11 | randomInt(), 12 | randomThreadId() 13 | )); 14 | } 15 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/VmDeathEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class VmDeathEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.VM_DEATH, VmDeathEvent.create( 11 | randomInt() 12 | )); 13 | } 14 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/event/events/VmStartEventTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.event.events; 2 | 3 | import org.junit.Test; 4 | 5 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 6 | 7 | public class VmStartEventTest extends EventTestBase { 8 | @Test 9 | public void testReadAndWriteEqualsOriginalEvent() throws ReflectiveOperationException { 10 | assertWrittenEventEqualsReadEvent(EventKind.VM_START, VmStartEvent.create( 11 | randomInt(), 12 | randomThreadId() 13 | )); 14 | } 15 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/DisposeCommandTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | import kaap.veiko.debuggerforker.commands.Command; 8 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 9 | import kaap.veiko.debuggerforker.commands.commandsets.CommandTestBase; 10 | import kaap.veiko.debuggerforker.commands.parser.CommandParser; 11 | import kaap.veiko.debuggerforker.packet.Packet; 12 | 13 | public class DisposeCommandTest extends CommandTestBase { 14 | 15 | @Test 16 | public void testCreate() { 17 | int packetId = generateRandomPacketId(); 18 | 19 | DisposeCommand command = DisposeCommand.create(packetId); 20 | 21 | assertEquals(packetId, command.getPacket().getId()); 22 | assertCommandIdentifier(CommandIdentifier.DISPOSE_COMMAND, command); 23 | } 24 | 25 | @Test 26 | public void testParse() { 27 | int packetId = generateRandomPacketId(); 28 | DisposeCommand originalCommand = DisposeCommand.create(packetId); 29 | Packet originalPacket = originalCommand.getPacket(); 30 | 31 | CommandParser parser = new CommandParser(getVmInformation()); 32 | Command parsedCommand = parser.parse(originalPacket); 33 | 34 | assertEqualPackets(originalPacket, parsedCommand.getPacket()); 35 | assertEquals(true, parsedCommand instanceof DisposeCommand); 36 | assertEquals(packetId, parsedCommand.getPacket().getId()); 37 | } 38 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/DisposeReplyTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import static kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier.DISPOSE_REPLY; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import org.junit.Test; 7 | 8 | import kaap.veiko.debuggerforker.commands.Command; 9 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 10 | import kaap.veiko.debuggerforker.commands.commandsets.CommandTestBase; 11 | import kaap.veiko.debuggerforker.commands.parser.CommandParser; 12 | import kaap.veiko.debuggerforker.packet.CommandPacket; 13 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 14 | 15 | public class DisposeReplyTest extends CommandTestBase { 16 | 17 | @Test 18 | public void testCreate() { 19 | int packetId = generateRandomPacketId(); 20 | 21 | DisposeReply reply = DisposeReply.create(packetId); 22 | 23 | assertEquals(packetId, reply.getPacket().getId()); 24 | assertCommandIdentifier(DISPOSE_REPLY, reply); 25 | } 26 | 27 | @Test 28 | public void testParse() { 29 | int packetId = generateRandomPacketId(); 30 | DisposeReply originalReply = DisposeReply.create(packetId); 31 | ReplyPacket originalPacket = originalReply.getPacket(); 32 | // Needed for parsing since replies don't contain info about what type they are 33 | originalPacket.setCommandPacket(new CommandPacket(packetId, DISPOSE_REPLY.getCommandSetId(), DISPOSE_REPLY.getCommandId(), new byte[0], null)); 34 | 35 | CommandParser parser = new CommandParser(getVmInformation()); 36 | Command parsedCommand = parser.parse(originalPacket); 37 | 38 | assertEqualPackets(originalPacket, parsedCommand.getPacket()); 39 | assertEquals(true, parsedCommand instanceof DisposeReply); 40 | assertEquals(packetId, parsedCommand.getPacket().getId()); 41 | } 42 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/HoldEventsCommandTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import kaap.veiko.debuggerforker.commands.Command; 8 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 9 | import kaap.veiko.debuggerforker.commands.commandsets.CommandTestBase; 10 | import kaap.veiko.debuggerforker.commands.parser.CommandParser; 11 | import kaap.veiko.debuggerforker.packet.Packet; 12 | 13 | public class HoldEventsCommandTest extends CommandTestBase { 14 | 15 | @Test 16 | public void testCreate() { 17 | int packetId = generateRandomPacketId(); 18 | 19 | HoldEventsCommand command = HoldEventsCommand.create(packetId); 20 | 21 | assertEquals(packetId, command.getPacket().getId()); 22 | assertCommandIdentifier(CommandIdentifier.HOLD_EVENTS_COMMAND, command); 23 | } 24 | 25 | @Test 26 | public void testParse() { 27 | int packetId = generateRandomPacketId(); 28 | HoldEventsCommand originalCommand = HoldEventsCommand.create(packetId); 29 | Packet originalPacket = originalCommand.getPacket(); 30 | 31 | CommandParser parser = new CommandParser(getVmInformation()); 32 | Command parsedCommand = parser.parse(originalPacket); 33 | 34 | assertEqualPackets(originalPacket, parsedCommand.getPacket()); 35 | assertEquals(true, parsedCommand instanceof HoldEventsCommand); 36 | assertEquals(packetId, parsedCommand.getPacket().getId()); 37 | } 38 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/IdSizesReplyTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import static kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier.ID_SIZES_REPLY; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import org.junit.Test; 7 | 8 | import kaap.veiko.debuggerforker.commands.Command; 9 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 10 | import kaap.veiko.debuggerforker.commands.commandsets.CommandTestBase; 11 | import kaap.veiko.debuggerforker.commands.parser.CommandParser; 12 | import kaap.veiko.debuggerforker.packet.CommandPacket; 13 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 14 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 15 | 16 | public class IdSizesReplyTest extends CommandTestBase { 17 | 18 | @Test 19 | public void testCreate() { 20 | int packetId = generateRandomPacketId(); 21 | IdSizes expectedIdSizes = new IdSizes(1, 2, 3, 4, 5); 22 | 23 | IdSizesReply reply = IdSizesReply.create(packetId, expectedIdSizes, getVmInformation()); 24 | 25 | assertEquals(packetId, reply.getPacket().getId()); 26 | assertCommandIdentifier(ID_SIZES_REPLY, reply); 27 | assertEquals(expectedIdSizes, reply.getIdSizes()); 28 | } 29 | 30 | @Test 31 | public void testParse() { 32 | int packetId = generateRandomPacketId(); 33 | IdSizes expectedIdSizes = new IdSizes(1, 2, 3, 4, 5); 34 | 35 | IdSizesReply originalCommand = IdSizesReply.create(packetId, expectedIdSizes, getVmInformation()); 36 | ReplyPacket originalPacket = originalCommand.getPacket(); 37 | // Needed for parsing since replies don't contain info about what type they are 38 | originalPacket.setCommandPacket(new CommandPacket(packetId, ID_SIZES_REPLY.getCommandSetId(), ID_SIZES_REPLY.getCommandId(), new byte[0], null)); 39 | 40 | CommandParser parser = new CommandParser(getVmInformation()); 41 | Command parsedCommand = parser.parse(originalPacket); 42 | 43 | assertEqualPackets(originalPacket, parsedCommand.getPacket()); 44 | assertEquals(true, parsedCommand instanceof IdSizesReply); 45 | assertEquals(packetId, parsedCommand.getPacket().getId()); 46 | assertEquals(expectedIdSizes, ((IdSizesReply) parsedCommand).getIdSizes()); 47 | } 48 | } -------------------------------------------------------------------------------- /commands/src/test/java/kaap/veiko/debuggerforker/commands/commandsets/virtualmachine/ReleaseEventsCommandTest.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.commands.commandsets.virtualmachine; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import kaap.veiko.debuggerforker.commands.Command; 8 | import kaap.veiko.debuggerforker.commands.commandsets.CommandIdentifier; 9 | import kaap.veiko.debuggerforker.commands.commandsets.CommandTestBase; 10 | import kaap.veiko.debuggerforker.commands.parser.CommandParser; 11 | import kaap.veiko.debuggerforker.packet.Packet; 12 | 13 | public class ReleaseEventsCommandTest extends CommandTestBase { 14 | 15 | @Test 16 | public void testCreate() { 17 | int packetId = generateRandomPacketId(); 18 | 19 | ReleaseEventsCommand command = ReleaseEventsCommand.create(packetId); 20 | 21 | assertEquals(packetId, command.getPacket().getId()); 22 | assertCommandIdentifier(CommandIdentifier.RELEASE_EVENTS_COMMAND, command); 23 | } 24 | 25 | @Test 26 | public void testParse() { 27 | int packetId = generateRandomPacketId(); 28 | ReleaseEventsCommand originalCommand = ReleaseEventsCommand.create(packetId); 29 | Packet originalPacket = originalCommand.getPacket(); 30 | 31 | CommandParser parser = new CommandParser(getVmInformation()); 32 | Command parsedCommand = parser.parse(originalPacket); 33 | 34 | assertEqualPackets(originalPacket, parsedCommand.getPacket()); 35 | assertEquals(true, parsedCommand instanceof ReleaseEventsCommand); 36 | assertEquals(packetId, parsedCommand.getPacket().getId()); 37 | } 38 | } -------------------------------------------------------------------------------- /connections/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jvm-debugger-forker 7 | kaap.veiko 8 | 0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | connections 13 | 14 | 15 | 16 | kaap.veiko 17 | packet 18 | ${parent.version} 19 | 20 | 21 | kaap.veiko 22 | types 23 | ${parent.version} 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /connections/src/main/java/kaap/veiko/debuggerforker/connections/ConnectorBase.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.connections; 2 | 3 | import java.io.IOException; 4 | import java.util.Set; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | import java.util.function.Consumer; 8 | 9 | import kaap.veiko.debuggerforker.packet.PacketStream; 10 | 11 | public abstract class ConnectorBase implements AutoCloseable { 12 | 13 | private final Thread thread; 14 | private final AtomicBoolean open = new AtomicBoolean(true); 15 | private final Set connectionHistory = ConcurrentHashMap.newKeySet(); 16 | private final int maxOpenConnections; 17 | 18 | protected ConnectorBase(Consumer listener, int maxOpenConnections, String threadName) { 19 | this.maxOpenConnections = maxOpenConnections; 20 | thread = new Thread(() -> { 21 | while (isOpen()) { 22 | connect(listener); 23 | } 24 | }, threadName); 25 | } 26 | 27 | private void connect(Consumer listener) { 28 | if (shouldConnect()) { 29 | try { 30 | T connection = getConnectionBlocking(); 31 | if (connection != null) { 32 | listener.accept(connection); 33 | connectionHistory.add(connection); 34 | } 35 | } 36 | catch (Exception e) { 37 | open.set(false); 38 | } 39 | } 40 | else { 41 | try { 42 | Thread.sleep(50); 43 | } 44 | catch (InterruptedException e) { 45 | open.set(false); 46 | Thread.currentThread().interrupt(); 47 | } 48 | } 49 | } 50 | 51 | private boolean shouldConnect() { 52 | removeClosedConnectionsFromHistory(); 53 | return maxOpenConnections > connectionHistory.size(); 54 | } 55 | 56 | private void removeClosedConnectionsFromHistory() { 57 | connectionHistory.stream() 58 | .filter(PacketStream::isClosed) 59 | .forEach(connectionHistory::remove); 60 | } 61 | 62 | private boolean isOpen() { 63 | return open.get() && !Thread.currentThread().isInterrupted(); 64 | } 65 | 66 | protected abstract T getConnectionBlocking() throws IOException; 67 | 68 | public void start() { 69 | thread.start(); 70 | } 71 | 72 | @Override 73 | public void close() { 74 | open.set(false); 75 | 76 | try { 77 | thread.join(100); 78 | } 79 | catch (InterruptedException e) { 80 | Thread.currentThread().interrupt(); 81 | } 82 | finally { 83 | thread.interrupt(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /connections/src/main/java/kaap/veiko/debuggerforker/connections/DebuggerConnector.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.connections; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.ServerSocketChannel; 7 | import java.nio.channels.SocketChannel; 8 | import java.util.Arrays; 9 | import java.util.function.Consumer; 10 | 11 | import kaap.veiko.debuggerforker.packet.DebuggerPacketStream; 12 | import kaap.veiko.debuggerforker.packet.internal.DebuggerPacketTransformer; 13 | import kaap.veiko.debuggerforker.packet.internal.PacketTransformer; 14 | 15 | public class DebuggerConnector extends ConnectorBase { 16 | private final ServerSocketChannel serverChannel; 17 | private final PacketTransformer packetTransformer = new DebuggerPacketTransformer(); 18 | 19 | public static DebuggerConnector create(int port, Consumer listener) throws IOException { 20 | ServerSocketChannel serverChannel = ServerSocketChannel.open(); 21 | serverChannel.bind(new InetSocketAddress("127.0.0.1", port)); 22 | return new DebuggerConnector(serverChannel, listener); 23 | } 24 | 25 | private DebuggerConnector(ServerSocketChannel serverChannel, Consumer listener) { 26 | super(listener, Integer.MAX_VALUE, "DebuggerConnectorThread"); 27 | this.serverChannel = serverChannel; 28 | } 29 | 30 | @Override 31 | protected DebuggerPacketStream getConnectionBlocking() throws IOException { 32 | SocketChannel socketChannel = serverChannel.accept(); 33 | socketChannel.configureBlocking(true); 34 | if (!socketChannel.finishConnect()) { 35 | throw new IOException("Failed to connect channel"); 36 | } 37 | handshake(socketChannel); 38 | return new DebuggerPacketStream(socketChannel, packetTransformer); 39 | } 40 | 41 | private void handshake(SocketChannel socketChannel) throws IOException { 42 | byte[] handshakeBytes = "JDWP-Handshake".getBytes("UTF-8"); 43 | 44 | ByteBuffer inBuffer = ByteBuffer.allocate(handshakeBytes.length); 45 | int bytesRead = 0; 46 | while (bytesRead != -1 && inBuffer.hasRemaining()) { 47 | bytesRead = socketChannel.read(inBuffer); 48 | } 49 | byte[] receivedBytes = inBuffer.array(); 50 | 51 | if (!Arrays.equals(handshakeBytes, receivedBytes)) { 52 | throw new IOException("Handshake failed - wrong message from Debugger"); 53 | } 54 | 55 | ByteBuffer outBuffer = ByteBuffer.wrap(handshakeBytes); 56 | while (outBuffer.hasRemaining()) { 57 | socketChannel.write(outBuffer); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /connections/src/main/java/kaap/veiko/debuggerforker/connections/VMConnector.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.connections; 2 | 3 | import java.io.IOException; 4 | import java.net.ConnectException; 5 | import java.net.InetSocketAddress; 6 | import java.nio.ByteBuffer; 7 | import java.nio.channels.SocketChannel; 8 | import java.util.Arrays; 9 | import java.util.function.Consumer; 10 | 11 | import kaap.veiko.debuggerforker.packet.VirtualMachinePacketStream; 12 | 13 | public class VMConnector extends ConnectorBase { 14 | 15 | private final InetSocketAddress socketAddress; 16 | 17 | public static VMConnector create(InetSocketAddress socketAddress, Consumer listener) { 18 | return new VMConnector(socketAddress, listener); 19 | } 20 | 21 | private VMConnector(InetSocketAddress socketAddress, Consumer listener) { 22 | super(listener, 1, "VmConnectorThread"); 23 | this.socketAddress = socketAddress; 24 | } 25 | 26 | @Override 27 | protected VirtualMachinePacketStream getConnectionBlocking() throws IOException { 28 | SocketChannel socketChannel = connect(socketAddress); 29 | if (socketChannel == null) { 30 | return null; 31 | } 32 | 33 | handshake(socketChannel); 34 | return new VirtualMachinePacketStream(socketChannel); 35 | } 36 | 37 | private SocketChannel connect(InetSocketAddress address) throws IOException { 38 | try { 39 | SocketChannel channel = SocketChannel.open(address); 40 | channel.configureBlocking(true); 41 | if (!channel.finishConnect()) { 42 | throw new IOException("Failed to connect channel"); 43 | } 44 | return channel; 45 | } 46 | catch (ConnectException connectException) { 47 | try { 48 | Thread.sleep(50); 49 | return null; 50 | } 51 | catch (InterruptedException interruptedException) { 52 | Thread.currentThread().interrupt(); 53 | throw new IOException("Connection failed - InterruptedException before connected to the VM"); 54 | } 55 | } 56 | } 57 | 58 | private void handshake(SocketChannel socketChannel) throws IOException { 59 | byte[] handshakeBytes = "JDWP-Handshake".getBytes("UTF-8"); 60 | 61 | ByteBuffer outBuffer = ByteBuffer.wrap(handshakeBytes); 62 | while (outBuffer.hasRemaining()) { 63 | socketChannel.write(outBuffer); 64 | } 65 | 66 | ByteBuffer inBuffer = ByteBuffer.allocate(handshakeBytes.length); 67 | int bytesRead = 0; 68 | while (bytesRead != -1 && inBuffer.hasRemaining()) { 69 | bytesRead = socketChannel.read(inBuffer); 70 | } 71 | byte[] receivedBytes = inBuffer.array(); 72 | 73 | if (!Arrays.equals(handshakeBytes, receivedBytes)) { 74 | throw new IOException("Handshake failed - wrong message from VM"); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /integration-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jvm-debugger-forker 7 | kaap.veiko 8 | 0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | integration-tests 13 | 14 | 15 | 16 | org.ow2.asm 17 | asm 18 | 6.0 19 | 20 | 21 | org.ow2.asm 22 | asm-commons 23 | 6.0 24 | 25 | 26 | ${project.groupId} 27 | server-logic 28 | ${project.version} 29 | 30 | 31 | com.sun 32 | tools 33 | 1.8 34 | system 35 | ${java.home}/../lib/tools.jar 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /integration-tests/src/main/java/ee/veikokaap/debugproxy/testframework/EventRequestIdentifier.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.testframework; 2 | 3 | import java.lang.reflect.Field; 4 | import com.sun.jdi.request.EventRequest; 5 | 6 | final class EventRequestIdentifier { 7 | private final Class requestClass; 8 | private final int requestId; 9 | 10 | static EventRequestIdentifier fromEventRequest(EventRequest request) throws ReflectiveOperationException { 11 | Field field = getDeclaredField(request.getClass(), "id"); 12 | field.setAccessible(true); 13 | int requestId = field.getInt(request); 14 | return new EventRequestIdentifier(request.getClass(), requestId); 15 | } 16 | 17 | private EventRequestIdentifier(Class requestClass, int requestId) { 18 | this.requestClass = requestClass; 19 | this.requestId = requestId; 20 | } 21 | 22 | public int getRequestId() { 23 | return requestId; 24 | } 25 | 26 | public Class getRequestClass() { 27 | return requestClass; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) { 33 | return true; 34 | } 35 | if (o == null || getClass() != o.getClass()) { 36 | return false; 37 | } 38 | 39 | EventRequestIdentifier that = (EventRequestIdentifier) o; 40 | 41 | if (requestId != that.requestId) { 42 | return false; 43 | } 44 | return requestClass != null ? requestClass.equals(that.requestClass) : that.requestClass == null; 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | int result = requestClass != null ? requestClass.hashCode() : 0; 50 | result = 31 * result + requestId; 51 | return result; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "EventRequestIdentifier{" + 57 | "requestClass=" + requestClass + 58 | ", requestId=" + requestId + 59 | '}'; 60 | } 61 | 62 | private static Field getDeclaredField(Class clazz, String fieldName) throws NoSuchFieldException { 63 | if (clazz == null) { 64 | throw new NoSuchFieldException(fieldName); 65 | } 66 | 67 | try { 68 | return clazz.getDeclaredField(fieldName); 69 | } 70 | catch (Exception ignored) { 71 | return getDeclaredField(clazz.getSuperclass(), fieldName); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /integration-tests/src/main/java/ee/veikokaap/debugproxy/testframework/SuspendManager.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.testframework; 2 | 3 | import java.util.function.Consumer; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import com.sun.jdi.event.BreakpointEvent; 7 | import com.sun.jdi.event.Event; 8 | 9 | public class SuspendManager { 10 | private final DebuggerProcess parentDebugger; 11 | private final Event event; 12 | 13 | SuspendManager(DebuggerProcess parentDebugger, Event event) { 14 | this.parentDebugger = parentDebugger; 15 | this.event = event; 16 | } 17 | 18 | void resume() { 19 | parentDebugger.resume(); 20 | } 21 | 22 | void stepOver(@Nullable Consumer listener) throws Throwable { 23 | BreakpointEvent event = (BreakpointEvent) this.event; 24 | AsyncTester tester = parentDebugger.stepOver(event, listener); 25 | resume(); 26 | tester.joinAndTest(); 27 | } 28 | 29 | void stepInto(@Nullable Consumer listener) throws Throwable { 30 | BreakpointEvent event = (BreakpointEvent) this.event; 31 | AsyncTester tester = parentDebugger.stepInto(event, listener); 32 | resume(); 33 | tester.joinAndTest(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /integration-tests/src/main/java/ee/veikokaap/debugproxy/testframework/utils/BreakpointLocation.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.testframework.utils; 2 | 3 | public class BreakpointLocation { 4 | private final String className; 5 | private final int lineNumber; 6 | 7 | BreakpointLocation(String className, int lineNumber) { 8 | this.className = className; 9 | this.lineNumber = lineNumber; 10 | } 11 | 12 | public String getClassName() { 13 | return className; 14 | } 15 | 16 | public int getLineNumber() { 17 | return lineNumber; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | if (this == o) { 23 | return true; 24 | } 25 | if (o == null || getClass() != o.getClass()) { 26 | return false; 27 | } 28 | 29 | BreakpointLocation location = (BreakpointLocation) o; 30 | 31 | if (lineNumber != location.lineNumber) { 32 | return false; 33 | } 34 | return className != null ? className.equals(location.className) : location.className == null; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int result = className != null ? className.hashCode() : 0; 40 | result = 31 * result + lineNumber; 41 | return result; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "BreakpointLocation{" + 47 | "className='" + className + '\'' + 48 | ", lineNumber=" + lineNumber + 49 | '}'; 50 | } 51 | } -------------------------------------------------------------------------------- /integration-tests/src/main/java/ee/veikokaap/debugproxy/testframework/utils/BreakpointUtil.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.testframework.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class BreakpointUtil { 7 | 8 | private static final Map> cache = new HashMap<>(); 9 | 10 | /** 11 | * Method that's only used for later placing breakpoints on the same line 12 | * @param markerId from 0 to 5 13 | */ 14 | public static void mark(int markerId) { 15 | } 16 | 17 | public static BreakpointLocation findBreakLocation(Class clazz, int id) { 18 | if (!cache.containsKey(clazz)) { 19 | cache.put(clazz, MarkerFinder.findLocations(clazz)); 20 | } 21 | 22 | return cache.get(clazz).get(id); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /integration-tests/src/main/kotlin/ee/veikokaap/debugproxy/testframework/AsyncTester.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.testframework 2 | 3 | import java.util.concurrent.TimeUnit 4 | import java.util.concurrent.TimeoutException 5 | import java.util.concurrent.atomic.AtomicReference 6 | import java.util.function.Consumer 7 | 8 | open class AsyncTester(private val consumer: Consumer): Consumer { 9 | 10 | protected val exception = AtomicReference(null) 11 | private val thread = AtomicReference(null) 12 | 13 | override fun accept(t: T) { 14 | if (exception.get() != null) { 15 | return // Test previously failed 16 | } 17 | 18 | thread.set(Thread.currentThread()) 19 | try { 20 | consumer.accept(t) 21 | } catch (e: Throwable) { 22 | exception.set(e) 23 | } 24 | } 25 | 26 | @Throws(Throwable::class) 27 | fun joinAndTest() { 28 | joinAndTest(Long.MAX_VALUE, TimeUnit.DAYS); 29 | } 30 | 31 | @Throws(Throwable::class) 32 | fun joinAndTest(time: Long, timeUnit: TimeUnit) { 33 | val millis = timeUnit.toMillis(time) 34 | val start = System.currentTimeMillis() 35 | 36 | waitForConsumerRun(millis, start) 37 | if (thread.get() == null) { 38 | throw AssertionError("Consumer hasn't been run in $time $timeUnit") 39 | } 40 | 41 | val timeLeft = timeLeft(millis, start) 42 | if (timeLeft <= 0) { 43 | throwTimeoutException(time, timeUnit) 44 | } 45 | thread.get().join(timeLeft) 46 | 47 | if (thread.get().isAlive) { 48 | throwTimeoutException(time, timeUnit) 49 | } 50 | if (exception.get() != null) { 51 | throw exception.get() 52 | } 53 | } 54 | 55 | private fun throwTimeoutException(time: Long, timeUnit: TimeUnit) { 56 | thread.get().interrupt() 57 | val timeoutException = TimeoutException("Thread should have finished work in $time $timeUnit") 58 | if (exception.get() != null) { 59 | timeoutException.addSuppressed(exception.get()) 60 | } 61 | throw timeoutException 62 | } 63 | 64 | private fun timeLeft(millis: Long, start: Long) = millis - (System.currentTimeMillis() - start) 65 | 66 | @Throws(InterruptedException::class) 67 | private fun waitForConsumerRun(millis: Long, start: Long) { 68 | while (thread.get() == null && System.currentTimeMillis() - start < millis) { 69 | Thread.sleep(100) 70 | } 71 | } 72 | 73 | fun failTestWithException(e: Exception) = exception.set(e) 74 | 75 | } 76 | -------------------------------------------------------------------------------- /integration-tests/src/main/kotlin/ee/veikokaap/debugproxy/testframework/SuspendManagerAsyncTester.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.testframework 2 | 3 | import java.util.* 4 | import java.util.function.Consumer 5 | 6 | class SuspendManagerAsyncTester(consumer: Consumer): AsyncTester(consumer) { 7 | 8 | private var afterQueue: Deque = ArrayDeque(); 9 | 10 | override fun accept(t: SuspendManager?) { 11 | super.accept(t); 12 | 13 | if (exception.get() == null) { 14 | afterQueue.forEach{ 15 | when (it.afterAction) { 16 | AfterAction.RESUME -> t!!.resume() 17 | AfterAction.STEP_OVER -> t!!.stepOver(it.listener) 18 | AfterAction.STEP_INTO -> t!!.stepInto(it.listener) 19 | } 20 | } 21 | } 22 | } 23 | 24 | infix fun thenResume(u: () -> Unit): SuspendManagerAsyncTester { 25 | afterQueue.addLast(AfterBreak(AfterAction.RESUME, null)) 26 | return this 27 | } 28 | 29 | infix fun thenStepOver(listener: ((SuspendManager) -> Unit)?): SuspendManagerAsyncTester { 30 | afterQueue.addLast(AfterBreak(AfterAction.STEP_OVER, listener)) 31 | return this 32 | } 33 | 34 | private data class AfterBreak(val afterAction: AfterAction, val listener: ((SuspendManager) -> Unit)?) 35 | 36 | private enum class AfterAction { 37 | RESUME, STEP_OVER, STEP_INTO 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/ee/veikokaap/debugproxy/tests/LoopBreakpointClass.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests; 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil; 4 | 5 | public class LoopBreakpointClass { 6 | 7 | public static final String BEFORE_MESSAGE = "Before breakpoints"; 8 | public static final String BREAKPOINT_MESSAGE_PREFIX = "Breakpoint: "; 9 | public static final String AFTER_MESSAGE = "After breakpoints"; 10 | 11 | public static String getBreakpointMessage(int breakCount) { 12 | return BREAKPOINT_MESSAGE_PREFIX + breakCount; 13 | } 14 | 15 | public static void main(String[] args) { 16 | System.out.println(BEFORE_MESSAGE); 17 | for (int i = 0; i < 10; i++) { 18 | System.out.println(getBreakpointMessage(i)); 19 | BreakpointUtil.mark(0); 20 | } 21 | System.out.println(AFTER_MESSAGE); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/ee/veikokaap/debugproxy/tests/SimpleBreakpointClass.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests; 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil; 4 | 5 | public class SimpleBreakpointClass { 6 | public static void main(String[] args) { 7 | System.out.println("Before breakpoints"); 8 | BreakpointUtil.mark(0); 9 | System.out.println("After breakpoint 0"); 10 | BreakpointUtil.mark(1); 11 | System.out.println("After breakpoint 1"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /integration-tests/src/test/java/ee/veikokaap/debugproxy/tests/StepBreakpointClass.java: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests; 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil; 4 | 5 | public class StepBreakpointClass { 6 | 7 | public static final String BEFORE_MESSAGE = "Before breakpoints"; 8 | public static final String AFTER_BREAKPOINT_0 = "After breakpoint 0"; 9 | public static final String STEPPED_OVER = "After step from breakpoint 0"; 10 | public static final String AFTER_BREAKPOINT_1 = "After breakpoint 1"; 11 | public static final String LAST_MESSAGE = "After breakpoints"; 12 | 13 | public static void main(String[] args) { 14 | System.out.println(BEFORE_MESSAGE); 15 | BreakpointUtil.mark(0); System.out.println(AFTER_BREAKPOINT_0); 16 | System.out.println(STEPPED_OVER); 17 | BreakpointUtil.mark(1); System.out.println(AFTER_BREAKPOINT_1); 18 | System.out.println(LAST_MESSAGE); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /integration-tests/src/test/kotlin/ee/veikokaap/debugproxy/tests/doubledebugger/SimpleDoubleDebuggerTests.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests.doubledebugger 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil 4 | import ee.veikokaap.debugproxy.tests.SimpleBreakpointClass 5 | import ee.veikokaap.debugproxy.tests.assertAddedOutput 6 | import ee.veikokaap.debugproxy.tests.runTest 7 | import org.junit.Test 8 | import java.util.concurrent.TimeUnit 9 | import kotlin.test.assertTrue 10 | 11 | open class SimpleDoubleDebuggerTests { 12 | 13 | val testClass = SimpleBreakpointClass::class.java 14 | val firstLocation = BreakpointUtil.findBreakLocation(testClass, 0) 15 | val secondLocation = BreakpointUtil.findBreakLocation(testClass, 1) 16 | 17 | @Test 18 | fun `test single breakpoint with 2 debuggers`() = runTest(testClass) { jvm, firstDebugger, secondDebugger -> 19 | val firstBreak = firstDebugger.breakAt(firstLocation) { 20 | jvm.outputDeque.assertAddedOutput("Before breakpoints") 21 | } thenResume {} 22 | 23 | val secondBreak = secondDebugger.breakAt(firstLocation) { 24 | firstBreak.joinAndTest(4, TimeUnit.SECONDS) 25 | assertTrue(jvm.outputDeque.isEmpty()) 26 | } thenResume {} 27 | 28 | firstDebugger.allBreakpointSet() 29 | 30 | secondBreak.joinAndTest() 31 | jvm.waitForExit() 32 | 33 | jvm.outputDeque.assertAddedOutput("After breakpoint 0", "After breakpoint 1") 34 | } 35 | 36 | @Test 37 | fun `test 2 breakpoints with 2 debuggers`() = runTest(testClass) { jvm, firstDebugger, secondDebugger -> 38 | val firstBreak = firstDebugger.breakAt(firstLocation) { 39 | jvm.outputDeque.assertAddedOutput("Before breakpoints") 40 | } thenResume {} 41 | 42 | val secondBreak = secondDebugger.breakAt(secondLocation) { 43 | jvm.outputDeque.assertAddedOutput("After breakpoint 0") 44 | } thenResume {} 45 | 46 | firstDebugger.allBreakpointSet() 47 | secondDebugger.allBreakpointSet() 48 | 49 | firstBreak.joinAndTest() 50 | secondBreak.joinAndTest() 51 | jvm.waitForExit() 52 | 53 | jvm.outputDeque.assertAddedOutput("After breakpoint 1") 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /integration-tests/src/test/kotlin/ee/veikokaap/debugproxy/tests/doubledebugger/StepDoubleDebuggerTests.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests.doubledebugger 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil 4 | import ee.veikokaap.debugproxy.tests.StepBreakpointClass 5 | import ee.veikokaap.debugproxy.tests.assertAddedOutput 6 | import ee.veikokaap.debugproxy.tests.runTest 7 | import org.junit.Test 8 | 9 | open class StepDoubleDebuggerTests { 10 | 11 | val testClass = StepBreakpointClass::class.java 12 | val firstBreakpoint = BreakpointUtil.findBreakLocation(testClass, 0) 13 | val secondBreakpoint = BreakpointUtil.findBreakLocation(testClass, 1) 14 | 15 | @Test 16 | fun `test two breakpoints that step to next line with two debuggers`() = runTest(testClass) { jvm, firstDebugger, secondDebugger -> 17 | val firstBreak = firstDebugger.breakAt(firstBreakpoint) { 18 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.BEFORE_MESSAGE) 19 | } thenStepOver { 20 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_0) 21 | } thenResume {} 22 | 23 | val secondBreak = secondDebugger.breakAt(secondBreakpoint) { 24 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER) 25 | } thenStepOver { 26 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_1) 27 | } thenResume {} 28 | 29 | firstDebugger.allBreakpointSet() 30 | 31 | firstBreak.joinAndTest() 32 | secondBreak.joinAndTest() 33 | jvm.waitForExit() 34 | 35 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.LAST_MESSAGE) 36 | } 37 | 38 | @Test 39 | fun `test stepping onto a existing breakpoint with two debuggers`() = runTest(testClass) { jvm, firstDebugger, secondDebugger -> 40 | val firstBreak = firstDebugger.breakAt(firstBreakpoint) { 41 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.BEFORE_MESSAGE) 42 | } thenStepOver { 43 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_0) 44 | } thenStepOver { 45 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER, remove = false) 46 | } thenResume {} 47 | 48 | val secondBreak = secondDebugger.breakAt(secondBreakpoint) { 49 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER, remove = false) 50 | firstBreak.joinAndTest() // wait until first breakpoint and steps have resumed 51 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER) 52 | } thenStepOver { 53 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_1) 54 | } thenResume {} 55 | 56 | firstDebugger.allBreakpointSet() 57 | 58 | firstBreak.joinAndTest() 59 | secondBreak.joinAndTest() 60 | jvm.waitForExit() 61 | 62 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.LAST_MESSAGE) 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /integration-tests/src/test/kotlin/ee/veikokaap/debugproxy/tests/singledebugger/LoopSingleDebuggerTests.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests.singledebugger 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil 4 | import ee.veikokaap.debugproxy.tests.LoopBreakpointClass 5 | import ee.veikokaap.debugproxy.tests.assertAddedOutput 6 | import ee.veikokaap.debugproxy.tests.runTest 7 | import org.junit.Test 8 | import java.util.concurrent.TimeUnit 9 | 10 | open class LoopSingleDebuggerTests { 11 | 12 | val testClass = LoopBreakpointClass::class.java 13 | val breakpoint = BreakpointUtil.findBreakLocation(testClass, 0) 14 | 15 | @Test 16 | fun `test a single breakpoint in for loop with a single debugger`() = runTest(testClass) { jvm, debugger -> 17 | var count = 0; 18 | val breakpoint = debugger.breakAt(breakpoint) { breakpoint -> 19 | if (count == 0) { 20 | jvm.outputDeque.assertAddedOutput(LoopBreakpointClass.BEFORE_MESSAGE, LoopBreakpointClass.getBreakpointMessage(0)) 21 | } 22 | else { 23 | jvm.outputDeque.assertAddedOutput(LoopBreakpointClass.getBreakpointMessage(count)) 24 | } 25 | count++; 26 | } thenResume {} 27 | 28 | debugger.allBreakpointSet() 29 | 30 | breakpoint.joinAndTest() 31 | jvm.waitForExit() 32 | 33 | jvm.outputDeque.assertAddedOutput(LoopBreakpointClass.AFTER_MESSAGE) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /integration-tests/src/test/kotlin/ee/veikokaap/debugproxy/tests/singledebugger/SimpleSingleDebuggerTests.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests.singledebugger 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil 4 | import ee.veikokaap.debugproxy.tests.SimpleBreakpointClass 5 | import ee.veikokaap.debugproxy.tests.assertAddedOutput 6 | import ee.veikokaap.debugproxy.tests.runTest 7 | import org.junit.Test 8 | 9 | open class SimpleSingleDebuggerTests { 10 | 11 | val testClass = SimpleBreakpointClass::class.java 12 | val firstBreakpoint = BreakpointUtil.findBreakLocation(testClass, 0) 13 | val secondBreakpoint = BreakpointUtil.findBreakLocation(testClass, 1) 14 | 15 | @Test 16 | fun `test a single breakpoint with a single debugger`() = runTest(testClass) { jvm, debugger -> 17 | val breakpoint = debugger.breakAt(firstBreakpoint) { breakpoint -> 18 | jvm.outputDeque.assertAddedOutput("Before breakpoints") 19 | } thenResume {} 20 | 21 | debugger.allBreakpointSet() 22 | 23 | breakpoint.joinAndTest() 24 | jvm.waitForExit() 25 | 26 | jvm.outputDeque.assertAddedOutput("After breakpoint 0", "After breakpoint 1") 27 | } 28 | 29 | @Test 30 | fun `test 2 breakpoints with a single debugger`() = runTest(testClass) { jvm, debugger -> 31 | val firstBreak = debugger.breakAt(firstBreakpoint) { 32 | jvm.outputDeque.assertAddedOutput("Before breakpoints") 33 | } thenResume {} 34 | 35 | val secondBreak = debugger.breakAt(secondBreakpoint) { 36 | jvm.outputDeque.assertAddedOutput("After breakpoint 0") 37 | } thenResume {} 38 | 39 | debugger.allBreakpointSet() 40 | 41 | firstBreak.joinAndTest() 42 | secondBreak.joinAndTest() 43 | jvm.waitForExit() 44 | 45 | jvm.outputDeque.assertAddedOutput("After breakpoint 1") 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /integration-tests/src/test/kotlin/ee/veikokaap/debugproxy/tests/singledebugger/StepSingleDebuggerTests.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests.singledebugger 2 | 3 | import ee.veikokaap.debugproxy.testframework.utils.BreakpointUtil 4 | import ee.veikokaap.debugproxy.tests.StepBreakpointClass 5 | import ee.veikokaap.debugproxy.tests.assertAddedOutput 6 | import ee.veikokaap.debugproxy.tests.runTest 7 | import org.junit.Test 8 | 9 | open class StepSingleDebuggerTests { 10 | 11 | val testClass = StepBreakpointClass::class.java 12 | val firstBreakpoint = BreakpointUtil.findBreakLocation(testClass, 0) 13 | val secondBreakpoint = BreakpointUtil.findBreakLocation(testClass, 1) 14 | 15 | @Test 16 | fun `test two breakpoints that step to next line with a single debugger`() = 17 | runTest(testClass) { jvm, debugger -> 18 | val firstBreak = debugger.breakAt(firstBreakpoint) { 19 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.BEFORE_MESSAGE) 20 | } thenStepOver { 21 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_0) 22 | } thenResume {} 23 | 24 | val secondBreak = debugger.breakAt(secondBreakpoint) { 25 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER) 26 | } thenStepOver { 27 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_1) 28 | } thenResume {} 29 | 30 | debugger.allBreakpointSet() 31 | firstBreak.joinAndTest() 32 | secondBreak.joinAndTest() 33 | jvm.waitForExit() 34 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.LAST_MESSAGE) 35 | } 36 | 37 | @Test 38 | fun `test stepping onto a existing breakpoint with a single debugger`() = runTest(testClass) { jvm, debugger -> 39 | val firstBreak = debugger.breakAt(firstBreakpoint) { 40 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.BEFORE_MESSAGE) 41 | } thenStepOver { 42 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_0) 43 | } thenStepOver { 44 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER, remove = false) 45 | } 46 | 47 | val secondBreak = debugger.breakAt(secondBreakpoint) { 48 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER, remove = false) 49 | firstBreak.joinAndTest() // wait until first breakpoint and steps have resumed 50 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.STEPPED_OVER) 51 | } thenStepOver { 52 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.AFTER_BREAKPOINT_1) 53 | } thenResume {} 54 | 55 | debugger.allBreakpointSet() 56 | 57 | firstBreak.joinAndTest() 58 | secondBreak.joinAndTest() 59 | jvm.waitForExit() 60 | 61 | jvm.outputDeque.assertAddedOutput(StepBreakpointClass.LAST_MESSAGE) 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /integration-tests/src/test/kotlin/ee/veikokaap/debugproxy/tests/testFunctions.kt: -------------------------------------------------------------------------------- 1 | package ee.veikokaap.debugproxy.tests 2 | 3 | import ee.veikokaap.debugproxy.testframework.AsyncTester 4 | import ee.veikokaap.debugproxy.testframework.DebuggerProcess 5 | import ee.veikokaap.debugproxy.testframework.JvmProcess 6 | import java.util.* 7 | import java.util.concurrent.TimeUnit 8 | import java.util.function.Consumer 9 | import kotlin.test.assertEquals 10 | 11 | fun Deque.assertAddedOutput(vararg lines: String, remove: Boolean = true) { 12 | val expectedList = lines.toList() 13 | 14 | synchronized(this) { 15 | val actualPolledList = this.toList(); 16 | if (remove) { 17 | this.clear() 18 | } 19 | assertEquals(expectedList, actualPolledList) 20 | } 21 | } 22 | 23 | fun runTest(testClass: Class<*>, timeout: Long = 10, timeUnit: TimeUnit = TimeUnit.SECONDS, test: (JvmProcess, DebuggerProcess) -> Unit) { 24 | JvmProcess.runClass(testClass).use { jvm -> 25 | DebuggerProcess.attach().use { debugger -> 26 | val tester = asyncTester { 27 | assertEquals("Listening for transport dt_socket at address: ${JvmProcess.DEBUGGER_PORT}", jvm.outputDeque.pollLast()) 28 | test(jvm, debugger) 29 | } 30 | Thread { tester.accept(null) }.start() 31 | tester.joinAndTest(timeout, timeUnit) 32 | } 33 | } 34 | } 35 | 36 | fun runTest(testClass: Class<*>, timeout: Long = 10, timeUnit: TimeUnit = TimeUnit.SECONDS, test: (JvmProcess, DebuggerProcess, DebuggerProcess) -> Unit) { 37 | JvmProcess.runClass(testClass).use { jvm -> 38 | DebuggerProcess.attach().use { firstDebugger -> 39 | DebuggerProcess.attach().use { secondDebugger -> 40 | val tester = asyncTester { 41 | assertEquals("Listening for transport dt_socket at address: ${JvmProcess.DEBUGGER_PORT}", jvm.outputDeque.pollLast()) 42 | test(jvm, firstDebugger, secondDebugger) 43 | } 44 | Thread { tester.accept(null) }.start() 45 | tester.joinAndTest(timeout, timeUnit) 46 | } 47 | } 48 | } 49 | } 50 | 51 | fun asyncTester(f: (T) -> Unit) = AsyncTester(object : Consumer { 52 | override fun accept(t: T) { 53 | f(t) 54 | } 55 | }) 56 | 57 | fun toRunnable(f: () -> Unit): Runnable = object : Runnable { 58 | override fun run() { 59 | f() 60 | } 61 | } -------------------------------------------------------------------------------- /packet/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jvm-debugger-forker 7 | kaap.veiko 8 | 0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | packet 13 | 14 | 15 | 16 | kaap.veiko 17 | types 18 | ${parent.version} 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/CommandPacket.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | import java.util.Arrays; 4 | 5 | import kaap.veiko.debuggerforker.packet.internal.PacketBase; 6 | 7 | public class CommandPacket extends PacketBase { 8 | 9 | private final short commandId; 10 | private final short commandSetId; 11 | private ReplyPacket replyPacket; 12 | 13 | public CommandPacket(int id, short commandSetId, short commandId, byte[] data, PacketSource source) { 14 | super(id, data, source); 15 | this.commandSetId = commandSetId; 16 | this.commandId = commandId; 17 | } 18 | 19 | public short getCommandId() { 20 | return commandId; 21 | } 22 | 23 | public short getCommandSetId() { 24 | return commandSetId; 25 | } 26 | 27 | public ReplyPacket getReplyPacket() { 28 | return replyPacket; 29 | } 30 | 31 | public void setReplyPacket(ReplyPacket replyPacket) { 32 | this.replyPacket = replyPacket; 33 | } 34 | 35 | @Override 36 | public boolean isReply() { 37 | return false; 38 | } 39 | 40 | @Override 41 | public T visit(PacketVisitor visitor) { 42 | return visitor.visit(this); 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) { 48 | return true; 49 | } 50 | if (o == null || getClass() != o.getClass()) { 51 | return false; 52 | } 53 | if (!super.equals(o)) { 54 | return false; 55 | } 56 | 57 | CommandPacket that = (CommandPacket) o; 58 | 59 | if (commandId != that.commandId) { 60 | return false; 61 | } 62 | if (commandSetId != that.commandSetId) { 63 | return false; 64 | } 65 | return replyPacket != null ? replyPacket.equals(that.replyPacket) : that.replyPacket == null; 66 | } 67 | 68 | @Override 69 | public int hashCode() { 70 | int result = super.hashCode(); 71 | result = 31 * result + (int) commandId; 72 | result = 31 * result + (int) commandSetId; 73 | return result; 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return "CommandPacket{" + 79 | "length=" + getLength() + 80 | ", id=" + getId() + 81 | ", flags=" + getFlags() + 82 | ", commandId=" + commandId + 83 | ", commandSetId=" + commandSetId + 84 | ", data=" + Arrays.toString(getData()) + 85 | ", source=" + getSource() + 86 | '}'; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/DebuggerPacketStream.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | import static kaap.veiko.debuggerforker.packet.PacketSource.SourceType.DEBUGGER; 4 | 5 | import java.io.IOException; 6 | import java.nio.channels.SocketChannel; 7 | 8 | import kaap.veiko.debuggerforker.packet.internal.PacketTransformer; 9 | import kaap.veiko.debuggerforker.packet.internal.PacketStreamBase; 10 | 11 | public class DebuggerPacketStream extends PacketStreamBase { 12 | 13 | public DebuggerPacketStream(SocketChannel socketChannel, PacketTransformer packetTransformer) throws IOException { 14 | super(socketChannel, DEBUGGER, packetTransformer); 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return "DebuggerPacketStream{" + 20 | "socketChannel=" + getSocketChannel() + 21 | '}'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/Packet.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | public interface Packet { 4 | int HEADER_LENGTH = 11; 5 | 6 | int getLength(); 7 | 8 | int getId(); 9 | 10 | short getFlags(); 11 | 12 | byte[] getData(); 13 | 14 | boolean isReply(); 15 | 16 | default boolean hasData() { 17 | return getLength() > HEADER_LENGTH; 18 | } 19 | 20 | PacketSource getSource(); 21 | 22 | boolean isSynthetic(); 23 | 24 | T visit(PacketVisitor visitor); 25 | } 26 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/PacketSource.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | import java.nio.channels.SocketChannel; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import kaap.veiko.debuggerforker.packet.internal.PacketTransformer; 7 | 8 | public class PacketSource { 9 | 10 | private final static AtomicInteger counter = new AtomicInteger(0); 11 | 12 | private final int id; 13 | 14 | private final SocketChannel socketChannel; 15 | private final SourceType sourceType; 16 | private final PacketTransformer packetTransformer; 17 | 18 | private boolean holdEvents; 19 | 20 | public PacketSource(SocketChannel socketChannel, SourceType sourceType, PacketTransformer packetTransformer) { 21 | this.socketChannel = socketChannel; 22 | this.sourceType = sourceType; 23 | this.packetTransformer = packetTransformer; 24 | id = counter.getAndIncrement(); 25 | } 26 | 27 | public int createNewOutputId() { 28 | return packetTransformer.createNewId(); 29 | } 30 | 31 | public boolean isDebugger() { 32 | return sourceType == SourceType.DEBUGGER; 33 | } 34 | 35 | public boolean isVirtualMachine() { 36 | return sourceType == SourceType.VIRTUAL_MACHINE; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) { 42 | return true; 43 | } 44 | if (o == null || getClass() != o.getClass()) { 45 | return false; 46 | } 47 | 48 | PacketSource that = (PacketSource) o; 49 | 50 | return socketChannel != null ? socketChannel.equals(that.socketChannel) : that.socketChannel == null; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return socketChannel != null ? socketChannel.hashCode() : 0; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return sourceType.toString() + "_" + id; 61 | 62 | } 63 | 64 | public void setHoldEvents(boolean holdEvents) { 65 | this.holdEvents = holdEvents; 66 | } 67 | 68 | public boolean isHoldEvents() { 69 | return holdEvents; 70 | } 71 | 72 | public enum SourceType { 73 | DEBUGGER, 74 | VIRTUAL_MACHINE 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/PacketStream.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.SocketChannel; 5 | 6 | public interface PacketStream { 7 | Packet read() throws IOException; 8 | 9 | void write(Packet packet) throws IOException; 10 | 11 | SocketChannel getSocketChannel(); 12 | 13 | PacketSource getSource(); 14 | 15 | void close(); 16 | 17 | boolean isClosed(); 18 | } 19 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/PacketVisitor.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | public interface PacketVisitor { 4 | T visit(ReplyPacket replyPacket); 5 | 6 | T visit(CommandPacket commandPacket); 7 | } 8 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/ReplyPacket.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | import java.util.Arrays; 4 | 5 | import kaap.veiko.debuggerforker.packet.internal.PacketBase; 6 | 7 | public class ReplyPacket extends PacketBase { 8 | 9 | private final short errorCode; 10 | private CommandPacket commandPacket; 11 | 12 | public ReplyPacket(int id, short errorCode, byte[] data, PacketSource source) { 13 | super(id, data, source); 14 | this.errorCode = errorCode; 15 | } 16 | 17 | public short getErrorCode() { 18 | return errorCode; 19 | } 20 | 21 | public CommandPacket getCommandPacket() { 22 | return commandPacket; 23 | } 24 | 25 | public void setCommandPacket(CommandPacket commandPacket) { 26 | this.commandPacket = commandPacket; 27 | } 28 | 29 | @Override 30 | public boolean isReply() { 31 | return true; 32 | } 33 | 34 | @Override 35 | public T visit(PacketVisitor visitor) { 36 | return visitor.visit(this); 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) { 42 | return true; 43 | } 44 | if (o == null || getClass() != o.getClass()) { 45 | return false; 46 | } 47 | if (!super.equals(o)) { 48 | return false; 49 | } 50 | 51 | ReplyPacket that = (ReplyPacket) o; 52 | 53 | if (errorCode != that.errorCode) { 54 | return false; 55 | } 56 | return commandPacket != null ? commandPacket.equals(that.commandPacket) : that.commandPacket == null; 57 | } 58 | 59 | @Override 60 | public int hashCode() { 61 | int result = super.hashCode(); 62 | result = 31 * result + (int) errorCode; 63 | return result; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "ReplyPacket{" + 69 | "length=" + getLength() + 70 | ", id=" + getId() + 71 | ", flags=" + getFlags() + 72 | ", errorCode=" + errorCode + 73 | ", data=" + Arrays.toString(getData()) + 74 | ", source=" + getSource() + 75 | '}'; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/VirtualMachinePacketStream.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet; 2 | 3 | import static kaap.veiko.debuggerforker.packet.PacketSource.SourceType.VIRTUAL_MACHINE; 4 | 5 | import java.io.IOException; 6 | import java.nio.channels.SocketChannel; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ConcurrentMap; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import kaap.veiko.debuggerforker.packet.internal.PacketStreamBase; 14 | import kaap.veiko.debuggerforker.packet.internal.VmPacketTransformer; 15 | 16 | public class VirtualMachinePacketStream extends PacketStreamBase { 17 | 18 | private final Logger log = LoggerFactory.getLogger(VirtualMachinePacketStream.class); 19 | 20 | private final ConcurrentMap writtenCommands = new ConcurrentHashMap<>(); 21 | private final ReplyPacketVisitor replyPacketVisitor = new ReplyPacketVisitor(); 22 | 23 | public VirtualMachinePacketStream(SocketChannel socketChannel) throws IOException { 24 | super(socketChannel, VIRTUAL_MACHINE, new VmPacketTransformer()); 25 | } 26 | 27 | @Override 28 | public Packet read() throws IOException { 29 | Packet packet = super.read(); 30 | if (packet == null) { 31 | return null; 32 | } 33 | 34 | return packet.visit(replyPacketVisitor); 35 | } 36 | 37 | @Override 38 | public void write(Packet packet) throws IOException { 39 | if (!packet.isReply() && packet instanceof CommandPacket) { 40 | writtenCommands.put(packet.getId(), (CommandPacket) packet); 41 | super.write(packet); 42 | } 43 | else { 44 | log.error("VirtualMachine can't receive replies. Tried to write packet {}", packet); 45 | } 46 | } 47 | 48 | private class ReplyPacketVisitor implements PacketVisitor { 49 | @Override 50 | public Packet visit(ReplyPacket replyPacket) { 51 | CommandPacket commandPacket = writtenCommands.get(replyPacket.getId()); 52 | commandPacket.setReplyPacket(replyPacket); 53 | replyPacket.setCommandPacket(commandPacket); 54 | 55 | return replyPacket; 56 | } 57 | 58 | @Override 59 | public Packet visit(CommandPacket packet) { 60 | return packet; 61 | } 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "VirtualMachinePacketStream{" + 67 | "socketChannel=" + getSocketChannel() + 68 | '}'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/ByteBufferDataReader.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import kaap.veiko.debuggerforker.packet.utils.ByteBufferUtil; 9 | import kaap.veiko.debuggerforker.types.DataReader; 10 | 11 | import kaap.veiko.debuggerforker.types.VMInformation; 12 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 13 | 14 | public class ByteBufferDataReader implements DataReader { 15 | 16 | private final static Logger log = LoggerFactory.getLogger(ByteBufferDataReader.class); 17 | 18 | private final ByteBuffer byteBuffer; 19 | private final VMInformation vmInformation; 20 | 21 | public ByteBufferDataReader(ByteBuffer packetByteBuffer, VMInformation vmInformation) { 22 | this.byteBuffer = packetByteBuffer; 23 | this.vmInformation = vmInformation; 24 | } 25 | 26 | @Override 27 | public byte readByte() { 28 | return byteBuffer.get(); 29 | } 30 | 31 | @Override 32 | public short readShort() { 33 | return byteBuffer.getShort(); 34 | } 35 | 36 | @Override 37 | public int readInt() { 38 | return byteBuffer.getInt(); 39 | } 40 | 41 | @Override 42 | public long readLong() { 43 | return byteBuffer.getLong(); 44 | } 45 | 46 | @Override 47 | public float readFloat() { 48 | return byteBuffer.getFloat(); 49 | } 50 | 51 | @Override 52 | public double readDouble() { 53 | return byteBuffer.getDouble(); 54 | } 55 | 56 | @Override 57 | public long readLongOfSize(IdSizes.SizeType sizeType) { 58 | IdSizes idSizes = vmInformation.getIdSizes(); 59 | int size; 60 | if (idSizes == null) { 61 | log.warn("Parsing value without knowing its size in bytes. Assuming size is 8 bytes."); 62 | size = 8; 63 | } 64 | else { 65 | size = idSizes.getSizeOfType(sizeType); 66 | } 67 | return ByteBufferUtil.getLong(byteBuffer, size); 68 | } 69 | 70 | @Override 71 | public boolean readBoolean() { 72 | return byteBuffer.get() != 0; 73 | } 74 | 75 | @Override 76 | public String readString() { 77 | return ByteBufferUtil.getString(byteBuffer); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/ByteBufferDataWriter.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import kaap.veiko.debuggerforker.packet.utils.ByteBufferUtil; 9 | import kaap.veiko.debuggerforker.types.DataType; 10 | import kaap.veiko.debuggerforker.types.DataWriter; 11 | import kaap.veiko.debuggerforker.types.VMInformation; 12 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 13 | 14 | public class ByteBufferDataWriter implements DataWriter { 15 | 16 | private final static Logger log = LoggerFactory.getLogger(ByteBufferDataWriter.class); 17 | 18 | private final ByteBuffer byteBuffer; 19 | private final VMInformation vmInformation; 20 | 21 | public ByteBufferDataWriter(ByteBuffer byteBuffer, VMInformation vmInformation) { 22 | this.byteBuffer = byteBuffer; 23 | this.vmInformation = vmInformation; 24 | } 25 | 26 | @Override 27 | public void writeBoolean(boolean bool) { 28 | if (bool) { 29 | byteBuffer.put((byte) 1); 30 | } 31 | else { 32 | byteBuffer.put((byte) 0); 33 | } 34 | } 35 | 36 | @Override 37 | public void writeByte(byte b) { 38 | byteBuffer.put(b); 39 | } 40 | 41 | @Override 42 | public void writeShort(short s) { 43 | byteBuffer.putShort(s); 44 | } 45 | 46 | @Override 47 | public void writeInt(int i) { 48 | byteBuffer.putInt(i); 49 | } 50 | 51 | @Override 52 | public void writeLong(long l) { 53 | byteBuffer.putLong(l); 54 | } 55 | 56 | @Override 57 | public void writeFloat(float f) { 58 | byteBuffer.putFloat(f); 59 | } 60 | 61 | @Override 62 | public void writeDouble(double d) { 63 | byteBuffer.putDouble(d); 64 | } 65 | 66 | @Override 67 | public void writeLongOfSize(long l, IdSizes.SizeType sizeType) { 68 | IdSizes idSizes = vmInformation.getIdSizes(); 69 | int size; 70 | if (idSizes == null) { 71 | log.warn("Writing value without knowing its size in bytes. Assuming size is 8 bytes."); 72 | size = 8; 73 | } 74 | else { 75 | size = idSizes.getSizeOfType(sizeType); 76 | } 77 | 78 | ByteBufferUtil.putLong(byteBuffer, l, size); 79 | } 80 | 81 | @Override 82 | public void writeString(String string) { 83 | ByteBufferUtil.putString(byteBuffer, string); 84 | } 85 | 86 | @Override 87 | public void writeType(DataType type) { 88 | type.write(this); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/DebuggerPacketTransformer.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ConcurrentMap; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import kaap.veiko.debuggerforker.packet.CommandPacket; 11 | import kaap.veiko.debuggerforker.packet.Packet; 12 | import kaap.veiko.debuggerforker.packet.PacketVisitor; 13 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 14 | 15 | public class DebuggerPacketTransformer implements PacketTransformer { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(DebuggerPacketTransformer.class); 18 | 19 | private final ConcurrentMap idMap = new ConcurrentHashMap<>(); 20 | private final AtomicInteger idCounter = new AtomicInteger(0); 21 | 22 | private final OutputPacketVisitor outputPacketVisitor = new OutputPacketVisitor(); 23 | private final InputPacketVisitor inputPacketVisitor = new InputPacketVisitor(); 24 | 25 | @Override 26 | public Packet transformReadPacket(Packet packet) { 27 | return packet.visit(outputPacketVisitor); 28 | } 29 | 30 | @Override 31 | public Packet transformWritePacket(Packet packet) { 32 | return packet.visit(inputPacketVisitor); 33 | } 34 | 35 | @Override 36 | public int createNewId() { 37 | return idCounter.getAndIncrement(); 38 | } 39 | 40 | private int getNewId(int originalId) { 41 | int newId = idCounter.getAndIncrement(); 42 | idMap.put(newId, originalId); 43 | return newId; 44 | } 45 | 46 | private int getOriginalId(int newId) { 47 | return idMap.get(newId); 48 | } 49 | 50 | private class OutputPacketVisitor implements PacketVisitor { 51 | @Override 52 | public Packet visit(ReplyPacket packet) { 53 | log.error("Debugger shouldn't output reply packets"); 54 | return packet; 55 | } 56 | 57 | @Override 58 | public Packet visit(CommandPacket packet) { 59 | // Generate new id to ensure every packet reaching vm has a unique id 60 | // This also makes it easy to later connect reply and command packets and avoid collision 61 | int newId = getNewId(packet.getId()); 62 | return new CommandPacket( 63 | newId, 64 | packet.getCommandSetId(), 65 | packet.getCommandId(), 66 | packet.getData(), 67 | packet.getSource() 68 | ); 69 | } 70 | } 71 | 72 | private class InputPacketVisitor implements PacketVisitor { 73 | @Override 74 | public Packet visit(ReplyPacket packet) { 75 | // Restore original id 76 | int originalId = getOriginalId(packet.getId()); 77 | return new ReplyPacket( 78 | originalId, 79 | packet.getErrorCode(), 80 | packet.getData(), 81 | packet.getSource() 82 | ); 83 | } 84 | 85 | @Override 86 | public Packet visit(CommandPacket packet) { 87 | // id of this packet should already be changed by the vm packet transformer 88 | return packet; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/PacketBase.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | import kaap.veiko.debuggerforker.packet.Packet; 7 | import kaap.veiko.debuggerforker.packet.PacketSource; 8 | import kaap.veiko.debuggerforker.packet.PacketStream; 9 | 10 | public abstract class PacketBase implements Packet { 11 | 12 | private final int id; 13 | private final byte[] data; 14 | private final PacketSource source; 15 | 16 | public PacketBase(int id, byte[] data, PacketSource source) { 17 | this.id = id; 18 | this.data = data; 19 | this.source = source; 20 | } 21 | 22 | @Override 23 | public int getLength() { 24 | return Packet.HEADER_LENGTH + getData().length; 25 | } 26 | 27 | @Override 28 | public int getId() { 29 | return id; 30 | } 31 | 32 | @Override 33 | public short getFlags() { 34 | return (short) (isReply() ? -128 : 0); 35 | } 36 | 37 | @Override 38 | public byte[] getData() { 39 | return data; 40 | } 41 | 42 | @Override 43 | public PacketSource getSource() { 44 | return source; 45 | } 46 | 47 | @Override 48 | public boolean isSynthetic() { 49 | return false; 50 | } 51 | 52 | @Override 53 | public boolean equals(Object o) { 54 | if (this == o) { 55 | return true; 56 | } 57 | if (!(o instanceof PacketBase)) { 58 | return false; 59 | } 60 | PacketBase that = (PacketBase) o; 61 | return getId() == that.getId() && 62 | isReply() == that.isReply() && 63 | Arrays.equals(getData(), that.getData()) && 64 | Objects.equals(getSource(), that.getSource()); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | int result = Objects.hash(getId(), isReply(), getSource()); 70 | result = 31 * result + Arrays.hashCode(getData()); 71 | return result; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/PacketParser.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.function.Function; 5 | 6 | import kaap.veiko.debuggerforker.packet.CommandPacket; 7 | import kaap.veiko.debuggerforker.packet.Packet; 8 | import kaap.veiko.debuggerforker.packet.PacketSource; 9 | import kaap.veiko.debuggerforker.packet.PacketStream; 10 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 11 | 12 | class PacketParser { 13 | 14 | private final int length; 15 | private final byte[] packetBytes; 16 | private int index = 0; 17 | 18 | private PacketParser(int length, byte[] packetBytes) { 19 | this.length = length; 20 | this.packetBytes = packetBytes; 21 | } 22 | 23 | static Packet parse(int length, byte[] packetBytes, PacketSource source) { 24 | return new PacketParser(length, packetBytes).internalParse(source); 25 | } 26 | 27 | private Packet internalParse(PacketSource source) { 28 | int id = readInt(); 29 | short flags = readByte(); 30 | 31 | if (flags == -128) { 32 | short errorCode = readShort(); 33 | byte[] data = readData(); 34 | return new ReplyPacket(id, errorCode, data, source); 35 | } 36 | else { 37 | short commandSetId = readByte(); 38 | short commandId = readByte(); 39 | byte[] data = readData(); 40 | 41 | return new CommandPacket(id, commandSetId, commandId, data, source); 42 | } 43 | } 44 | 45 | private int readInt() { 46 | return readBytesAndApply(4, ByteBuffer::getInt); 47 | } 48 | 49 | private short readShort() { 50 | return readBytesAndApply(2, ByteBuffer::getShort); 51 | } 52 | 53 | private short readByte() { 54 | return (short) readBytesAndApply(1, ByteBuffer::get); 55 | } 56 | 57 | private byte[] readData() { 58 | if (length > Packet.HEADER_LENGTH) { 59 | return readBytesAndApply(length - Packet.HEADER_LENGTH, bf -> { 60 | byte[] data = new byte[length - Packet.HEADER_LENGTH]; 61 | bf.get(data); 62 | return data; 63 | }); 64 | } else { 65 | return new byte[0]; 66 | } 67 | } 68 | 69 | private T readBytesAndApply(int nrOfBytes, Function function) { 70 | ByteBuffer buf = ByteBuffer.wrap(packetBytes, index, nrOfBytes); 71 | index += nrOfBytes; 72 | return function.apply(buf); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/PacketReader.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.SocketChannel; 6 | 7 | import kaap.veiko.debuggerforker.packet.Packet; 8 | import kaap.veiko.debuggerforker.packet.PacketSource; 9 | import kaap.veiko.debuggerforker.packet.PacketStream; 10 | 11 | class PacketReader { 12 | 13 | private final SocketChannel socketChannel; 14 | private final PacketSource source; 15 | 16 | private boolean readingLength = true; 17 | private boolean readingData = false; 18 | private boolean doneReading = false; 19 | private int length; 20 | private ByteBuffer lengthBuffer = ByteBuffer.allocate(4); 21 | private ByteBuffer dataBuffer = null; 22 | 23 | PacketReader(SocketChannel socketChannel, PacketSource source) { 24 | this.socketChannel = socketChannel; 25 | this.source = source; 26 | } 27 | 28 | public Packet read() throws IOException { 29 | if (readingLength) { 30 | readLength(); 31 | } 32 | if (readingData) { 33 | readData(); 34 | } 35 | 36 | if (doneReading) { 37 | doneReading = false; 38 | readingLength = true; 39 | dataBuffer.flip(); 40 | byte[] array; 41 | if (dataBuffer.hasArray()) { 42 | array = dataBuffer.array(); 43 | } else { 44 | array = new byte[length]; 45 | dataBuffer.get(array); 46 | } 47 | 48 | return PacketParser.parse(length, array, source); 49 | } 50 | return null; 51 | } 52 | 53 | private void readLength() throws IOException { 54 | while (lengthBuffer.hasRemaining()) { 55 | int read = socketChannel.read(lengthBuffer); 56 | if (read == 0) { 57 | break; 58 | } 59 | } 60 | 61 | if (!lengthBuffer.hasRemaining()) { 62 | lengthBuffer.flip(); 63 | length = lengthBuffer.getInt(); 64 | dataBuffer = ByteBuffer.allocate(length - 4); 65 | lengthBuffer.clear(); 66 | readingLength = false; 67 | readingData = true; 68 | } 69 | } 70 | 71 | private void readData() throws IOException { 72 | while (dataBuffer.hasRemaining()) { 73 | int read = socketChannel.read(dataBuffer); 74 | if (read == 0) { 75 | break; 76 | } 77 | } 78 | 79 | if (!dataBuffer.hasRemaining()) { 80 | readingData = false; 81 | doneReading = true; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/PacketTransformer.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import kaap.veiko.debuggerforker.packet.Packet; 4 | 5 | public interface PacketTransformer { 6 | Packet transformReadPacket(Packet packet); 7 | 8 | Packet transformWritePacket(Packet packet); 9 | 10 | int createNewId(); 11 | } 12 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/PacketWriter.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.SocketChannel; 6 | 7 | import kaap.veiko.debuggerforker.packet.CommandPacket; 8 | import kaap.veiko.debuggerforker.packet.Packet; 9 | import kaap.veiko.debuggerforker.packet.PacketVisitor; 10 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 11 | 12 | class PacketWriter { 13 | 14 | private final SocketChannel socketChannel; 15 | private final PacketByteBufferConverter packetByteBufferConverter = new PacketByteBufferConverter(); 16 | 17 | PacketWriter(SocketChannel socketChannel) { 18 | this.socketChannel = socketChannel; 19 | } 20 | 21 | void write(Packet packet) throws IOException { 22 | ByteBuffer buffer = packet.visit(packetByteBufferConverter); 23 | while (buffer.hasRemaining()) { 24 | socketChannel.write(buffer); 25 | } 26 | } 27 | 28 | private class PacketByteBufferConverter implements PacketVisitor { 29 | 30 | private void writeHeader(Packet packet, ByteBuffer byteBuffer) { 31 | byteBuffer.putInt(packet.getLength()); 32 | byteBuffer.putInt(packet.getId()); 33 | byteBuffer.put((byte) packet.getFlags()); 34 | } 35 | 36 | private void writeData(Packet packet, ByteBuffer byteBuffer) { 37 | if (packet.hasData()) { 38 | byteBuffer.put(packet.getData()); 39 | } 40 | } 41 | 42 | @Override 43 | public ByteBuffer visit(ReplyPacket packet) { 44 | ByteBuffer byteBuffer = ByteBuffer.allocate(packet.getLength()); 45 | 46 | writeHeader(packet, byteBuffer); 47 | byteBuffer.putShort(packet.getErrorCode()); 48 | 49 | writeData(packet, byteBuffer); 50 | 51 | byteBuffer.flip(); 52 | return byteBuffer; 53 | } 54 | 55 | @Override 56 | public ByteBuffer visit(CommandPacket packet) { 57 | ByteBuffer byteBuffer = ByteBuffer.allocate(packet.getLength()); 58 | writeHeader(packet, byteBuffer); 59 | 60 | byteBuffer.put((byte) packet.getCommandSetId()); 61 | byteBuffer.put((byte) packet.getCommandId()); 62 | 63 | writeData(packet, byteBuffer); 64 | 65 | byteBuffer.flip(); 66 | return byteBuffer; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/internal/VmPacketTransformer.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.internal; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ConcurrentMap; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import kaap.veiko.debuggerforker.packet.CommandPacket; 10 | import kaap.veiko.debuggerforker.packet.Packet; 11 | import kaap.veiko.debuggerforker.packet.PacketVisitor; 12 | import kaap.veiko.debuggerforker.packet.ReplyPacket; 13 | 14 | public class VmPacketTransformer implements PacketTransformer { 15 | 16 | private static final Logger log = LoggerFactory.getLogger(VmPacketTransformer.class); 17 | 18 | private final ConcurrentMap idMap = new ConcurrentHashMap<>(); 19 | private final AtomicInteger idCounter = new AtomicInteger(0); 20 | 21 | private final OutputPacketVisitor outputPacketVisitor = new OutputPacketVisitor(); 22 | private final InputPacketVisitor inputPacketVisitor = new InputPacketVisitor(); 23 | 24 | @Override 25 | public Packet transformReadPacket(Packet packet) { 26 | return packet.visit(outputPacketVisitor); 27 | } 28 | 29 | @Override 30 | public Packet transformWritePacket(Packet packet) { 31 | return packet.visit(inputPacketVisitor); 32 | } 33 | 34 | @Override 35 | public int createNewId() { 36 | return idCounter.getAndIncrement(); 37 | } 38 | 39 | private int getNewId(int originalId) { 40 | int newId = idCounter.getAndIncrement(); 41 | idMap.put(newId, originalId); 42 | return newId; 43 | } 44 | 45 | private int getOriginalId(int newId) { 46 | return idMap.get(newId); 47 | } 48 | 49 | private class OutputPacketVisitor implements PacketVisitor { 50 | @Override 51 | public Packet visit(ReplyPacket packet) { 52 | // Changing this id is the task of DebuggerPacketTransformer 53 | return packet; 54 | } 55 | 56 | @Override 57 | public Packet visit(CommandPacket packet) { 58 | // Generate new id to allow creating artificial packets later without id collision 59 | int newId = getNewId(packet.getId()); 60 | return new CommandPacket( 61 | newId, 62 | packet.getCommandSetId(), 63 | packet.getCommandId(), 64 | packet.getData(), 65 | packet.getSource() 66 | ); 67 | } 68 | } 69 | 70 | private class InputPacketVisitor implements PacketVisitor { 71 | @Override 72 | public Packet visit(ReplyPacket packet) { 73 | log.error("Virtual Machine shouldn't receive replies"); 74 | return packet; 75 | } 76 | 77 | @Override 78 | public Packet visit(CommandPacket packet) { 79 | // Changing this id is the task of DebuggerPacketTransformer 80 | return packet; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packet/src/main/java/kaap/veiko/debuggerforker/packet/utils/ByteBufferUtil.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.packet.utils; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.charset.StandardCharsets; 5 | 6 | public class ByteBufferUtil { 7 | public static Long getLong(ByteBuffer buffer, int size) { 8 | if (size > 8 || size < 0) { 9 | throw new UnsupportedOperationException("malformed input"); 10 | } 11 | int offset = 8 - size; 12 | byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0}; 13 | buffer.get(bytes, offset, size); 14 | return ByteBuffer.wrap(bytes).getLong(); 15 | } 16 | 17 | public static void putLong(ByteBuffer buffer, long value, int size) { 18 | ByteBuffer tmpBuffer = ByteBuffer.allocate(8); 19 | tmpBuffer.putLong(value); 20 | tmpBuffer.flip(); 21 | 22 | byte[] bytes = new byte[size]; 23 | for (int i = 0; i < (8 - size); i++) { 24 | tmpBuffer.get(); 25 | } 26 | tmpBuffer.get(bytes, 0, size); 27 | 28 | buffer.put(bytes); 29 | } 30 | 31 | public static Integer getInt(ByteBuffer buffer, int size) { 32 | if (size > 4 || size < 0) { 33 | throw new UnsupportedOperationException("malformed input"); 34 | } 35 | int offset = 4 - size; 36 | byte[] bytes = new byte[]{0, 0, 0, 0}; 37 | buffer.get(bytes, offset, size); 38 | return ByteBuffer.wrap(bytes).getInt(); 39 | } 40 | 41 | public static Short getShort(ByteBuffer buffer, int size) { 42 | if (size > 2 || size < 0) { 43 | throw new UnsupportedOperationException("malformed input"); 44 | } 45 | int offset = 2 - size; 46 | byte[] bytes = new byte[]{0, 0}; 47 | buffer.get(bytes, offset, size); 48 | return ByteBuffer.wrap(bytes).getShort(); 49 | } 50 | 51 | public static String getString(ByteBuffer buffer) { 52 | int length = buffer.getInt(); 53 | byte[] bytes = new byte[length]; 54 | buffer.get(bytes, 0, length); 55 | 56 | return new String(bytes, StandardCharsets.UTF_8); 57 | } 58 | 59 | public static void putString(ByteBuffer buffer, String string) { 60 | byte[] bytes = string.getBytes(StandardCharsets.UTF_8); 61 | 62 | buffer.putInt(bytes.length); 63 | buffer.put(bytes); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /server-logic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jvm-debugger-forker 7 | kaap.veiko 8 | 0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | server-logic 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-shade-plugin 19 | 3.1.0 20 | 21 | 22 | package 23 | 24 | shade 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-jar-plugin 32 | 3.0.2 33 | 34 | 35 | 36 | kaap.veiko.debuggerforker.Main 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | kaap.veiko 47 | commands 48 | ${parent.version} 49 | 50 | 51 | kaap.veiko 52 | packet 53 | ${parent.version} 54 | 55 | 56 | kaap.veiko 57 | connections 58 | ${parent.version} 59 | 60 | 61 | kaap.veiko 62 | types 63 | ${parent.version} 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /server-logic/src/main/java/kaap/veiko/debuggerforker/DebuggerProxy.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import kaap.veiko.debuggerforker.commands.Command; 10 | import kaap.veiko.debuggerforker.handlers.CommandHandler; 11 | 12 | public class DebuggerProxy { 13 | 14 | private final static Logger log = LoggerFactory.getLogger(DebuggerProxy.class); 15 | 16 | private final CommandHandler commandHandler; 17 | public final DebugProxyServer proxyServer; 18 | private AtomicBoolean debuggerConnected = new AtomicBoolean(false); 19 | private final AtomicBoolean vmConnected = new AtomicBoolean(false); 20 | 21 | public DebuggerProxy(InetSocketAddress vmAddress, int debuggerPort) throws IOException { 22 | proxyServer = new DebugProxyServer(vmAddress, debuggerPort); 23 | commandHandler = new CommandHandler(proxyServer.getVmInformation(), proxyServer.getProxyCommandStream()); 24 | } 25 | 26 | public void start() { 27 | proxyServer.addVirtualMachineConnectedListener(vmStream -> vmConnected.set(true)); 28 | proxyServer.addDebuggerConnectedListener(debuggerStream -> debuggerConnected.set(true)); 29 | 30 | proxyServer.start(); 31 | waitUntilConnected(); 32 | 33 | while (proxyServer.getProxyCommandStream().isOpen()) { 34 | Command command = proxyServer.getProxyCommandStream().read(); 35 | if (command != null) { 36 | command.visit(commandHandler); 37 | } 38 | } 39 | } 40 | 41 | private void waitUntilConnected() { 42 | while (!vmConnected.get() || !debuggerConnected.get()) { 43 | try { 44 | Thread.sleep(10); 45 | } 46 | catch (InterruptedException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | } 51 | 52 | public void stop() { 53 | proxyServer.close(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /server-logic/src/main/java/kaap/veiko/debuggerforker/Main.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public class Main { 10 | 11 | private static final String JVM_HOST_ARG = "jvm.host="; 12 | private static final String JVM_PORT_ARG = "jvm.port="; 13 | private static final String PROXY_PORT_ARG = "proxy.port="; 14 | 15 | public static void main(String[] args) throws IOException { 16 | List arguments = Arrays.asList(args); 17 | 18 | InetSocketAddress virtualMachineAddress = getJvmAddress(arguments); 19 | Integer proxyPort = getProxyPort(arguments); 20 | 21 | if (virtualMachineAddress == null || proxyPort == null) { 22 | return; 23 | } 24 | 25 | DebuggerProxy proxy = new DebuggerProxy(virtualMachineAddress, proxyPort); 26 | proxy.start(); 27 | } 28 | 29 | private static InetSocketAddress getJvmAddress(List args) { 30 | String host = args.stream() 31 | .filter(s -> s.startsWith(JVM_HOST_ARG)) 32 | .map(s -> s.substring(JVM_HOST_ARG.length())) 33 | .findFirst() 34 | .orElse("127.0.0.1"); 35 | 36 | Optional port = args.stream() 37 | .filter(s -> s.startsWith(JVM_PORT_ARG)) 38 | .map(s -> s.substring(JVM_PORT_ARG.length())) 39 | .findFirst(); 40 | 41 | if (port.isPresent()) { 42 | try { 43 | int jvmPort = Integer.parseInt(port.get()); 44 | return new InetSocketAddress(host, jvmPort); 45 | } 46 | catch (NumberFormatException e) { 47 | System.err.println("JVM debug port is not a valid number"); 48 | return null; 49 | } 50 | } 51 | else { 52 | System.err.println("No JVM debug port specified. Please add " + JVM_PORT_ARG + " argument."); 53 | return null; 54 | } 55 | } 56 | 57 | private static Integer getProxyPort(List args) { 58 | Optional port = args.stream() 59 | .filter(s -> s.startsWith(PROXY_PORT_ARG)) 60 | .map(s -> s.substring(PROXY_PORT_ARG.length())) 61 | .findFirst(); 62 | 63 | if (port.isPresent()) { 64 | try { 65 | return Integer.parseInt(port.get()); 66 | } 67 | catch (NumberFormatException e) { 68 | System.err.println("Proxy server port is not a valid number"); 69 | return null; 70 | } 71 | } 72 | else { 73 | System.err.println("No proxy server port specified. Please add -D" + PROXY_PORT_ARG + " JVM argument."); 74 | return null; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /server-logic/src/main/java/kaap/veiko/debuggerforker/ProxyCommandStream.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.Selector; 5 | import java.util.Deque; 6 | import java.util.List; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import kaap.veiko.debuggerforker.commands.Command; 12 | import kaap.veiko.debuggerforker.commands.CommandStream; 13 | import kaap.veiko.debuggerforker.managers.CommandStreamInputOutputManager; 14 | import kaap.veiko.debuggerforker.packet.PacketSource; 15 | 16 | public class ProxyCommandStream { 17 | 18 | private final Logger log = LoggerFactory.getLogger(ProxyCommandStream.class); 19 | 20 | private final CommandStreamInputOutputManager commandStreamManager; 21 | private final Thread managerThread; 22 | 23 | public static ProxyCommandStream create() throws IOException { 24 | return new ProxyCommandStream(Selector.open()); 25 | } 26 | 27 | private ProxyCommandStream(Selector selector) { 28 | commandStreamManager = new CommandStreamInputOutputManager(selector); 29 | this.managerThread = new Thread(commandStreamManager); 30 | } 31 | 32 | void registerCommandStream(CommandStream commandStream) throws Exception { 33 | commandStreamManager.addCommandStream(commandStream); 34 | } 35 | 36 | public void markForClosingAfterAllPacketsWritten(PacketSource source) { 37 | commandStreamManager.markForClosing(source); 38 | } 39 | 40 | public Command read() { 41 | Command readCommand = commandStreamManager.getReadQueue().pollFirst(); 42 | if (readCommand != null) { 43 | log.info("Command from readQueue: {}", readCommand); 44 | } 45 | return readCommand; 46 | } 47 | 48 | public void writeToVm(Command command) { 49 | PacketSource vmSource = commandStreamManager.getVmSource(); 50 | if (vmSource != null) { 51 | write(vmSource, command); 52 | } 53 | } 54 | 55 | public void writeToAllDebuggers(Command command) { 56 | commandStreamManager.getAllDebuggers().forEach(s -> write(s, command)); 57 | } 58 | 59 | public void write(PacketSource source, Command command) { 60 | Deque writeQueue = commandStreamManager.getWriteQueue(source); 61 | if (writeQueue != null) { 62 | log.info("Adding command '{}' to writeQueue for source {}", command, source); 63 | writeQueue.addLast(command); 64 | } else { 65 | log.warn("Trying to write to a packet source which isn't registered: {}", source); 66 | } 67 | } 68 | 69 | public PacketSource getVmSource() { 70 | return commandStreamManager.getVmSource(); 71 | } 72 | 73 | public List getAllDebuggers() { 74 | return commandStreamManager.getAllDebuggers(); 75 | } 76 | 77 | void start() { 78 | managerThread.start(); 79 | } 80 | 81 | boolean isOpen() { 82 | return commandStreamManager.isOpen(); 83 | } 84 | 85 | void close() { 86 | commandStreamManager.close(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /server-logic/src/main/java/kaap/veiko/debuggerforker/handlers/RequestIdentifier.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.handlers; 2 | 3 | import kaap.veiko.debuggerforker.types.jdwp.EventKind; 4 | 5 | final public class RequestIdentifier { 6 | private final EventKind eventKind; 7 | private final int requestId; 8 | 9 | public RequestIdentifier(EventKind eventKind, int requestId) { 10 | this.eventKind = eventKind; 11 | this.requestId = requestId; 12 | } 13 | 14 | public EventKind getEventKind() { 15 | return eventKind; 16 | } 17 | 18 | public int getRequestId() { 19 | return requestId; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) { 25 | return true; 26 | } 27 | if (o == null || getClass() != o.getClass()) { 28 | return false; 29 | } 30 | 31 | RequestIdentifier requestIdentifier = (RequestIdentifier) o; 32 | 33 | if (requestId != requestIdentifier.requestId) { 34 | return false; 35 | } 36 | return eventKind == requestIdentifier.eventKind; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | int result = eventKind != null ? eventKind.hashCode() : 0; 42 | result = 31 * result + requestId; 43 | return result; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "RequestIdentifier{" + 49 | "eventKind=" + eventKind + 50 | ", requestId=" + requestId + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /server-logic/src/main/java/kaap/veiko/debuggerforker/managers/ChannelInputOutputManager.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.managers; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.ClosedChannelException; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.Selector; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | abstract class ChannelInputOutputManager implements Runnable, AutoCloseable { 13 | private final Logger log = LoggerFactory.getLogger(ChannelInputOutputManager.class); 14 | 15 | private final Object selectLock = new Object(); 16 | private final Object registerLock = new Object(); 17 | private final Selector selector; 18 | 19 | private final CountDownLatch startLatch = new CountDownLatch(1); 20 | 21 | private final AtomicBoolean open = new AtomicBoolean(true); 22 | 23 | ChannelInputOutputManager(Selector selector) { 24 | this.selector = selector; 25 | } 26 | 27 | abstract void internalRegister(T object, Selector selector) throws ClosedChannelException; 28 | 29 | abstract void handleKey(SelectionKey key); 30 | 31 | void register(T object) throws Exception { 32 | if (!open.get()) { 33 | throw new IOException("Can't register object to closed runnable."); 34 | } 35 | 36 | try { 37 | synchronizedRegister(object); 38 | } 39 | catch (Exception registerException) { 40 | try { 41 | object.close(); 42 | } 43 | catch (Exception closeException) { 44 | registerException.addSuppressed(closeException); 45 | throw registerException; 46 | } 47 | } 48 | } 49 | 50 | private void synchronizedRegister(T object) throws ClosedChannelException { 51 | synchronized (registerLock) { 52 | selector.wakeup(); 53 | synchronized (selectLock) { // Wait until select has woken. 54 | internalRegister(object, selector); 55 | log.info("Registered new channel: {}", object); 56 | } 57 | } 58 | } 59 | 60 | @Override 61 | public void run() { 62 | try { 63 | while (isOpen() && !Thread.currentThread().isInterrupted()) { 64 | int selected = synchronizedSelect(); 65 | if (selected == 0) { 66 | continue; 67 | } 68 | selector.selectedKeys().forEach(this::handleKey); 69 | selector.selectedKeys().clear(); 70 | } 71 | } 72 | catch (Exception e) { 73 | close(); 74 | log.error("Stopping {} due to exception.", this, e); 75 | } 76 | } 77 | 78 | private int synchronizedSelect() throws IOException { 79 | int selected; 80 | synchronized (selectLock) { 81 | selected = selector.select(); 82 | } 83 | synchronized (registerLock) { 84 | } // Wait until new channel is registered 85 | 86 | return selected; 87 | } 88 | 89 | public boolean isOpen() { 90 | return open.get(); 91 | } 92 | 93 | public void close() { 94 | open.set(false); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /server-logic/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /types/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jvm-debugger-forker 7 | kaap.veiko 8 | 0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | types 13 | 14 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/DataReader.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types; 2 | 3 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 4 | 5 | public interface DataReader { 6 | byte readByte(); 7 | 8 | short readShort(); 9 | 10 | int readInt(); 11 | 12 | long readLong(); 13 | 14 | long readLongOfSize(IdSizes.SizeType sizeType); 15 | 16 | boolean readBoolean(); 17 | 18 | float readFloat(); 19 | 20 | double readDouble(); 21 | 22 | String readString(); 23 | } 24 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/DataType.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types; 2 | 3 | public interface DataType { 4 | void write(DataWriter writer); 5 | } 6 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/DataWriter.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types; 2 | 3 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 4 | 5 | public interface DataWriter { 6 | void writeBoolean(boolean bool); 7 | 8 | void writeByte(byte b); 9 | 10 | void writeShort(short s); 11 | 12 | void writeInt(int i); 13 | 14 | void writeLong(long l); 15 | 16 | void writeFloat(float f); 17 | 18 | void writeDouble(double d); 19 | 20 | void writeLongOfSize(long l, IdSizes.SizeType sizeType); 21 | 22 | void writeString(String string); 23 | 24 | void writeType(DataType type); 25 | } 26 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/VMInformation.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types; 2 | 3 | import kaap.veiko.debuggerforker.types.jdwp.IdSizes; 4 | 5 | public class VMInformation { 6 | private IdSizes idSizes; 7 | 8 | public IdSizes getIdSizes() { 9 | return idSizes; 10 | } 11 | 12 | public void setIdSizes(IdSizes idSizes) { 13 | this.idSizes = idSizes; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ArrayId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ArrayId extends ObjectId { 6 | public static ArrayId read(DataReader reader) { 7 | return new ArrayId(reader); 8 | } 9 | 10 | ArrayId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ArrayId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ArrayTypeId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ArrayTypeId extends ReferenceTypeId { 6 | public static ArrayTypeId read(DataReader reader) { 7 | return new ArrayTypeId(reader); 8 | } 9 | 10 | ArrayTypeId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ArrayTypeId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ClassId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ClassId extends ReferenceTypeId { 6 | public static ClassId read(DataReader reader) { 7 | return new ClassId(reader); 8 | } 9 | 10 | ClassId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ClassId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ClassLoaderId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ClassLoaderId extends ObjectId { 6 | public static ClassLoaderId read(DataReader reader) { 7 | return new ClassLoaderId(reader); 8 | } 9 | 10 | ClassLoaderId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ClassLoaderId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ClassObjectId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ClassObjectId extends ObjectId { 6 | public static ClassObjectId read(DataReader reader) { 7 | return new ClassObjectId(reader); 8 | } 9 | 10 | ClassObjectId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ClassObjectId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/DataTypeBase.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import kaap.veiko.debuggerforker.types.DataReader; 7 | import kaap.veiko.debuggerforker.types.DataType; 8 | import kaap.veiko.debuggerforker.types.DataWriter; 9 | 10 | abstract class DataTypeBase implements DataType { 11 | 12 | private final static Logger log = LoggerFactory.getLogger(DataTypeBase.class); 13 | private final long value; 14 | private final IdSizes.SizeType sizeType; 15 | 16 | DataTypeBase(DataReader reader, IdSizes.SizeType sizeType) { 17 | this.sizeType = sizeType; 18 | this.value = reader.readLongOfSize(sizeType); 19 | } 20 | 21 | DataTypeBase(long value, IdSizes.SizeType sizeType) { 22 | this.value = value; 23 | this.sizeType = sizeType; 24 | } 25 | 26 | @Override 27 | public void write(DataWriter writer) { 28 | writer.writeLongOfSize(value, sizeType); 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if (this == o) { 34 | return true; 35 | } 36 | if (o == null || getClass() != o.getClass()) { 37 | return false; 38 | } 39 | 40 | DataTypeBase that = (DataTypeBase) o; 41 | 42 | return value == that.value; 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return (int) (value ^ (value >>> 32)); 48 | } 49 | 50 | public long asLong() { 51 | return value; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return this.getClass().getSimpleName() + "{" + 57 | "value=" + value + 58 | '}'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/EventKind.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import java.util.Arrays; 4 | 5 | import kaap.veiko.debuggerforker.types.DataReader; 6 | import kaap.veiko.debuggerforker.types.DataType; 7 | import kaap.veiko.debuggerforker.types.DataWriter; 8 | 9 | public enum EventKind implements DataType { 10 | SINGLE_STEP(1), 11 | BREAKPOINT(2), 12 | FRAME_POP(3), 13 | EXCEPTION(4), 14 | USER_DEFINED(5), 15 | THREAD_START(6), 16 | THREAD_DEATH(7), 17 | CLASS_PREPARE(8), 18 | CLASS_UNLOAD(9), 19 | CLASS_LOAD(10), 20 | FIELD_ACCESS(20), 21 | FIELD_MODIFICATION(21), 22 | EXCEPTION_CATCH(30), 23 | METHOD_ENTRY(40), 24 | METHOD_EXIT(41), 25 | METHOD_EXIT_WITH_RETURN_VALUE(42), 26 | MONITOR_CONTENDED_ENTER(43), 27 | MONITOR_CONTENDED_ENTERED(44), 28 | MONITOR_WAIT(45), 29 | MONITOR_WAITED(46), 30 | VM_START(90), 31 | VM_DEATH(99), 32 | VM_DISCONNECTED(100); 33 | 34 | public final byte id; 35 | 36 | EventKind(int id) { 37 | this.id = (byte) id; 38 | } 39 | 40 | public static EventKind read(DataReader reader) { 41 | return findByValue(reader.readByte()); 42 | } 43 | 44 | private static EventKind findByValue(byte value) { 45 | return Arrays.stream(EventKind.values()) 46 | .filter(eventKind -> eventKind.id == value) 47 | .findFirst().get(); 48 | } 49 | 50 | public final byte getId() { 51 | return id; 52 | } 53 | 54 | @Override 55 | public void write(DataWriter writer) { 56 | writer.writeByte(id); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/FieldId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class FieldId extends DataTypeBase { 6 | public static FieldId read(DataReader reader) { 7 | return new FieldId(reader); 8 | } 9 | 10 | FieldId(DataReader reader) { 11 | super(reader, IdSizes.SizeType.FIELD_ID_SIZE); 12 | } 13 | 14 | public FieldId(long value) { 15 | super(value, IdSizes.SizeType.FIELD_ID_SIZE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/FrameId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class FrameId extends DataTypeBase { 6 | public static FrameId read(DataReader reader) { 7 | return new FrameId(reader); 8 | } 9 | 10 | FrameId(DataReader reader) { 11 | super(reader, IdSizes.SizeType.FRAME_ID_SIZE); 12 | } 13 | 14 | public FrameId(long value) { 15 | super(value, IdSizes.SizeType.FRAME_ID_SIZE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/InterfaceId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class InterfaceId extends ReferenceTypeId { 6 | public static InterfaceId read(DataReader reader) { 7 | return new InterfaceId(reader); 8 | } 9 | 10 | InterfaceId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public InterfaceId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/Location.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | import kaap.veiko.debuggerforker.types.DataType; 5 | import kaap.veiko.debuggerforker.types.DataWriter; 6 | 7 | public class Location implements DataType { 8 | private final byte typeTag; 9 | private final ClassId classId; 10 | private final MethodId methodId; 11 | private final long index; 12 | 13 | public static Location read(DataReader reader) { 14 | return new Location(reader); 15 | } 16 | 17 | Location(DataReader reader) { 18 | typeTag = reader.readByte(); 19 | classId = ClassId.read(reader); 20 | methodId = MethodId.read(reader); 21 | index = reader.readLong(); 22 | } 23 | 24 | public Location(byte typeTag, ClassId classId, MethodId methodId, long index) { 25 | this.typeTag = typeTag; 26 | this.classId = classId; 27 | this.methodId = methodId; 28 | this.index = index; 29 | } 30 | 31 | public byte getTypeTag() { 32 | return typeTag; 33 | } 34 | 35 | public ClassId getClassId() { 36 | return classId; 37 | } 38 | 39 | public MethodId getMethodId() { 40 | return methodId; 41 | } 42 | 43 | public long getIndex() { 44 | return index; 45 | } 46 | 47 | @Override 48 | public void write(DataWriter writer) { 49 | writer.writeByte(typeTag); 50 | writer.writeType(classId); 51 | writer.writeType(methodId); 52 | writer.writeLong(index); 53 | } 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) { 58 | return true; 59 | } 60 | if (o == null || getClass() != o.getClass()) { 61 | return false; 62 | } 63 | 64 | Location location = (Location) o; 65 | 66 | if (typeTag != location.typeTag) { 67 | return false; 68 | } 69 | if (index != location.index) { 70 | return false; 71 | } 72 | if (classId != null ? !classId.equals(location.classId) : location.classId != null) { 73 | return false; 74 | } 75 | return methodId != null ? methodId.equals(location.methodId) : location.methodId == null; 76 | } 77 | 78 | @Override 79 | public int hashCode() { 80 | int result = (int) typeTag; 81 | result = 31 * result + (classId != null ? classId.hashCode() : 0); 82 | result = 31 * result + (methodId != null ? methodId.hashCode() : 0); 83 | result = 31 * result + (int) (index ^ (index >>> 32)); 84 | return result; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return "Location{" + 90 | "typeTag=" + typeTag + 91 | ", classId=" + classId + 92 | ", methodId=" + methodId + 93 | ", index=" + index + 94 | '}'; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/MethodId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class MethodId extends DataTypeBase { 6 | public static MethodId read(DataReader reader) { 7 | return new MethodId(reader); 8 | } 9 | 10 | MethodId(DataReader reader) { 11 | super(reader, IdSizes.SizeType.METHOD_ID_SIZE); 12 | } 13 | 14 | public MethodId(long value) { 15 | super(value, IdSizes.SizeType.METHOD_ID_SIZE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ObjectId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ObjectId extends DataTypeBase { 6 | public static ObjectId read(DataReader reader) { 7 | return new ObjectId(reader); 8 | } 9 | 10 | ObjectId(DataReader reader) { 11 | super(reader, IdSizes.SizeType.OBJECT_ID_SIZE); 12 | } 13 | 14 | public ObjectId(long value) { 15 | super(value, IdSizes.SizeType.OBJECT_ID_SIZE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ReferenceTypeId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ReferenceTypeId extends ObjectId { 6 | public static ReferenceTypeId read(DataReader reader) { 7 | return new ReferenceTypeId(reader); 8 | } 9 | 10 | ReferenceTypeId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ReferenceTypeId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/StringId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class StringId extends ObjectId { 6 | public static StringId read(DataReader reader) { 7 | return new StringId(reader); 8 | } 9 | 10 | StringId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public StringId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/TaggedObjectId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | import kaap.veiko.debuggerforker.types.DataType; 5 | import kaap.veiko.debuggerforker.types.DataWriter; 6 | 7 | public class TaggedObjectId implements DataType { 8 | 9 | private final byte tag; 10 | private final ObjectId objectId; 11 | 12 | public static TaggedObjectId read(DataReader reader) { 13 | return new TaggedObjectId(reader); 14 | } 15 | 16 | TaggedObjectId(DataReader reader) { 17 | tag = reader.readByte(); 18 | objectId = new ObjectId(reader); 19 | } 20 | 21 | public TaggedObjectId(byte tag, ObjectId objectId) { 22 | this.tag = tag; 23 | this.objectId = objectId; 24 | } 25 | 26 | public byte getTag() { 27 | return tag; 28 | } 29 | 30 | public ObjectId getObjectId() { 31 | return objectId; 32 | } 33 | 34 | public long asLong() { 35 | return getObjectId().asLong(); 36 | } 37 | 38 | @Override 39 | public void write(DataWriter writer) { 40 | writer.writeByte(tag); 41 | writer.writeType(objectId); 42 | } 43 | 44 | @Override 45 | public boolean equals(Object o) { 46 | if (this == o) { 47 | return true; 48 | } 49 | if (o == null || getClass() != o.getClass()) { 50 | return false; 51 | } 52 | 53 | TaggedObjectId that = (TaggedObjectId) o; 54 | 55 | if (tag != that.tag) { 56 | return false; 57 | } 58 | return objectId != null ? objectId.equals(that.objectId) : that.objectId == null; 59 | } 60 | 61 | @Override 62 | public int hashCode() { 63 | int result = (int) tag; 64 | result = 31 * result + (objectId != null ? objectId.hashCode() : 0); 65 | return result; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "TaggedObjectId{" + 71 | "tag=" + tag + 72 | ", objectId=" + asLong() + 73 | '}'; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ThreadGroupId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ThreadGroupId extends ObjectId { 6 | public static ThreadGroupId read(DataReader reader) { 7 | return new ThreadGroupId(reader); 8 | } 9 | 10 | ThreadGroupId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ThreadGroupId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/ThreadId.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class ThreadId extends ObjectId { 6 | public static ThreadId read(DataReader reader) { 7 | return new ThreadId(reader); 8 | } 9 | 10 | ThreadId(DataReader reader) { 11 | super(reader); 12 | } 13 | 14 | public ThreadId(long value) { 15 | super(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/Type.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import java.util.Arrays; 4 | import java.util.function.BiConsumer; 5 | import java.util.function.Function; 6 | 7 | import kaap.veiko.debuggerforker.types.DataReader; 8 | import kaap.veiko.debuggerforker.types.DataWriter; 9 | 10 | public enum Type { 11 | ARRAY(91, ObjectId::read, DataWriter::writeType), 12 | BYTE(66, DataReader::readByte, DataWriter::writeByte), 13 | CHAR(67, DataReader::readShort, DataWriter::writeShort), 14 | OBJECT(76, ObjectId::read, DataWriter::writeType), 15 | FLOAT(70, DataReader::readFloat, DataWriter::writeFloat), 16 | DOUBLE(68, DataReader::readDouble, DataWriter::writeDouble), 17 | INT(73, DataReader::readInt, DataWriter::writeInt), 18 | LONG(74, DataReader::readLong, DataWriter::writeLong), 19 | SHORT(83, DataReader::readShort, DataWriter::writeShort), 20 | VOID(86, reader -> null, (writer, o) -> {}), 21 | BOOLEAN(90, DataReader::readBoolean, DataWriter::writeBoolean), 22 | STRING(115, ObjectId::read, DataWriter::writeType), 23 | THREAD(116, ObjectId::read, DataWriter::writeType), 24 | THREAD_GROUP(103, ObjectId::read, DataWriter::writeType), 25 | CLASS_LOADER(108, ObjectId::read, DataWriter::writeType), 26 | CLASS_OBJECT(99, ObjectId::read, DataWriter::writeType); 27 | 28 | private final byte id; 29 | private final Function readFunction; 30 | private final BiConsumer writeFunction; 31 | 32 | Type(int value, Function readFunction, BiConsumer writeConsumer) { 33 | this.id = (byte) value; 34 | this.readFunction = (Function) readFunction; 35 | this.writeFunction = (BiConsumer) writeConsumer; 36 | } 37 | 38 | public static Type findByValue(byte value) { 39 | return Arrays.stream(Type.values()) 40 | .filter(type -> type.id == value) 41 | .findFirst().get(); 42 | } 43 | 44 | public Object read(DataReader reader) { 45 | return readFunction.apply(reader); 46 | } 47 | 48 | public void write(DataWriter writer, Object value) { 49 | writeFunction.accept(writer, value); 50 | } 51 | 52 | public byte getId() { 53 | return id; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/UntaggedValue.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | 5 | public class UntaggedValue extends Value { 6 | public static UntaggedValue read(DataReader reader, byte typeTag) { 7 | return new UntaggedValue(reader, typeTag); 8 | } 9 | 10 | UntaggedValue(DataReader reader, byte typeTag) { 11 | super(reader, typeTag); 12 | } 13 | 14 | public UntaggedValue(Type type, Object value) { 15 | super(type, value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /types/src/main/java/kaap/veiko/debuggerforker/types/jdwp/Value.java: -------------------------------------------------------------------------------- 1 | package kaap.veiko.debuggerforker.types.jdwp; 2 | 3 | import kaap.veiko.debuggerforker.types.DataReader; 4 | import kaap.veiko.debuggerforker.types.DataType; 5 | import kaap.veiko.debuggerforker.types.DataWriter; 6 | 7 | public class Value implements DataType { 8 | private final Type type; 9 | private final Object value; 10 | 11 | public static Value read(DataReader reader) { 12 | return new Value(reader); 13 | } 14 | 15 | private Value(DataReader reader) { 16 | this(reader, reader.readByte()); 17 | } 18 | 19 | protected Value(DataReader reader, byte typeTag) { 20 | this.type = Type.findByValue(typeTag); 21 | value = type.read(reader); 22 | } 23 | 24 | public Value(Type type, Object value) { 25 | this.type = type; 26 | this.value = value; 27 | } 28 | 29 | public Type getType() { 30 | return type; 31 | } 32 | 33 | public Object getValue() { 34 | return value; 35 | } 36 | 37 | @Override 38 | public void write(DataWriter writer) { 39 | writer.writeByte(type.getId()); 40 | type.write(writer, value); 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (o == null || getClass() != o.getClass()) { 49 | return false; 50 | } 51 | 52 | Value value1 = (Value) o; 53 | 54 | if (type != value1.type) { 55 | return false; 56 | } 57 | return value != null ? value.equals(value1.value) : value1.value == null; 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | int result = type != null ? type.hashCode() : 0; 63 | result = 31 * result + (value != null ? value.hashCode() : 0); 64 | return result; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "Value{" + 70 | "type=" + type + 71 | ", value=" + value + 72 | '}'; 73 | } 74 | } 75 | --------------------------------------------------------------------------------