Basic DDMS thread support.
DDMS can now see our running threads...
Change-Id: I42d2fce4db9eb846fa0b4aac46ca6bb3443a6c9f
diff --git a/src/debugger.cc b/src/debugger.cc
index 07c445e..ca6643f 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -19,6 +19,7 @@
#include <sys/uio.h>
#include "ScopedPrimitiveArray.h"
+#include "stack_indirect_reference_table.h"
#include "thread_list.h"
namespace art {
@@ -62,6 +63,8 @@
static bool gDebuggerConnected; // debugger or DDMS is connected.
static bool gDebuggerActive; // debugger is making requests.
+static bool gDdmThreadNotification = false;
+
static ObjectRegistry* gRegistry = NULL;
/*
@@ -146,6 +149,8 @@
* "transport=dt_socket,address=8000,server=y,suspend=n"
*/
bool Dbg::ParseJdwpOptions(const std::string& options) {
+ LOG(VERBOSE) << "ParseJdwpOptions: " << options;
+
std::vector<std::string> pairs;
Split(options, ',', pairs);
@@ -559,20 +564,6 @@
UNIMPLEMENTED(FATAL);
}
-void Dbg::PostThreadStart(Thread* t) {
- if (!gDebuggerConnected) {
- return;
- }
- UNIMPLEMENTED(WARNING);
-}
-
-void Dbg::PostThreadDeath(Thread* t) {
- if (!gDebuggerConnected) {
- return;
- }
- UNIMPLEMENTED(WARNING);
-}
-
void Dbg::PostClassPrepare(Class* c) {
UNIMPLEMENTED(FATAL);
}
@@ -712,16 +703,101 @@
return true;
}
+void DdmBroadcast(bool connect) {
+ LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
+
+ Thread* self = Thread::Current();
+ if (self->GetState() != Thread::kRunnable) {
+ LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
+ /* try anyway? */
+ }
+
+ JNIEnv* env = self->GetJniEnv();
+ static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
+ static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
+ jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
+ env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
+ if (env->ExceptionCheck()) {
+ LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
void Dbg::DdmConnected() {
- LOG(VERBOSE) << "Broadcasting DDM connect...";
- //broadcast(CONNECTED);
- UNIMPLEMENTED(WARNING);
+ DdmBroadcast(true);
}
void Dbg::DdmDisconnected() {
- LOG(VERBOSE) << "Broadcasting DDM disconnect...";
- //broadcast(DISCONNECTED);
- //gDvm.ddmThreadNotification = false;
+ DdmBroadcast(false);
+ gDdmThreadNotification = false;
+}
+
+/*
+ * Send a notification when a thread starts or stops.
+ *
+ * Because we broadcast the full set of threads when the notifications are
+ * first enabled, it's possible for "thread" to be actively executing.
+ */
+void DdmSendThreadNotification(Thread* t, bool started) {
+ if (!gDdmThreadNotification) {
+ return;
+ }
+
+ if (started) {
+ SirtRef<String> name(t->GetName());
+ size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
+
+ size_t byte_count = char_count*2 + sizeof(uint32_t)*2;
+ std::vector<uint8_t> buf(byte_count);
+ JDWP::set4BE(&buf[0], t->GetThinLockId());
+ JDWP::set4BE(&buf[4], char_count);
+ if (char_count > 0) {
+ // Copy the UTF-16 string, transforming to big-endian.
+ const jchar* src = name->GetCharArray()->GetData();
+ jchar* dst = reinterpret_cast<jchar*>(&buf[8]);
+ while (char_count--) {
+ JDWP::set2BE(reinterpret_cast<uint8_t*>(dst++), *src++);
+ }
+ }
+ Dbg::DdmSendChunk(CHUNK_TYPE("THCR"), buf.size(), &buf[0]);
+ } else {
+ uint8_t buf[4];
+ JDWP::set4BE(&buf[0], t->GetThinLockId());
+ Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
+ }
+}
+
+void DdmSendThreadStartCallback(Thread* t) {
+ DdmSendThreadNotification(t, true);
+}
+
+void Dbg::DdmSetThreadNotification(bool enable) {
+ // We lock the thread list to avoid sending duplicate events or missing
+ // a thread change. We should be okay holding this lock while sending
+ // the messages out. (We have to hold it while accessing a live thread.)
+ ThreadListLock lock;
+
+ gDdmThreadNotification = enable;
+ if (enable) {
+ Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback);
+ }
+}
+
+void PostThreadStartOrStop(Thread* t, bool is_start) {
+ if (gDebuggerActive) {
+ JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
+ gJdwpState->PostThreadChange(id, is_start);
+ }
+ DdmSendThreadNotification(t, is_start);
+}
+
+void Dbg::PostThreadStart(Thread* t) {
+ PostThreadStartOrStop(t, true);
+}
+
+void Dbg::PostThreadDeath(Thread* t) {
+ PostThreadStartOrStop(t, false);
}
void Dbg::DdmSendChunk(int type, size_t byte_count, const uint8_t* buf) {
diff --git a/src/debugger.h b/src/debugger.h
index 6eabf33..111282c 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -227,6 +227,7 @@
/*
* DDM support.
*/
+ static void DdmSetThreadNotification(bool enable);
static bool DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen);
static void DdmConnected();
static void DdmDisconnected();
diff --git a/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 16d3671..fab4968 100644
--- a/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -85,8 +85,7 @@
}
static void DdmVmInternal_threadNotify(JNIEnv* env, jclass, jboolean enable) {
- UNIMPLEMENTED(WARNING);
- //dvmDdmSetThreadNotification(enable);
+ Dbg::DdmSetThreadNotification(enable);
}
static JNINativeMethod gMethods[] = {
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 0be0f09..26faede 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -382,6 +382,13 @@
thread_exit_cond_.Signal();
}
+void ThreadList::ForEach(void (*callback)(Thread*)) {
+ thread_list_lock_.AssertHeld();
+ for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
+ callback(*it);
+ }
+}
+
void ThreadList::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
ThreadListLocker locker(this);
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
diff --git a/src/thread_list.h b/src/thread_list.h
index baa7dad..6dd7455 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -41,6 +41,9 @@
void SuspendSelfForDebugger();
void RunWhileSuspended(Thread* thread, void (*callback)(void*), void* arg);
+ // Iterates over all the threads. The caller must hold the thread list lock.
+ void ForEach(void (*callback)(Thread*));
+
void Register();
void Unregister();