Implement more DDMS support.
You can now launch DDMS without killing running oatexec processes...
Change-Id: I13e1d6df4f0cbd0c06b69471823a13e4e820b93b
diff --git a/build/Android.common.mk b/build/Android.common.mk
index c56bb0f..096350a 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -164,6 +164,7 @@
src/object.cc \
src/offsets.cc \
src/org_apache_harmony_dalvik_ddmc_DdmServer.cc \
+ src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc \
src/os_linux.cc \
src/reference_table.cc \
src/reflection.cc \
diff --git a/src/dalvik_system_VMDebug.cc b/src/dalvik_system_VMDebug.cc
index 4537a69..d5d0917 100644
--- a/src/dalvik_system_VMDebug.cc
+++ b/src/dalvik_system_VMDebug.cc
@@ -35,6 +35,7 @@
*/
jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
std::vector<std::string> features;
+ // TODO: we might need to uncomment these to make them work.
//features.push_back("method-trace-profiling");
//features.push_back("method-trace-profiling-streaming");
//features.push_back("hprof-heap-dump");
diff --git a/src/dalvik_system_VMRuntime.cc b/src/dalvik_system_VMRuntime.cc
index b6a8ed7..8334059 100644
--- a/src/dalvik_system_VMRuntime.cc
+++ b/src/dalvik_system_VMRuntime.cc
@@ -15,6 +15,7 @@
*/
#include "class_linker.h"
+#include "debugger.h"
#include "jni_internal.h"
#include "object.h"
#include "thread.h"
@@ -87,8 +88,7 @@
}
jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) {
- // TODO: debugger!
- return JNI_FALSE;
+ return Dbg::IsDebuggerConnected();
}
jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
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) {
diff --git a/src/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/src/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index b0aba2f..b46ee1f 100644
--- a/src/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/src/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -39,7 +39,7 @@
} // namespace
void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) {
- jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/ddmc/DdmServer", gMethods, NELEM(gMethods));
+ jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/ddmc/DdmServer", gMethods, NELEM(gMethods));
}
} // namespace art
diff --git a/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
new file mode 100644
index 0000000..16d3671
--- /dev/null
+++ b/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#include "debugger.h"
+#include "logging.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+#include "ScopedPrimitiveArray.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+namespace {
+
+static void DdmVmInternal_enableRecentAllocations(JNIEnv* env, jclass, jboolean enable) {
+ UNIMPLEMENTED(WARNING);
+ if (enable) {
+ //dvmEnableAllocTracker();
+ } else {
+ //dvmDisableAllocTracker();
+ }
+}
+
+static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv* env, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return NULL;
+ //ArrayObject* data = dvmDdmGetRecentAllocations();
+ //dvmReleaseTrackedAlloc(data, NULL);
+ //return reinterpret_cast<jbyteArray>(addLocalReference(env, data));
+}
+
+static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv* env, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return JNI_FALSE;
+ //return (gDvm.allocRecords != NULL);
+}
+
+/*
+ * Get a stack trace as an array of StackTraceElement objects. Returns
+ * NULL on failure, e.g. if the threadId couldn't be found.
+ */
+static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint threadId) {
+ UNIMPLEMENTED(WARNING);
+ return NULL;
+ //ArrayObject* trace = dvmDdmGetStackTraceById(threadId);
+ //return reinterpret_cast<jobjectArray>(addLocalReference(env, trace));
+}
+
+static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return NULL;
+ //ArrayObject* result = dvmDdmGenerateThreadStats();
+ //dvmReleaseTrackedAlloc(result, NULL);
+ //return reinterpret_cast<jbyteArray>(addLocalReference(env, result));
+}
+
+static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
+ UNIMPLEMENTED(WARNING);
+ return 0;
+ //return dvmDdmHandleHpifChunk(when);
+}
+
+/*
+ * Enable DDM heap notifications.
+ * @param when: 0=never (off), 1=during GC
+ * @param what: 0=merged objects, 1=distinct objects
+ * @param native: false=virtual heap, true=native heap
+ */
+static jboolean DdmVmInternal_heapSegmentNotify(JNIEnv* env, jclass, jint when, jint what, jboolean native) {
+ UNIMPLEMENTED(WARNING);
+ return JNI_FALSE;
+ //return dvmDdmHandleHpsgNhsgChunk(when, what, native);
+}
+
+static void DdmVmInternal_threadNotify(JNIEnv* env, jclass, jboolean enable) {
+ UNIMPLEMENTED(WARNING);
+ //dvmDdmSetThreadNotification(enable);
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(DdmVmInternal, enableRecentAllocations, "(Z)V"),
+ NATIVE_METHOD(DdmVmInternal, getRecentAllocations, "()[B"),
+ NATIVE_METHOD(DdmVmInternal, getRecentAllocationStatus, "()Z"),
+ NATIVE_METHOD(DdmVmInternal, getStackTraceById, "(I)[Ljava/lang/StackTraceElement;"),
+ NATIVE_METHOD(DdmVmInternal, getThreadStats, "()[B"),
+ NATIVE_METHOD(DdmVmInternal, heapInfoNotify, "(I)Z"),
+ NATIVE_METHOD(DdmVmInternal, heapSegmentNotify, "(IIZ)Z"),
+ NATIVE_METHOD(DdmVmInternal, threadNotify, "(Z)V"),
+};
+
+} // namespace
+
+void register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/ddmc/DdmVmInternal", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/runtime.cc b/src/runtime.cc
index c777fa1..7595ed8 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -570,7 +570,7 @@
REGISTER(register_java_lang_reflect_Proxy);
REGISTER(register_java_util_concurrent_atomic_AtomicLong);
REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmServer);
- //REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmVmInternal);
+ REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmVmInternal);
REGISTER(register_sun_misc_Unsafe);
#undef REGISTER
}
diff --git a/src/thread_list.cc b/src/thread_list.cc
index f386547..0be0f09 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -132,12 +132,13 @@
MutexLock mu(thread_suspend_count_lock_);
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
Thread* thread = *it;
- if (thread != self && thread != debug_thread) {
- if (verbose_) {
- LOG(INFO) << "requesting thread suspend: " << *thread;
- }
- ++thread->suspend_count_;
+ if (thread == self || (for_debugger && thread == debug_thread)) {
+ continue;
}
+ if (verbose_) {
+ LOG(INFO) << "requesting thread suspend: " << *thread;
+ }
+ ++thread->suspend_count_;
}
}
@@ -157,11 +158,12 @@
*/
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
Thread* thread = *it;
- if (thread != self && thread != debug_thread) {
- thread->WaitUntilSuspended();
- if (verbose_) {
- LOG(INFO) << "thread suspended: " << *thread;
- }
+ if (thread == self || (for_debugger && thread == debug_thread)) {
+ continue;
+ }
+ thread->WaitUntilSuspended();
+ if (verbose_) {
+ LOG(INFO) << "thread suspended: " << *thread;
}
}
@@ -257,12 +259,13 @@
MutexLock mu(thread_suspend_count_lock_);
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
Thread* thread = *it;
- if (thread != self && thread != debug_thread) {
- if (thread->suspend_count_ > 0) {
- --thread->suspend_count_;
- } else {
- LOG(WARNING) << *thread << " suspend count already zero";
- }
+ if (thread == self || (for_debugger && thread == debug_thread)) {
+ continue;
+ }
+ if (thread->suspend_count_ > 0) {
+ --thread->suspend_count_;
+ } else {
+ LOG(WARNING) << *thread << " suspend count already zero";
}
}
}