Implement more DDMS support.

You can now launch DDMS without killing running oatexec processes...

Change-Id: I13e1d6df4f0cbd0c06b69471823a13e4e820b93b
diff --git a/src/debugger.cc b/src/debugger.cc
index 7d6d344..07c445e 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -18,6 +18,7 @@
 
 #include <sys/uio.h>
 
+#include "ScopedPrimitiveArray.h"
 #include "thread_list.h"
 
 namespace art {
@@ -607,17 +608,120 @@
   UNIMPLEMENTED(FATAL);
 }
 
+/*
+ * "buf" contains a full JDWP packet, possibly with multiple chunks.  We
+ * need to process each, accumulate the replies, and ship the whole thing
+ * back.
+ *
+ * Returns "true" if we have a reply.  The reply buffer is newly allocated,
+ * and includes the chunk type/length, followed by the data.
+ *
+ * TODO: we currently assume that the request and reply include a single
+ * chunk.  If this becomes inconvenient we will need to adapt.
+ */
 bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen) {
-  UNIMPLEMENTED(FATAL);
-  return false;
+  CHECK_GE(dataLen, 0);
+
+  Thread* self = Thread::Current();
+  JNIEnv* env = self->GetJniEnv();
+
+  static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
+  static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
+  static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
+      "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+  static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
+  static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
+  static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
+  static jfieldID type_fid = env->GetFieldID(Chunk_class, "type", "I");
+
+  // Create a byte[] corresponding to 'buf'.
+  jbyteArray dataArray = env->NewByteArray(dataLen);
+  if (dataArray == NULL) {
+    LOG(WARNING) << "byte[] allocation failed: " << dataLen;
+    env->ExceptionClear();
+    return false;
+  }
+  env->SetByteArrayRegion(dataArray, 0, dataLen, reinterpret_cast<const jbyte*>(buf));
+
+  const int kChunkHdrLen = 8;
+
+  // Run through and find all chunks.  [Currently just find the first.]
+  ScopedByteArrayRO contents(env, dataArray);
+  jint type = JDWP::get4BE(reinterpret_cast<const uint8_t*>(&contents[0]));
+  jint length = JDWP::get4BE(reinterpret_cast<const uint8_t*>(&contents[4]));
+  jint offset = kChunkHdrLen;
+  if (offset + length > dataLen) {
+    LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%d)", length, dataLen);
+    return false;
+  }
+
+  // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
+  jobject chunk = env->CallStaticObjectMethod(DdmServer_class, dispatch_mid, type, dataArray, offset, length);
+  if (env->ExceptionCheck()) {
+    LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return false;
+  }
+
+  if (chunk == NULL) {
+    return false;
+  }
+
+  /*
+   * Pull the pieces out of the chunk.  We copy the results into a
+   * newly-allocated buffer that the caller can free.  We don't want to
+   * continue using the Chunk object because nothing has a reference to it.
+   *
+   * We could avoid this by returning type/data/offset/length and having
+   * the caller be aware of the object lifetime issues, but that
+   * integrates the JDWP code more tightly into the VM, and doesn't work
+   * if we have responses for multiple chunks.
+   *
+   * So we're pretty much stuck with copying data around multiple times.
+   */
+  jbyteArray replyData = reinterpret_cast<jbyteArray>(env->GetObjectField(chunk, data_fid));
+  length = env->GetIntField(chunk, length_fid);
+  offset = env->GetIntField(chunk, offset_fid);
+  type = env->GetIntField(chunk, type_fid);
+
+  LOG(VERBOSE) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData, offset, length);
+  if (length == 0 || replyData == NULL) {
+    return false;
+  }
+
+  jsize replyLength = env->GetArrayLength(replyData);
+  if (offset + length > replyLength) {
+    LOG(WARNING) << StringPrintf("chunk off=%d len=%d exceeds reply array len %d", offset, length, replyLength);
+    return false;
+  }
+
+  uint8_t* reply = new uint8_t[length + kChunkHdrLen];
+  if (reply == NULL) {
+    LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
+    return false;
+  }
+  JDWP::set4BE(reply + 0, type);
+  JDWP::set4BE(reply + 4, length);
+  env->GetByteArrayRegion(replyData, offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen));
+
+  *pReplyBuf = reply;
+  *pReplyLen = length + kChunkHdrLen;
+
+  LOG(VERBOSE) << StringPrintf("dvmHandleDdm returning type=%.4s buf=%p len=%d", (char*) reply, reply, length);
+  return true;
 }
 
 void Dbg::DdmConnected() {
-  UNIMPLEMENTED(FATAL);
+  LOG(VERBOSE) << "Broadcasting DDM connect...";
+  //broadcast(CONNECTED);
+  UNIMPLEMENTED(WARNING);
 }
 
 void Dbg::DdmDisconnected() {
-  UNIMPLEMENTED(FATAL);
+  LOG(VERBOSE) << "Broadcasting DDM disconnect...";
+  //broadcast(DISCONNECTED);
+  //gDvm.ddmThreadNotification = false;
 }
 
 void Dbg::DdmSendChunk(int type, size_t byte_count, const uint8_t* buf) {