Use writev() for DDMS packets.

The code was using the JDWP "expanding buffer" stuff, which is fine for
4-byte status messages but will be terrible for buffers of tracing and
profiling data.  Now we construct the header in a separate buffer.
diff --git a/vm/jdwp/JdwpAdb.c b/vm/jdwp/JdwpAdb.c
index a74f9e1..cbb5e9d 100644
--- a/vm/jdwp/JdwpAdb.c
+++ b/vm/jdwp/JdwpAdb.c
@@ -434,8 +434,6 @@
 
     cmd = cmdSet = 0;       // shut up gcc
 
-    /*dumpPacket(netState->inputBuffer);*/
-
     length = read4BE(&buf);
     id = read4BE(&buf);
     flags = read1(&buf);
@@ -673,7 +671,6 @@
     JdwpNetState* netState = state->netState;
     int cc;
 
-    /* dumpPacket(expandBufGetBuffer(pReq)); */
     if (netState->clientSock < 0) {
         /* can happen with some DDMS events */
         LOGV("NOT sending request -- no debugger is attached\n");
@@ -697,6 +694,53 @@
     return true;
 }
 
+/*
+ * Send a request that was split into two buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const void* header,
+    size_t headerLen, const void* body, size_t bodyLen)
+{
+    JdwpNetState* netState = state->netState;
+
+    assert(headerLen > 0);
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        LOGV("NOT sending request -- no debugger is attached\n");
+        return false;
+    }
+
+    struct iovec iov[2];
+    int iovcnt = 1;
+    iov[0].iov_base = (void*) header;
+    iov[0].iov_len = headerLen;
+    if (body != NULL) {
+        iovcnt++;
+        iov[1].iov_base = (void*) body;
+        iov[1].iov_len = bodyLen;
+    }
+
+    /*
+     * TODO: we currently assume the writev() will complete in one
+     * go, which may not be safe for a network socket.  We may need
+     * to mutex this against handlePacket().
+     */
+    ssize_t actual;
+    actual = writev(netState->clientSock, iov, iovcnt);
+    if ((size_t)actual != headerLen + bodyLen) {
+        LOGE("Failed sending b-req to debugger: %s (%d of %zu)\n",
+            strerror(errno), (int) actual, headerLen+bodyLen);
+        return false;
+    }
+
+    return true;
+}
+
 
 /*
  * Our functions.
@@ -711,7 +755,8 @@
     isConnected,
     awaitingHandshake,
     processIncoming,
-    sendRequest
+    sendRequest,
+    sendBufferedRequest
 };
 
 /*
diff --git a/vm/jdwp/JdwpEvent.c b/vm/jdwp/JdwpEvent.c
index 3233463..bf6054d 100644
--- a/vm/jdwp/JdwpEvent.c
+++ b/vm/jdwp/JdwpEvent.c
@@ -1260,38 +1260,17 @@
  */
 void dvmJdwpDdmSendChunk(JdwpState* state, int type, int len, const u1* buf)
 {
-    ExpandBuf* pReq;
-    u1* outBuf;
+    u1 header[kJDWPHeaderLen + 8];
 
-    /*
-     * Write the chunk header and data into the ExpandBuf.
-     */
-    pReq = expandBufAlloc();
-    expandBufAddSpace(pReq, kJDWPHeaderLen);
-    expandBufAdd4BE(pReq, type);
-    expandBufAdd4BE(pReq, len);
-    if (len > 0) {
-        outBuf = expandBufAddSpace(pReq, len);
-        memcpy(outBuf, buf, len);
-    }
+    /* form the header (JDWP plus DDMS) */
+    set4BE(header, sizeof(header) + len);
+    set4BE(header+4, dvmJdwpNextRequestSerial(state));
+    set1(header+8, 0);     /* flags */
+    set1(header+9, kJDWPDdmCmdSet);
+    set1(header+10, kJDWPDdmCmd);
+    set4BE(header+11, type);
+    set4BE(header+15, len);
 
-    /*
-     * Go back and write the JDWP header.
-     */
-    outBuf = expandBufGetBuffer(pReq);
-
-    set4BE(outBuf, expandBufGetLength(pReq));
-    set4BE(outBuf+4, dvmJdwpNextRequestSerial(state));
-    set1(outBuf+8, 0);     /* flags */
-    set1(outBuf+9, kJDWPDdmCmdSet);
-    set1(outBuf+10, kJDWPDdmCmd);
-
-    /*
-     * Send it up.
-     */
-    //LOGD("Sending chunk (type=0x%08x len=%d)\n", type, len);
-    dvmJdwpSendRequest(state, pReq);
-
-    expandBufFree(pReq);
+    dvmJdwpSendBufferedRequest(state, header, sizeof(header), buf, len);
 }
 
diff --git a/vm/jdwp/JdwpPriv.h b/vm/jdwp/JdwpPriv.h
index 087b560..f5d80f8 100644
--- a/vm/jdwp/JdwpPriv.h
+++ b/vm/jdwp/JdwpPriv.h
@@ -59,6 +59,8 @@
     bool (*awaitingHandshake)(struct JdwpState* state);
     bool (*processIncoming)(struct JdwpState* state);
     bool (*sendRequest)(struct JdwpState* state, ExpandBuf* pReq);
+    bool (*sendBufferedRequest)(struct JdwpState* state, const void* header,
+        size_t headerLen, const void* body, size_t bodyLen);
 } JdwpTransport;
 
 const JdwpTransport* dvmJdwpSocketTransport();
@@ -167,5 +169,11 @@
 INLINE bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq) {
     return (*state->transport->sendRequest)(state, pReq);
 }
+INLINE bool dvmJdwpSendBufferedRequest(JdwpState* state, const void* header,
+    size_t headerLen, const void* body, size_t bodyLen)
+{
+    return (*state->transport->sendBufferedRequest)(state, header, headerLen,
+        body, bodyLen);
+}
 
 #endif /*_DALVIK_JDWP_JDWPPRIV*/
diff --git a/vm/jdwp/JdwpSocket.c b/vm/jdwp/JdwpSocket.c
index 7b1ccfc..a6cbb02 100644
--- a/vm/jdwp/JdwpSocket.c
+++ b/vm/jdwp/JdwpSocket.c
@@ -825,7 +825,7 @@
     JdwpNetState* netState = state->netState;
     int cc;
 
-    dumpPacket(expandBufGetBuffer(pReq));
+    /*dumpPacket(expandBufGetBuffer(pReq));*/
     if (netState->clientSock < 0) {
         /* can happen with some DDMS events */
         LOGV("NOT sending request -- no debugger is attached\n");
@@ -849,9 +849,60 @@
     return true;
 }
 
+/*
+ * Send a request that was split into two buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const void* header,
+    size_t headerLen, const void* body, size_t bodyLen)
+{
+    JdwpNetState* netState = state->netState;
+
+    assert(headerLen > 0);
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        LOGV("NOT sending request -- no debugger is attached\n");
+        return false;
+    }
+
+    struct iovec iov[2];
+    int iovcnt = 1;
+    iov[0].iov_base = (void*) header;
+    iov[0].iov_len = headerLen;
+    if (body != NULL) {
+        iovcnt++;
+        iov[1].iov_base = (void*) body;
+        iov[1].iov_len = bodyLen;
+    }
+
+    /*
+     * TODO: we currently assume the writev() will complete in one
+     * go, which may not be safe for a network socket.  We may need
+     * to mutex this against handlePacket().
+     */
+    ssize_t actual;
+    actual = writev(netState->clientSock, iov, iovcnt);
+    if ((size_t)actual != headerLen + bodyLen) {
+        LOGE("Failed sending b-req to debugger: %s (%d of %zu)\n",
+            strerror(errno), (int) actual, headerLen+bodyLen);
+        return false;
+    }
+
+    return true;
+}
+
 
 /*
  * Our functions.
+ *
+ * We can't generally share the implementations with other transports,
+ * even if they're also socket-based, because our JdwpNetState will be
+ * different from theirs.
  */
 static const JdwpTransport socketTransport = {
     prepareSocket,
@@ -863,7 +914,8 @@
     isConnected,
     awaitingHandshake,
     processIncoming,
-    sendRequest
+    sendRequest,
+    sendBufferedRequest,
 };
 
 /*