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) {