There are a few issues with using JDWP for our purposes:
The VM only expects one connection from a debugger, so you couldn't attach the monitor and a debugger at the same time. This will be worked around by connecting the debugger to the monitor and passing the traffic through. (We're already doing the pass-through with "jdwpspy"; requires some management of our request IDs though.) This should be more convenient than the current "guess the port number" system when we're attached to a device. The VM behaves differently when a debugger is attached. It will run more slowly, and any objects passed to the monitor or debugger are immune to GC. We can work around this by not enabling the slow path until non-DDM traffic is observed. We also want to have a "debugger has connected/disconnected" message that allows the VM to release debugger-related resources without dropping the net connection. Non-DDM VMs should not freak out when DDM connects. There are no guarantees here for 3rd-party VMs (e.g. a certain mainstream VM, which crashes instantly), but our older JamVM can be configured to reject the "hello" packet. Connection EstablishmentThere are two basic approaches: have the server contact the VMs, and have the VMs contact the server. The former is less "precise" than the latter, because you have to scan for the clients, but it has some advantages.
There are three interesting scenarios:
The DDM server is started, then the USB-attached device is booted or the simulator is launched. The device or simulator is already running when the DDM server is started. The DDM server is running when an already-started device is attached to USB.If we have the VMs connect to the DDM server on startup, we only handle case #1. If the DDM server scans for VMs when it starts, we only handle case #2. Neither handles case #3, which is probably the most important of the bunch as the device matures.
The plan is to have a drop-down menu with two entries, "scan workstation" and "scan device". The former causes the DDM server to search for VMs on "localhost", the latter causes it to search for VMs on the other side of an ADB connection. The DDM server will scan for VMs every few seconds, either checking a range of known VM ports (e.g. 8000-8040) or interacting with some sort of process database on the device. Changing modes causes all existing connections to be dropped.
When the DDM server first starts, it will try to execute "adb usb" to ensure that the ADB server is running. (Note it will be necessary to launch the DDM server from a shell with "adb" in the path.) If this fails, talking to the device will still be possible so long as the ADB daemon is already running.
Connecting a DebuggerWith the DDM server sitting on the JDWP port of all VMs, it will be necessary to connect the debugger through the DDM server. Each VM being debugged will have a separate port being listened to by the DDM server, allowing you to connect a debugger to one or more VMs simultaneously.
In the common case, however, the developer will only want to debug a single VM. One port (say 8700) will be listened to by the DDM server, and anything connecting to it will be connected to the "current VM" (selected in the UI). This should allow developers to focus on a single application, which may otherwise shift around in the ordering, without having to adjust their IDE settings to a different port every time they restart the device.
Packet FormatInformation is sent in chunks. Each chunk starts with:
u4 type u4 lengthand contains a variable amount of type-specific data. Unrecognized types cause an empty response from the client and are quietly ignored by the server. [Should probably return an error; need an "error" chunk type and a handler on the server side.]The same chunk type may have different meanings when sent in different directions. For example, the same type may be used for both a query and a response to the query. For sanity the type must always be used in related transactions.
This is somewhat redundant with the JDWP framing, which includes a 4-byte length and a two-byte type code ("command set" and "command"; a range of command set values is designated for "vendor-defined commands and extensions"). Using the chunk format allows us to remain independent of the underlying transport, avoids intrusive integration with JDWP client code, and provides a way to send multiple chunks in a single transmission unit. [I'm taking the multi-chunk packets into account in the design, but do not plan to implement them unless the need arises.]
Because we may be sending data over a slow USB link, the chunks may be compressed. Compressed chunks are written as a chunk type that indicates the compression, followed by the compressed length, followed by the original chunk type and the uncompressed length. For zlib's deflate algorithm, the chunk type is "ZLIB".
Following the JDWP model, packets sent from the server to the client are always acknowledged, but packets sent from client to server never are. The JDWP error code field is always set to "no error"; failure responses from specific requests must be encoded into the DDM messages.