Bring across the JDWP implementation.

This compiles and links, but does nothing until we fill out the 100 or so
unimplemented methods in "debugger.cc". Note that I also need to add the
extra command-line handling for the JDWP agent stuff, and add calls from
the runtime to the various "something interesting is going on" hooks.

Change-Id: I477cf3caf9e248c384ce1d739cbfadb60e2008bc
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
new file mode 100644
index 0000000..3a2d73e
--- /dev/null
+++ b/src/jdwp/jdwp_event.cc
@@ -0,0 +1,1211 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Send events to the debugger.
+ */
+#include "debugger.h"
+#include "jdwp/jdwp_priv.h"
+#include "jdwp/jdwp_constants.h"
+#include "jdwp/jdwp_handler.h"
+#include "jdwp/jdwp_event.h"
+#include "jdwp/jdwp_expand_buf.h"
+#include "logging.h"
+#include "stringprintf.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>     /* for offsetof() */
+#include <unistd.h>
+
+/*
+General notes:
+
+The event add/remove stuff usually happens from the debugger thread,
+in response to requests from the debugger, but can also happen as the
+result of an event in an arbitrary thread (e.g. an event with a "count"
+mod expires).  It's important to keep the event list locked when processing
+events.
+
+Event posting can happen from any thread.  The JDWP thread will not usually
+post anything but VM start/death, but if a JDWP request causes a class
+to be loaded, the ClassPrepare event will come from the JDWP thread.
+
+
+We can have serialization issues when we post an event to the debugger.
+For example, a thread could send an "I hit a breakpoint and am suspending
+myself" message to the debugger.  Before it manages to suspend itself, the
+debugger's response ("not interested, resume thread") arrives and is
+processed.  We try to resume a thread that hasn't yet suspended.
+
+This means that, after posting an event to the debugger, we need to wait
+for the event thread to suspend itself (and, potentially, all other threads)
+before processing any additional requests from the debugger.  While doing
+so we need to be aware that multiple threads may be hitting breakpoints
+or other events simultaneously, so we either need to wait for all of them
+or serialize the events with each other.
+
+The current mechanism works like this:
+  Event thread:
+   - If I'm going to suspend, grab the "I am posting an event" token.  Wait
+     for it if it's not currently available.
+   - Post the event to the debugger.
+   - If appropriate, suspend others and then myself.  As part of suspending
+     myself, release the "I am posting" token.
+  JDWP thread:
+   - When an event arrives, see if somebody is posting an event.  If so,
+     sleep until we can acquire the "I am posting an event" token.  Release
+     it immediately and continue processing -- the event we have already
+     received should not interfere with other events that haven't yet
+     been posted.
+
+Some care must be taken to avoid deadlock:
+
+ - thread A and thread B exit near-simultaneously, and post thread-death
+   events with a "suspend all" clause
+ - thread A gets the event token, thread B sits and waits for it
+ - thread A wants to suspend all other threads, but thread B is waiting
+   for the token and can't be suspended
+
+So we need to mark thread B in such a way that thread A doesn't wait for it.
+
+If we just bracket the "grab event token" call with a change to VMWAIT
+before sleeping, the switch back to RUNNING state when we get the token
+will cause thread B to suspend (remember, thread A's global suspend is
+still in force, even after it releases the token).  Suspending while
+holding the event token is very bad, because it prevents the JDWP thread
+from processing incoming messages.
+
+We need to change to VMWAIT state at the *start* of posting an event,
+and stay there until we either finish posting the event or decide to
+put ourselves to sleep.  That way we don't interfere with anyone else and
+don't allow anyone else to interfere with us.
+*/
+
+
+#define kJdwpEventCommandSet    64
+#define kJdwpCompositeCommand   100
+
+namespace art {
+
+namespace JDWP {
+
+/*
+ * Stuff to compare against when deciding if a mod matches.  Only the
+ * values for mods valid for the event being evaluated will be filled in.
+ * The rest will be zeroed.
+ */
+struct ModBasket {
+  const JdwpLocation* pLoc;           /* LocationOnly */
+  const char*         className;      /* ClassMatch/ClassExclude */
+  ObjectId            threadId;       /* ThreadOnly */
+  RefTypeId           classId;        /* ClassOnly */
+  RefTypeId           excepClassId;   /* ExceptionOnly */
+  bool                caught;         /* ExceptionOnly */
+  FieldId             field;          /* FieldOnly */
+  ObjectId            thisPtr;        /* InstanceOnly */
+  /* nothing for StepOnly -- handled differently */
+};
+
+/*
+ * Get the next "request" serial number.  We use this when sending
+ * packets to the debugger.
+ */
+uint32_t NextRequestSerial(JdwpState* state) {
+  MutexLock mu(state->serial_lock_);
+  return state->requestSerial++;
+}
+
+/*
+ * Get the next "event" serial number.  We use this in the response to
+ * message type EventRequest.Set.
+ */
+uint32_t NextEventSerial(JdwpState* state) {
+  MutexLock mu(state->serial_lock_);
+  return state->eventSerial++;
+}
+
+/*
+ * Lock the "event" mutex, which guards the list of registered events.
+ */
+static void lockEventMutex(JdwpState* state) {
+  //Dbg::ThreadWaiting();
+  state->event_lock_.Lock();
+  //Dbg::ThreadRunning();
+}
+
+/*
+ * Unlock the "event" mutex.
+ */
+static void unlockEventMutex(JdwpState* state) {
+  state->event_lock_.Unlock();
+}
+
+/*
+ * Dump an event to the log file.
+ */
+static void dumpEvent(const JdwpEvent* pEvent) {
+  LOG(INFO) << StringPrintf("Event id=0x%4x %p (prev=%p next=%p):", pEvent->requestId, pEvent, pEvent->prev, pEvent->next);
+  LOG(INFO) << "  kind=" << pEvent->eventKind << " susp=" << pEvent->suspendPolicy << " modCount=" << pEvent->modCount;
+
+  for (int i = 0; i < pEvent->modCount; i++) {
+    const JdwpEventMod* pMod = &pEvent->mods[i];
+    LOG(INFO) << "  " << static_cast<JdwpModKind>(pMod->modKind);
+    /* TODO - show details */
+  }
+}
+
+/*
+ * Add an event to the list.  Ordering is not important.
+ *
+ * If something prevents the event from being registered, e.g. it's a
+ * single-step request on a thread that doesn't exist, the event will
+ * not be added to the list, and an appropriate error will be returned.
+ */
+JdwpError RegisterEvent(JdwpState* state, JdwpEvent* pEvent) {
+  lockEventMutex(state);
+
+  CHECK(state != NULL);
+  CHECK(pEvent != NULL);
+  CHECK(pEvent->prev == NULL);
+  CHECK(pEvent->next == NULL);
+
+  /*
+   * If one or more "break"-type mods are used, register them with
+   * the interpreter.
+   */
+  for (int i = 0; i < pEvent->modCount; i++) {
+    const JdwpEventMod* pMod = &pEvent->mods[i];
+    if (pMod->modKind == MK_LOCATION_ONLY) {
+      /* should only be for Breakpoint, Step, and Exception */
+      Dbg::WatchLocation(&pMod->locationOnly.loc);
+    } else if (pMod->modKind == MK_STEP) {
+      /* should only be for EK_SINGLE_STEP; should only be one */
+      JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size);
+      JdwpStepDepth depth = static_cast<JdwpStepDepth>(pMod->step.depth);
+      Dbg::ConfigureStep(pMod->step.threadId, size, depth);
+    } else if (pMod->modKind == MK_FIELD_ONLY) {
+      /* should be for EK_FIELD_ACCESS or EK_FIELD_MODIFICATION */
+      dumpEvent(pEvent);  /* TODO - need for field watches */
+    }
+  }
+
+  /*
+   * Add to list.
+   */
+  if (state->eventList != NULL) {
+    pEvent->next = state->eventList;
+    state->eventList->prev = pEvent;
+  }
+  state->eventList = pEvent;
+  state->numEvents++;
+
+  unlockEventMutex(state);
+
+  return ERR_NONE;
+}
+
+/*
+ * Remove an event from the list.  This will also remove the event from
+ * any optimization tables, e.g. breakpoints.
+ *
+ * Does not free the JdwpEvent.
+ *
+ * Grab the eventLock before calling here.
+ */
+static void unregisterEvent(JdwpState* state, JdwpEvent* pEvent) {
+  if (pEvent->prev == NULL) {
+    /* head of the list */
+    CHECK(state->eventList == pEvent);
+
+    state->eventList = pEvent->next;
+  } else {
+    pEvent->prev->next = pEvent->next;
+  }
+
+  if (pEvent->next != NULL) {
+    pEvent->next->prev = pEvent->prev;
+    pEvent->next = NULL;
+  }
+  pEvent->prev = NULL;
+
+  /*
+   * Unhook us from the interpreter, if necessary.
+   */
+  for (int i = 0; i < pEvent->modCount; i++) {
+    JdwpEventMod* pMod = &pEvent->mods[i];
+    if (pMod->modKind == MK_LOCATION_ONLY) {
+      /* should only be for Breakpoint, Step, and Exception */
+      Dbg::UnwatchLocation(&pMod->locationOnly.loc);
+    }
+    if (pMod->modKind == MK_STEP) {
+      /* should only be for EK_SINGLE_STEP; should only be one */
+      Dbg::UnconfigureStep(pMod->step.threadId);
+    }
+  }
+
+  state->numEvents--;
+  CHECK(state->numEvents != 0 || state->eventList == NULL);
+}
+
+/*
+ * Remove the event with the given ID from the list.
+ *
+ * Failure to find the event isn't really an error, but it is a little
+ * weird.  (It looks like Eclipse will try to be extra careful and will
+ * explicitly remove one-off single-step events.)
+ */
+void UnregisterEventById(JdwpState* state, uint32_t requestId) {
+  lockEventMutex(state);
+
+  JdwpEvent* pEvent = state->eventList;
+  while (pEvent != NULL) {
+    if (pEvent->requestId == requestId) {
+      unregisterEvent(state, pEvent);
+      EventFree(pEvent);
+      goto done;      /* there can be only one with a given ID */
+    }
+
+    pEvent = pEvent->next;
+  }
+
+  //LOGD("Odd: no match when removing event reqId=0x%04x", requestId);
+
+done:
+  unlockEventMutex(state);
+}
+
+/*
+ * Remove all entries from the event list.
+ */
+void UnregisterAll(JdwpState* state) {
+  lockEventMutex(state);
+
+  JdwpEvent* pEvent = state->eventList;
+  while (pEvent != NULL) {
+    JdwpEvent* pNextEvent = pEvent->next;
+
+    unregisterEvent(state, pEvent);
+    EventFree(pEvent);
+    pEvent = pNextEvent;
+  }
+
+  state->eventList = NULL;
+
+  unlockEventMutex(state);
+}
+
+/*
+ * Allocate a JdwpEvent struct with enough space to hold the specified
+ * number of mod records.
+ */
+JdwpEvent* EventAlloc(int numMods) {
+  JdwpEvent* newEvent;
+  int allocSize = offsetof(JdwpEvent, mods) + numMods * sizeof(newEvent->mods[0]);
+  newEvent = reinterpret_cast<JdwpEvent*>(malloc(allocSize));
+  memset(newEvent, 0, allocSize);
+  return newEvent;
+}
+
+/*
+ * Free a JdwpEvent.
+ *
+ * Do not call this until the event has been removed from the list.
+ */
+void EventFree(JdwpEvent* pEvent) {
+  if (pEvent == NULL) {
+    return;
+  }
+
+  /* make sure it was removed from the list */
+  CHECK(pEvent->prev == NULL);
+  CHECK(pEvent->next == NULL);
+  /* want to check state->eventList != pEvent */
+
+  /*
+   * Free any hairy bits in the mods.
+   */
+  for (int i = 0; i < pEvent->modCount; i++) {
+    if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
+      free(pEvent->mods[i].classMatch.classPattern);
+      pEvent->mods[i].classMatch.classPattern = NULL;
+    }
+    if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
+      free(pEvent->mods[i].classExclude.classPattern);
+      pEvent->mods[i].classExclude.classPattern = NULL;
+    }
+  }
+
+  free(pEvent);
+}
+
+/*
+ * Allocate storage for matching events.  To keep things simple we
+ * use an array with enough storage for the entire list.
+ *
+ * The state->eventLock should be held before calling.
+ */
+static JdwpEvent** allocMatchList(JdwpState* state) {
+  return (JdwpEvent**) malloc(sizeof(JdwpEvent*) * state->numEvents);
+}
+
+/*
+ * Run through the list and remove any entries with an expired "count" mod
+ * from the event list, then free the match list.
+ */
+static void cleanupMatchList(JdwpState* state, JdwpEvent** matchList, int matchCount) {
+  JdwpEvent** ppEvent = matchList;
+
+  while (matchCount--) {
+    JdwpEvent* pEvent = *ppEvent;
+
+    for (int i = 0; i < pEvent->modCount; i++) {
+      if (pEvent->mods[i].modKind == MK_COUNT && pEvent->mods[i].count.count == 0) {
+        LOG(VERBOSE) << "##### Removing expired event";
+        unregisterEvent(state, pEvent);
+        EventFree(pEvent);
+        break;
+      }
+    }
+
+    ppEvent++;
+  }
+
+  free(matchList);
+}
+
+/*
+ * Match a string against a "restricted regular expression", which is just
+ * a string that may start or end with '*' (e.g. "*.Foo" or "java.*").
+ *
+ * ("Restricted name globbing" might have been a better term.)
+ */
+static bool patternMatch(const char* pattern, const char* target) {
+  int patLen = strlen(pattern);
+
+  if (pattern[0] == '*') {
+    int targetLen = strlen(target);
+    patLen--;
+    // TODO: remove printf when we find a test case to verify this
+    LOG(ERROR) << ">>> comparing '" << (pattern + 1) << "' to '" << (target + (targetLen-patLen)) << "'";
+
+    if (targetLen < patLen) {
+      return false;
+    }
+    return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
+  } else if (pattern[patLen-1] == '*') {
+    return strncmp(pattern, target, patLen-1) == 0;
+  } else {
+    return strcmp(pattern, target) == 0;
+  }
+}
+
+/*
+ * See if two locations are equal.
+ *
+ * It's tempting to do a bitwise compare ("struct ==" or memcmp), but if
+ * the storage wasn't zeroed out there could be undefined values in the
+ * padding.  Besides, the odds of "idx" being equal while the others aren't
+ * is very small, so this is usually just a simple integer comparison.
+ */
+static inline bool locationMatch(const JdwpLocation* pLoc1, const JdwpLocation* pLoc2) {
+  return pLoc1->idx == pLoc2->idx &&
+      pLoc1->methodId == pLoc2->methodId &&
+      pLoc1->classId == pLoc2->classId &&
+      pLoc1->typeTag == pLoc2->typeTag;
+}
+
+/*
+ * See if the event's mods match up with the contents of "basket".
+ *
+ * If we find a Count mod before rejecting an event, we decrement it.  We
+ * need to do this even if later mods cause us to ignore the event.
+ */
+static bool modsMatch(JdwpState* state, JdwpEvent* pEvent, ModBasket* basket) {
+  JdwpEventMod* pMod = pEvent->mods;
+
+  for (int i = pEvent->modCount; i > 0; i--, pMod++) {
+    switch (pMod->modKind) {
+    case MK_COUNT:
+      CHECK_GT(pMod->count.count, 0);
+      pMod->count.count--;
+      break;
+    case MK_CONDITIONAL:
+      CHECK(false);  // should not be getting these
+      break;
+    case MK_THREAD_ONLY:
+      if (pMod->threadOnly.threadId != basket->threadId) {
+        return false;
+      }
+      break;
+    case MK_CLASS_ONLY:
+      if (!Dbg::MatchType(basket->classId, pMod->classOnly.refTypeId)) {
+        return false;
+      }
+      break;
+    case MK_CLASS_MATCH:
+      if (!patternMatch(pMod->classMatch.classPattern, basket->className)) {
+        return false;
+      }
+      break;
+    case MK_CLASS_EXCLUDE:
+      if (patternMatch(pMod->classMatch.classPattern, basket->className)) {
+        return false;
+      }
+      break;
+    case MK_LOCATION_ONLY:
+      if (!locationMatch(&pMod->locationOnly.loc, basket->pLoc)) {
+        return false;
+      }
+      break;
+    case MK_EXCEPTION_ONLY:
+      if (pMod->exceptionOnly.refTypeId != 0 && !Dbg::MatchType(basket->excepClassId, pMod->exceptionOnly.refTypeId)) {
+        return false;
+      }
+      if ((basket->caught && !pMod->exceptionOnly.caught) || (!basket->caught && !pMod->exceptionOnly.uncaught)) {
+        return false;
+      }
+      break;
+    case MK_FIELD_ONLY:
+      if (!Dbg::MatchType(basket->classId, pMod->fieldOnly.refTypeId) || pMod->fieldOnly.fieldId != basket->field) {
+        return false;
+      }
+      break;
+    case MK_STEP:
+      if (pMod->step.threadId != basket->threadId) {
+        return false;
+      }
+      break;
+    case MK_INSTANCE_ONLY:
+      if (pMod->instanceOnly.objectId != basket->thisPtr) {
+        return false;
+      }
+      break;
+    default:
+      LOG(ERROR) << "unhandled mod kind " << pMod->modKind;
+      CHECK(false);
+      break;
+    }
+  }
+  return true;
+}
+
+/*
+ * Find all events of type "eventKind" with mods that match up with the
+ * rest of the arguments.
+ *
+ * Found events are appended to "matchList", and "*pMatchCount" is advanced,
+ * so this may be called multiple times for grouped events.
+ *
+ * DO NOT call this multiple times for the same eventKind, as Count mods are
+ * decremented during the scan.
+ */
+static void findMatchingEvents(JdwpState* state, JdwpEventKind eventKind,
+    ModBasket* basket, JdwpEvent** matchList, int* pMatchCount) {
+  /* start after the existing entries */
+  matchList += *pMatchCount;
+
+  JdwpEvent* pEvent = state->eventList;
+  while (pEvent != NULL) {
+    if (pEvent->eventKind == eventKind && modsMatch(state, pEvent, basket)) {
+      *matchList++ = pEvent;
+      (*pMatchCount)++;
+    }
+
+    pEvent = pEvent->next;
+  }
+}
+
+/*
+ * Scan through the list of matches and determine the most severe
+ * suspension policy.
+ */
+static JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** matchList, int matchCount) {
+  JdwpSuspendPolicy policy = SP_NONE;
+
+  while (matchCount--) {
+    if ((*matchList)->suspendPolicy > policy) {
+      policy = (*matchList)->suspendPolicy;
+    }
+    matchList++;
+  }
+
+  return policy;
+}
+
+/*
+ * Three possibilities:
+ *  SP_NONE - do nothing
+ *  SP_EVENT_THREAD - suspend ourselves
+ *  SP_ALL - suspend everybody except JDWP support thread
+ */
+static void suspendByPolicy(JdwpState* state, JdwpSuspendPolicy suspendPolicy) {
+  if (suspendPolicy == SP_NONE) {
+    return;
+  }
+
+  if (suspendPolicy == SP_ALL) {
+    Dbg::SuspendVM(true);
+  } else {
+    CHECK_EQ(suspendPolicy, SP_EVENT_THREAD);
+  }
+
+  /* this is rare but possible -- see CLASS_PREPARE handling */
+  if (Dbg::GetThreadSelfId() == state->debugThreadId) {
+    LOG(INFO) << "NOTE: suspendByPolicy not suspending JDWP thread";
+    return;
+  }
+
+  DebugInvokeReq* pReq = Dbg::GetInvokeReq();
+  while (true) {
+    pReq->ready = true;
+    Dbg::SuspendSelf();
+    pReq->ready = false;
+
+    /*
+     * The JDWP thread has told us (and possibly all other threads) to
+     * resume.  See if it has left anything in our DebugInvokeReq mailbox.
+     */
+    if (!pReq->invokeNeeded) {
+      /*LOGD("suspendByPolicy: no invoke needed");*/
+      break;
+    }
+
+    /* grab this before posting/suspending again */
+    SetWaitForEventThread(state, Dbg::GetThreadSelfId());
+
+    /* leave pReq->invokeNeeded raised so we can check reentrancy */
+    LOG(VERBOSE) << "invoking method...";
+    Dbg::ExecuteMethod(pReq);
+
+    pReq->err = ERR_NONE;
+
+    /* clear this before signaling */
+    pReq->invokeNeeded = false;
+
+    LOG(VERBOSE) << "invoke complete, signaling and self-suspending";
+    MutexLock mu(pReq->lock_);
+    pReq->cond_.Signal();
+  }
+}
+
+/*
+ * Determine if there is a method invocation in progress in the current
+ * thread.
+ *
+ * We look at the "invokeNeeded" flag in the per-thread DebugInvokeReq
+ * state.  If set, we're in the process of invoking a method.
+ */
+static bool invokeInProgress(JdwpState* state) {
+  DebugInvokeReq* pReq = Dbg::GetInvokeReq();
+  return pReq->invokeNeeded;
+}
+
+/*
+ * We need the JDWP thread to hold off on doing stuff while we post an
+ * event and then suspend ourselves.
+ *
+ * Call this with a threadId of zero if you just want to wait for the
+ * current thread operation to complete.
+ *
+ * This could go to sleep waiting for another thread, so it's important
+ * that the thread be marked as VMWAIT before calling here.
+ */
+void SetWaitForEventThread(JdwpState* state, ObjectId threadId) {
+  bool waited = false;
+
+  /* this is held for very brief periods; contention is unlikely */
+  MutexLock mu(state->event_thread_lock_);
+
+  /*
+   * If another thread is already doing stuff, wait for it.  This can
+   * go to sleep indefinitely.
+   */
+  while (state->eventThreadId != 0) {
+    LOG(VERBOSE) << StringPrintf("event in progress (0x%llx), 0x%llx sleeping", state->eventThreadId, threadId);
+    waited = true;
+    state->event_thread_cond_.Wait(state->event_thread_lock_);
+  }
+
+  if (waited || threadId != 0) {
+    LOG(VERBOSE) << StringPrintf("event token grabbed (0x%llx)", threadId);
+  }
+  if (threadId != 0) {
+    state->eventThreadId = threadId;
+  }
+}
+
+/*
+ * Clear the threadId and signal anybody waiting.
+ */
+void ClearWaitForEventThread(JdwpState* state) {
+  /*
+   * Grab the mutex.  Don't try to go in/out of VMWAIT mode, as this
+   * function is called by dvmSuspendSelf(), and the transition back
+   * to RUNNING would confuse it.
+   */
+  MutexLock mu(state->event_thread_lock_);
+
+  CHECK_NE(state->eventThreadId, 0U);
+  LOG(VERBOSE) << StringPrintf("cleared event token (0x%llx)", state->eventThreadId);
+
+  state->eventThreadId = 0;
+
+  state->event_thread_cond_.Signal();
+}
+
+
+/*
+ * Prep an event.  Allocates storage for the message and leaves space for
+ * the header.
+ */
+static ExpandBuf* eventPrep() {
+  ExpandBuf* pReq = expandBufAlloc();
+  expandBufAddSpace(pReq, kJDWPHeaderLen);
+  return pReq;
+}
+
+/*
+ * Write the header into the buffer and send the packet off to the debugger.
+ *
+ * Takes ownership of "pReq" (currently discards it).
+ */
+static void eventFinish(JdwpState* state, ExpandBuf* pReq) {
+  uint8_t* buf = expandBufGetBuffer(pReq);
+
+  set4BE(buf, expandBufGetLength(pReq));
+  set4BE(buf+4, NextRequestSerial(state));
+  set1(buf+8, 0);     /* flags */
+  set1(buf+9, kJdwpEventCommandSet);
+  set1(buf+10, kJdwpCompositeCommand);
+
+  SendRequest(state, pReq);
+
+  expandBufFree(pReq);
+}
+
+
+/*
+ * Tell the debugger that we have finished initializing.  This is always
+ * sent, even if the debugger hasn't requested it.
+ *
+ * This should be sent "before the main thread is started and before
+ * any application code has been executed".  The thread ID in the message
+ * must be for the main thread.
+ */
+bool PostVMStart(JdwpState* state, bool suspend) {
+  JdwpSuspendPolicy suspendPolicy;
+  ObjectId threadId = Dbg::GetThreadSelfId();
+
+  if (suspend) {
+    suspendPolicy = SP_ALL;
+  } else {
+    suspendPolicy = SP_NONE;
+  }
+
+  /* probably don't need this here */
+  lockEventMutex(state);
+
+  ExpandBuf* pReq = NULL;
+  if (true) {
+    LOG(VERBOSE) << "EVENT: " << EK_VM_START;
+    LOG(VERBOSE) << "  suspendPolicy=" << suspendPolicy;
+
+    pReq = eventPrep();
+    expandBufAdd1(pReq, suspendPolicy);
+    expandBufAdd4BE(pReq, 1);
+
+    expandBufAdd1(pReq, EK_VM_START);
+    expandBufAdd4BE(pReq, 0);       /* requestId */
+    expandBufAdd8BE(pReq, threadId);
+  }
+
+  unlockEventMutex(state);
+
+  /* send request and possibly suspend ourselves */
+  if (pReq != NULL) {
+    int oldStatus = Dbg::ThreadWaiting();
+    if (suspendPolicy != SP_NONE) {
+      SetWaitForEventThread(state, threadId);
+    }
+
+    eventFinish(state, pReq);
+
+    suspendByPolicy(state, suspendPolicy);
+    Dbg::ThreadContinuing(oldStatus);
+  }
+
+  return true;
+}
+
+// TODO: This doesn't behave like the real dvmDescriptorToName.
+// I'm hoping this isn't used to communicate with the debugger, and we can just use descriptors.
+char* dvmDescriptorToName(const char* descriptor) {
+  return strdup(descriptor);
+}
+
+/*
+ * A location of interest has been reached.  This handles:
+ *   Breakpoint
+ *   SingleStep
+ *   MethodEntry
+ *   MethodExit
+ * These four types must be grouped together in a single response.  The
+ * "eventFlags" indicates the type of event(s) that have happened.
+ *
+ * Valid mods:
+ *   Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, InstanceOnly
+ *   LocationOnly (for breakpoint/step only)
+ *   Step (for step only)
+ *
+ * Interesting test cases:
+ *  - Put a breakpoint on a native method.  Eclipse creates METHOD_ENTRY
+ *    and METHOD_EXIT events with a ClassOnly mod on the method's class.
+ *  - Use "run to line".  Eclipse creates a BREAKPOINT with Count=1.
+ *  - Single-step to a line with a breakpoint.  Should get a single
+ *    event message with both events in it.
+ */
+bool PostLocationEvent(JdwpState* state, const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags) {
+  ModBasket basket;
+  char* nameAlloc = NULL;
+
+  memset(&basket, 0, sizeof(basket));
+  basket.pLoc = pLoc;
+  basket.classId = pLoc->classId;
+  basket.thisPtr = thisPtr;
+  basket.threadId = Dbg::GetThreadSelfId();
+  basket.className = nameAlloc = dvmDescriptorToName(Dbg::GetClassDescriptor(pLoc->classId));
+
+  /*
+   * On rare occasions we may need to execute interpreted code in the VM
+   * while handling a request from the debugger.  Don't fire breakpoints
+   * while doing so.  (I don't think we currently do this at all, so
+   * this is mostly paranoia.)
+   */
+  if (basket.threadId == state->debugThreadId) {
+    LOG(VERBOSE) << "Ignoring location event in JDWP thread";
+    free(nameAlloc);
+    return false;
+  }
+
+  /*
+   * The debugger variable display tab may invoke the interpreter to format
+   * complex objects.  We want to ignore breakpoints and method entry/exit
+   * traps while working on behalf of the debugger.
+   *
+   * If we don't ignore them, the VM will get hung up, because we'll
+   * suspend on a breakpoint while the debugger is still waiting for its
+   * method invocation to complete.
+   */
+  if (invokeInProgress(state)) {
+    LOG(VERBOSE) << "Not checking breakpoints during invoke (" << basket.className << ")";
+    free(nameAlloc);
+    return false;
+  }
+
+  /* don't allow the list to be updated while we scan it */
+  lockEventMutex(state);
+
+  JdwpEvent** matchList = allocMatchList(state);
+  int matchCount = 0;
+
+  if ((eventFlags & Dbg::kBreakPoint) != 0) {
+    findMatchingEvents(state, EK_BREAKPOINT, &basket, matchList, &matchCount);
+  }
+  if ((eventFlags & Dbg::kSingleStep) != 0) {
+    findMatchingEvents(state, EK_SINGLE_STEP, &basket, matchList, &matchCount);
+  }
+  if ((eventFlags & Dbg::kMethodEntry) != 0) {
+    findMatchingEvents(state, EK_METHOD_ENTRY, &basket, matchList, &matchCount);
+  }
+  if ((eventFlags & Dbg::kMethodExit) != 0) {
+    findMatchingEvents(state, EK_METHOD_EXIT, &basket, matchList, &matchCount);
+  }
+
+  ExpandBuf* pReq = NULL;
+  JdwpSuspendPolicy suspendPolicy = SP_NONE;
+  if (matchCount != 0) {
+    LOG(VERBOSE) << "EVENT: " << matchList[0]->eventKind << "(" << matchCount << " total) "
+                 << basket.className << "." << Dbg::GetMethodName(pLoc->classId, pLoc->methodId)
+                 << " thread=" << (void*) basket.threadId << " code=" << (void*) pLoc->idx << ")";
+
+    suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+    LOG(VERBOSE) << "  suspendPolicy=" << suspendPolicy;
+
+    pReq = eventPrep();
+    expandBufAdd1(pReq, suspendPolicy);
+    expandBufAdd4BE(pReq, matchCount);
+
+    for (int i = 0; i < matchCount; i++) {
+      expandBufAdd1(pReq, matchList[i]->eventKind);
+      expandBufAdd4BE(pReq, matchList[i]->requestId);
+      expandBufAdd8BE(pReq, basket.threadId);
+      AddLocation(pReq, pLoc);
+    }
+  }
+
+  cleanupMatchList(state, matchList, matchCount);
+  unlockEventMutex(state);
+
+  /* send request and possibly suspend ourselves */
+  if (pReq != NULL) {
+    int oldStatus = Dbg::ThreadWaiting();
+    if (suspendPolicy != SP_NONE) {
+      SetWaitForEventThread(state, basket.threadId);
+    }
+
+    eventFinish(state, pReq);
+
+    suspendByPolicy(state, suspendPolicy);
+    Dbg::ThreadContinuing(oldStatus);
+  }
+
+  free(nameAlloc);
+  return matchCount != 0;
+}
+
+/*
+ * A thread is starting or stopping.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly
+ */
+bool PostThreadChange(JdwpState* state, ObjectId threadId, bool start) {
+  CHECK_EQ(threadId, Dbg::GetThreadSelfId());
+
+  /*
+   * I don't think this can happen.
+   */
+  if (invokeInProgress(state)) {
+    LOG(WARNING) << "Not posting thread change during invoke";
+    return false;
+  }
+
+  ModBasket basket;
+  memset(&basket, 0, sizeof(basket));
+  basket.threadId = threadId;
+
+  /* don't allow the list to be updated while we scan it */
+  lockEventMutex(state);
+
+  JdwpEvent** matchList = allocMatchList(state);
+  int matchCount = 0;
+
+  if (start) {
+    findMatchingEvents(state, EK_THREAD_START, &basket, matchList, &matchCount);
+  } else {
+    findMatchingEvents(state, EK_THREAD_DEATH, &basket, matchList, &matchCount);
+  }
+
+  ExpandBuf* pReq = NULL;
+  JdwpSuspendPolicy suspendPolicy = SP_NONE;
+  if (matchCount != 0) {
+    LOG(VERBOSE) << "EVENT: " << matchList[0]->eventKind << "(" << matchCount << " total) "
+                 << "thread=" << (void*) basket.threadId << ")";
+
+    suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+    LOG(VERBOSE) << "  suspendPolicy=" << suspendPolicy;
+
+    pReq = eventPrep();
+    expandBufAdd1(pReq, suspendPolicy);
+    expandBufAdd4BE(pReq, matchCount);
+
+    for (int i = 0; i < matchCount; i++) {
+      expandBufAdd1(pReq, matchList[i]->eventKind);
+      expandBufAdd4BE(pReq, matchList[i]->requestId);
+      expandBufAdd8BE(pReq, basket.threadId);
+    }
+  }
+
+  cleanupMatchList(state, matchList, matchCount);
+  unlockEventMutex(state);
+
+  /* send request and possibly suspend ourselves */
+  if (pReq != NULL) {
+    int oldStatus = Dbg::ThreadWaiting();
+    if (suspendPolicy != SP_NONE) {
+      SetWaitForEventThread(state, basket.threadId);
+    }
+    eventFinish(state, pReq);
+
+    suspendByPolicy(state, suspendPolicy);
+    Dbg::ThreadContinuing(oldStatus);
+  }
+
+  return matchCount != 0;
+}
+
+/*
+ * Send a polite "VM is dying" message to the debugger.
+ *
+ * Skips the usual "event token" stuff.
+ */
+bool PostVMDeath(JdwpState* state) {
+  LOG(VERBOSE) << "EVENT: " << EK_VM_DEATH;
+
+  ExpandBuf* pReq = eventPrep();
+  expandBufAdd1(pReq, SP_NONE);
+  expandBufAdd4BE(pReq, 1);
+
+  expandBufAdd1(pReq, EK_VM_DEATH);
+  expandBufAdd4BE(pReq, 0);
+  eventFinish(state, pReq);
+  return true;
+}
+
+/*
+ * An exception has been thrown.  It may or may not have been caught.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, LocationOnly,
+ *    ExceptionOnly, InstanceOnly
+ *
+ * The "exceptionId" has not been added to the GC-visible object registry,
+ * because there's a pretty good chance that we're not going to send it
+ * up the debugger.
+ */
+bool PostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+    ObjectId exceptionId, RefTypeId exceptionClassId,
+    const JdwpLocation* pCatchLoc, ObjectId thisPtr)
+{
+  ModBasket basket;
+  char* nameAlloc = NULL;
+
+  memset(&basket, 0, sizeof(basket));
+  basket.pLoc = pThrowLoc;
+  basket.classId = pThrowLoc->classId;
+  basket.threadId = Dbg::GetThreadSelfId();
+  basket.className = nameAlloc = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
+  basket.excepClassId = exceptionClassId;
+  basket.caught = (pCatchLoc->classId != 0);
+  basket.thisPtr = thisPtr;
+
+  /* don't try to post an exception caused by the debugger */
+  if (invokeInProgress(state)) {
+    LOG(VERBOSE) << "Not posting exception hit during invoke (" << basket.className << ")";
+    free(nameAlloc);
+    return false;
+  }
+
+  /* don't allow the list to be updated while we scan it */
+  lockEventMutex(state);
+
+  JdwpEvent** matchList = allocMatchList(state);
+  int matchCount = 0;
+
+  findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);
+
+  ExpandBuf* pReq = NULL;
+  JdwpSuspendPolicy suspendPolicy = SP_NONE;
+  if (matchCount != 0) {
+    LOG(VERBOSE) << "EVENT: " << matchList[0]->eventKind << "(" << matchCount << " total)"
+                 << " thread=" << (void*) basket.threadId
+                 << " exceptId=" << (void*) exceptionId
+                 << " caught=" << basket.caught << ")";
+    LOG(VERBOSE) << StringPrintf("  throw: %d %llx %x %lld (%s.%s)", pThrowLoc->typeTag,
+        pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
+        Dbg::GetClassDescriptor(pThrowLoc->classId),
+        Dbg::GetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
+    if (pCatchLoc->classId == 0) {
+      LOG(VERBOSE) << "  catch: (not caught)";
+    } else {
+      LOG(VERBOSE) << StringPrintf("  catch: %d %llx %x %lld (%s.%s)", pCatchLoc->typeTag,
+          pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
+          Dbg::GetClassDescriptor(pCatchLoc->classId),
+          Dbg::GetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
+    }
+
+    suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+    LOG(VERBOSE) << "  suspendPolicy=" << suspendPolicy;
+
+    pReq = eventPrep();
+    expandBufAdd1(pReq, suspendPolicy);
+    expandBufAdd4BE(pReq, matchCount);
+
+    for (int i = 0; i < matchCount; i++) {
+      expandBufAdd1(pReq, matchList[i]->eventKind);
+      expandBufAdd4BE(pReq, matchList[i]->requestId);
+      expandBufAdd8BE(pReq, basket.threadId);
+
+      AddLocation(pReq, pThrowLoc);
+      expandBufAdd1(pReq, JT_OBJECT);
+      expandBufAdd8BE(pReq, exceptionId);
+      AddLocation(pReq, pCatchLoc);
+    }
+
+    /* don't let the GC discard it */
+    Dbg::RegisterObjectId(exceptionId);
+  }
+
+  cleanupMatchList(state, matchList, matchCount);
+  unlockEventMutex(state);
+
+  /* send request and possibly suspend ourselves */
+  if (pReq != NULL) {
+    int oldStatus = Dbg::ThreadWaiting();
+    if (suspendPolicy != SP_NONE) {
+      SetWaitForEventThread(state, basket.threadId);
+    }
+
+    eventFinish(state, pReq);
+
+    suspendByPolicy(state, suspendPolicy);
+    Dbg::ThreadContinuing(oldStatus);
+  }
+
+  free(nameAlloc);
+  return matchCount != 0;
+}
+
+/*
+ * Announce that a class has been loaded.
+ *
+ * Valid mods:
+ *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
+ */
+bool PostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId, const char* signature, int status) {
+  ModBasket basket;
+  char* nameAlloc = NULL;
+
+  memset(&basket, 0, sizeof(basket));
+  basket.classId = refTypeId;
+  basket.threadId = Dbg::GetThreadSelfId();
+  basket.className = nameAlloc = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
+
+  /* suppress class prep caused by debugger */
+  if (invokeInProgress(state)) {
+    LOG(VERBOSE) << "Not posting class prep caused by invoke (" << basket.className << ")";
+    free(nameAlloc);
+    return false;
+  }
+
+  /* don't allow the list to be updated while we scan it */
+  lockEventMutex(state);
+
+  JdwpEvent** matchList = allocMatchList(state);
+  int matchCount = 0;
+
+  findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList, &matchCount);
+
+  ExpandBuf* pReq = NULL;
+  JdwpSuspendPolicy suspendPolicy = SP_NONE;
+  if (matchCount != 0) {
+    LOG(VERBOSE) << "EVENT: " << matchList[0]->eventKind << "(" << matchCount << " total) "
+                 << "thread=" << (void*) basket.threadId << ")";
+
+    suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+    LOG(VERBOSE) << "  suspendPolicy=" << suspendPolicy;
+
+    if (basket.threadId == state->debugThreadId) {
+      /*
+       * JDWP says that, for a class prep in the debugger thread, we
+       * should set threadId to null and if any threads were supposed
+       * to be suspended then we suspend all other threads.
+       */
+      LOG(VERBOSE) << "  NOTE: class prepare in debugger thread!";
+      basket.threadId = 0;
+      if (suspendPolicy == SP_EVENT_THREAD) {
+        suspendPolicy = SP_ALL;
+      }
+    }
+
+    pReq = eventPrep();
+    expandBufAdd1(pReq, suspendPolicy);
+    expandBufAdd4BE(pReq, matchCount);
+
+    for (int i = 0; i < matchCount; i++) {
+      expandBufAdd1(pReq, matchList[i]->eventKind);
+      expandBufAdd4BE(pReq, matchList[i]->requestId);
+      expandBufAdd8BE(pReq, basket.threadId);
+
+      expandBufAdd1(pReq, tag);
+      expandBufAdd8BE(pReq, refTypeId);
+      expandBufAddUtf8String(pReq, (const uint8_t*) signature);
+      expandBufAdd4BE(pReq, status);
+    }
+  }
+
+  cleanupMatchList(state, matchList, matchCount);
+
+  unlockEventMutex(state);
+
+  /* send request and possibly suspend ourselves */
+  if (pReq != NULL) {
+    int oldStatus = Dbg::ThreadWaiting();
+    if (suspendPolicy != SP_NONE) {
+      SetWaitForEventThread(state, basket.threadId);
+    }
+    eventFinish(state, pReq);
+
+    suspendByPolicy(state, suspendPolicy);
+    Dbg::ThreadContinuing(oldStatus);
+  }
+
+  free(nameAlloc);
+  return matchCount != 0;
+}
+
+bool SendBufferedRequest(JdwpState* state, const iovec* iov, int iovcnt) {
+  return (*state->transport->sendBufferedRequest)(state, iov, iovcnt);
+}
+
+/*
+ * Send up a chunk of DDM data.
+ *
+ * While this takes the form of a JDWP "event", it doesn't interact with
+ * other debugger traffic, and can't suspend the VM, so we skip all of
+ * the fun event token gymnastics.
+ */
+void DdmSendChunkV(JdwpState* state, int type, const iovec* iov, int iovcnt) {
+  uint8_t header[kJDWPHeaderLen + 8];
+  size_t dataLen = 0;
+
+  CHECK(iov != NULL);
+  CHECK(iovcnt > 0 && iovcnt < 10);
+
+  /*
+   * "Wrap" the contents of the iovec with a JDWP/DDMS header.  We do
+   * this by creating a new copy of the vector with space for the header.
+   */
+  iovec wrapiov[iovcnt+1];
+  for (int i = 0; i < iovcnt; i++) {
+    wrapiov[i+1].iov_base = iov[i].iov_base;
+    wrapiov[i+1].iov_len = iov[i].iov_len;
+    dataLen += iov[i].iov_len;
+  }
+
+  /* form the header (JDWP plus DDMS) */
+  set4BE(header, sizeof(header) + dataLen);
+  set4BE(header+4, NextRequestSerial(state));
+  set1(header+8, 0);     /* flags */
+  set1(header+9, kJDWPDdmCmdSet);
+  set1(header+10, kJDWPDdmCmd);
+  set4BE(header+11, type);
+  set4BE(header+15, dataLen);
+
+  wrapiov[0].iov_base = header;
+  wrapiov[0].iov_len = sizeof(header);
+
+  /*
+   * Make sure we're in VMWAIT in case the write blocks.
+   */
+  int oldStatus = Dbg::ThreadWaiting();
+  SendBufferedRequest(state, wrapiov, iovcnt+1);
+  Dbg::ThreadContinuing(oldStatus);
+}
+
+}  // namespace JDWP
+
+}  // namespace art