Implement enough JDWP functionality that we can attach jdb.
You can also try "classes" and "classpath", though anything else
deadlocks because we're suspended but jdb thinks we aren't. I don't
think that's a new bug with this patch, though, so I'll look at that
next.
Change-Id: I54456b6a7fe72642be696c66aa485dc0c8a7f913
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 505f86f..3e547e4 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -818,6 +818,21 @@
visitor(array_iftable_, arg);
}
+void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) const {
+ MutexLock mu(classes_lock_);
+ typedef Table::const_iterator It; // TODO: C++0x auto
+ for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) {
+ if (!visitor(it->second, arg)) {
+ return;
+ }
+ }
+ for (It it = image_classes_.begin(), end = image_classes_.end(); it != end; ++it) {
+ if (!visitor(it->second, arg)) {
+ return;
+ }
+ }
+}
+
ClassLinker::~ClassLinker() {
String::ResetClass();
Field::ResetClass();
diff --git a/src/class_linker.h b/src/class_linker.h
index 478a260..5032d9f 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -40,6 +40,8 @@
class InternTable;
class ObjectLock;
+typedef bool (ClassVisitor)(Class* c, void* arg);
+
class ClassLinker {
public:
// Creates the class linker by boot strapping from dex files.
@@ -205,6 +207,8 @@
return boot_class_path_;
}
+ void VisitClasses(ClassVisitor* visitor, void* arg) const;
+
void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
const DexFile& FindDexFile(const DexCache* dex_cache) const;
diff --git a/src/debugger.cc b/src/debugger.cc
index 6d3f80c..cdb17d3 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -64,6 +64,13 @@
return map_.find(id) != map_.end();
}
+ template<typename T> T Get(JDWP::ObjectId id) {
+ MutexLock mu(lock_);
+ typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
+ It it = map_.find(id);
+ return (it != map_.end()) ? reinterpret_cast<T>(it->second) : NULL;
+ }
+
void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
MutexLock mu(lock_);
typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
@@ -265,7 +272,7 @@
// If a debugger has already attached, send the "welcome" message.
// This may cause us to suspend all threads.
if (gJdwpState->IsActive()) {
- //ScopedThreadStateChange(Thread::Current(), Thread::kRunnable);
+ //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
if (!gJdwpState->PostVMStart()) {
LOG(WARNING) << "failed to post 'start' message to debugger";
}
@@ -315,8 +322,21 @@
gDebuggerConnected = true;
}
-void Dbg::Active() {
- UNIMPLEMENTED(FATAL);
+void Dbg::GoActive() {
+ // Enable all debugging features, including scans for breakpoints.
+ // This is a no-op if we're already active.
+ // Only called from the JDWP handler thread.
+ if (gDebuggerActive) {
+ return;
+ }
+
+ LOG(INFO) << "Debugger is active";
+
+ // TODO: CHECK we don't have any outstanding breakpoints.
+
+ gDebuggerActive = true;
+
+ //dvmEnableAllSubMode(kSubModeDebuggerActive);
}
void Dbg::Disconnected() {
@@ -369,9 +389,9 @@
}
}
-const char* Dbg::GetClassDescriptor(JDWP::RefTypeId id) {
- UNIMPLEMENTED(FATAL);
- return NULL;
+std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
+ Class* c = gRegistry->Get<Class*>(classId);
+ return c->GetDescriptor()->ToModifiedUtf8();
}
JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
@@ -399,16 +419,55 @@
return false;
}
-void Dbg::GetClassList(uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
- UNIMPLEMENTED(FATAL);
+void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
+ // Get the complete list of reference classes (i.e. all classes except
+ // the primitive types).
+ // Returns a newly-allocated buffer full of RefTypeId values.
+ struct ClassListCreator {
+ static bool Visit(Class* c, void* arg) {
+ return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
+ }
+
+ bool Visit(Class* c) {
+ if (!c->IsPrimitive()) {
+ classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
+ }
+ return true;
+ }
+
+ std::vector<JDWP::RefTypeId> classes;
+ };
+
+ ClassListCreator clc;
+ Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
+ *pClassCount = clc.classes.size();
+ *pClasses = new JDWP::RefTypeId[clc.classes.size()];
+ for (size_t i = 0; i < clc.classes.size(); ++i) {
+ (*pClasses)[i] = clc.classes[i];
+ }
}
void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
UNIMPLEMENTED(FATAL);
}
-void Dbg::GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, const char** pSignature) {
- UNIMPLEMENTED(FATAL);
+void Dbg::GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
+ Class* c = gRegistry->Get<Class*>(classId);
+ if (c->IsArrayClass()) {
+ *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
+ *pTypeTag = JDWP::TT_ARRAY;
+ } else {
+ if (c->IsErroneous()) {
+ *pStatus = JDWP::CS_ERROR;
+ } else {
+ *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
+ }
+ *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+ }
+
+ if (pDescriptor != NULL) {
+ *pDescriptor = c->GetDescriptor()->ToModifiedUtf8();
+ }
}
bool Dbg::FindLoadedClassBySignature(const char* classDescriptor, JDWP::RefTypeId* pRefTypeId) {
@@ -598,12 +657,52 @@
//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
+void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
+ struct ThreadListVisitor {
+ static void Visit(Thread* t, void* arg) {
+ reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
+ }
+
+ void Visit(Thread* t) {
+ if (t == Dbg::GetDebugThread()) {
+ // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
+ // query all threads, so it's easier if we just don't tell them about this thread.
+ return;
+ }
+ if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
+ threads.push_back(gRegistry->Add(t->GetPeer()));
+ }
+ }
+
+ Object* thread_group;
+ std::vector<JDWP::ObjectId> threads;
+ };
+
+ ThreadListVisitor tlv;
+ tlv.thread_group = thread_group;
+
+ {
+ ScopedThreadListLock thread_list_lock;
+ Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
+ }
+
+ *pThreadCount = tlv.threads.size();
+ if (*pThreadCount == 0) {
+ *ppThreadIds = NULL;
+ } else {
+ *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
+ for (size_t i = 0; i < *pThreadCount; ++i) {
+ (*ppThreadIds)[i] = tlv.threads[i];
+ }
+ }
+}
+
void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
- UNIMPLEMENTED(FATAL);
+ GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
}
void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
- UNIMPLEMENTED(FATAL);
+ GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
}
int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
@@ -621,6 +720,7 @@
}
void Dbg::SuspendVM() {
+ ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable); // TODO: do we really want to change back? should the JDWP thread be Runnable usually?
Runtime::Current()->GetThreadList()->SuspendAll(true);
}
@@ -800,7 +900,7 @@
return true;
}
-void DdmBroadcast(bool connect) {
+void Dbg::DdmBroadcast(bool connect) {
LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
Thread* self = Thread::Current();
@@ -822,11 +922,11 @@
}
void Dbg::DdmConnected() {
- DdmBroadcast(true);
+ Dbg::DdmBroadcast(true);
}
void Dbg::DdmDisconnected() {
- DdmBroadcast(false);
+ Dbg::DdmBroadcast(false);
gDdmThreadNotification = false;
}
@@ -859,7 +959,7 @@
}
}
-void DdmSendThreadStartCallback(Thread* t, void*) {
+static void DdmSendThreadStartCallback(Thread* t, void*) {
Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
}
@@ -875,7 +975,7 @@
}
}
-void PostThreadStartOrStop(Thread* t, uint32_t type) {
+void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
if (gDebuggerActive) {
JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
@@ -884,11 +984,11 @@
}
void Dbg::PostThreadStart(Thread* t) {
- PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
+ Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
}
void Dbg::PostThreadDeath(Thread* t) {
- PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
+ Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
}
void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
@@ -1067,7 +1167,13 @@
Reset();
}
+ static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
+ reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
+ }
+
private:
+ enum { ALLOCATION_UNIT_SIZE = 8 };
+
void Reset() {
p = &buf[0];
totalAllocationUnits = 0;
@@ -1075,105 +1181,92 @@
pieceLenField = NULL;
}
- DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
-};
+ void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
+ CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
-#define ALLOCATION_UNIT_SIZE 8
+ /* Make sure there's enough room left in the buffer.
+ * We need to use two bytes for every fractional 256
+ * allocation units used by the chunk.
+ */
+ {
+ size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
+ size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
+ if (bytesLeft < needed) {
+ Flush();
+ }
-uint8_t ExamineObject(const Object* o, bool is_native_heap) {
- if (o == NULL) {
- return HPSG_STATE(SOLIDITY_FREE, 0);
+ bytesLeft = buf.size() - (size_t)(p - &buf[0]);
+ if (bytesLeft < needed) {
+ LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
+ return;
+ }
+ }
+
+ // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
+ EnsureHeader(chunk_ptr);
+
+ // Determine the type of this chunk.
+ // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
+ // If it's the same, we should combine them.
+ uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
+
+ // Write out the chunk description.
+ chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
+ totalAllocationUnits += chunk_len;
+ while (chunk_len > 256) {
+ *p++ = state | HPSG_PARTIAL;
+ *p++ = 255; // length - 1
+ chunk_len -= 256;
+ }
+ *p++ = state;
+ *p++ = chunk_len - 1;
}
- // It's an allocated chunk. Figure out what it is.
+ uint8_t ExamineObject(const Object* o, bool is_native_heap) {
+ if (o == NULL) {
+ return HPSG_STATE(SOLIDITY_FREE, 0);
+ }
- // If we're looking at the native heap, we'll just return
- // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
- if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
- return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
- }
+ // It's an allocated chunk. Figure out what it is.
- Class* c = o->GetClass();
- if (c == NULL) {
- // The object was probably just created but hasn't been initialized yet.
+ // If we're looking at the native heap, we'll just return
+ // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
+ if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
+ return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+ }
+
+ Class* c = o->GetClass();
+ if (c == NULL) {
+ // The object was probably just created but hasn't been initialized yet.
+ return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+ }
+
+ if (!Heap::IsHeapAddress(c)) {
+ LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
+ return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+ }
+
+ if (c->IsClassClass()) {
+ return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
+ }
+
+ if (c->IsArrayClass()) {
+ if (o->IsObjectArray()) {
+ return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+ }
+ switch (c->GetComponentSize()) {
+ case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
+ case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
+ case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+ case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
+ }
+ }
+
return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
}
- if (!Heap::IsHeapAddress(c)) {
- LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
- return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
- }
-
- if (c->IsClassClass()) {
- return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
- }
-
- if (c->IsArrayClass()) {
- if (o->IsObjectArray()) {
- return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
- }
- switch (c->GetComponentSize()) {
- case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
- case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
- case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
- case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
- }
- }
-
- return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
-}
-
-static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
- HeapChunkContext* context = reinterpret_cast<HeapChunkContext*>(arg);
-
- CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
-
- /* Make sure there's enough room left in the buffer.
- * We need to use two bytes for every fractional 256
- * allocation units used by the chunk.
- */
- {
- size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
- size_t bytesLeft = context->buf.size() - (size_t)(context->p - &context->buf[0]);
- if (bytesLeft < needed) {
- context->Flush();
- }
-
- bytesLeft = context->buf.size() - (size_t)(context->p - &context->buf[0]);
- if (bytesLeft < needed) {
- LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
- return;
- }
- }
-
- // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
- context->EnsureHeader(chunk_ptr);
-
- // Determine the type of this chunk.
- // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
- // If it's the same, we should combine them.
- uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (context->type == CHUNK_TYPE("NHSG")));
-
- // Write out the chunk description.
- chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
- context->totalAllocationUnits += chunk_len;
- while (chunk_len > 256) {
- *context->p++ = state | HPSG_PARTIAL;
- *context->p++ = 255; // length - 1
- chunk_len -= 256;
- }
- *context->p++ = state;
- *context->p++ = chunk_len - 1;
-}
-
-static void WalkHeap(bool merge, bool native) {
- HeapChunkContext context(merge, native);
- if (native) {
- dlmalloc_walk_heap(HeapChunkCallback, &context);
- } else {
- Heap::WalkHeap(HeapChunkCallback, &context);
- }
-}
+ DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
+};
void Dbg::DdmSendHeapSegments(bool native) {
Dbg::HpsgWhen when;
@@ -1198,7 +1291,12 @@
Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
// Send a series of heap segment chunks.
- WalkHeap((what == HPSG_WHAT_MERGED_OBJECTS), native);
+ HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
+ if (native) {
+ dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
+ } else {
+ Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
+ }
// Finally, send a heap end chunk.
Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
diff --git a/src/debugger.h b/src/debugger.h
index 35ef619..c2e8661 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -88,7 +88,7 @@
* when the debugger attaches.
*/
static void Connected();
- static void Active();
+ static void GoActive();
static void Disconnected();
/*
@@ -125,7 +125,7 @@
/*
* Class, Object, Array
*/
- static const char* GetClassDescriptor(JDWP::RefTypeId id);
+ static std::string GetClassDescriptor(JDWP::RefTypeId id);
static JDWP::ObjectId GetClassObject(JDWP::RefTypeId id);
static JDWP::RefTypeId GetSuperclass(JDWP::RefTypeId id);
static JDWP::ObjectId GetClassLoader(JDWP::RefTypeId id);
@@ -133,7 +133,7 @@
static bool IsInterface(JDWP::RefTypeId id);
static void GetClassList(uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf);
static void GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf);
- static void GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, const char** pSignature);
+ static void GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, std::string* pDescriptor);
static bool FindLoadedClassBySignature(const char* classDescriptor, JDWP::RefTypeId* pRefTypeId);
static void GetObjectType(JDWP::ObjectId objectId, uint8_t* pRefTypeTag, JDWP::RefTypeId* pRefTypeId);
static uint8_t GetClassObjectType(JDWP::RefTypeId refTypeId);
@@ -273,6 +273,10 @@
static void DdmSendHeapSegments(bool native);
private:
+ static void DdmBroadcast(bool);
+ static void GetThreadGroupThreadsImpl(Object*, JDWP::ObjectId**, uint32_t*);
+ static void PostThreadStartOrStop(Thread*, uint32_t);
+
static AllocRecord* recent_allocation_records_;
};
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index 6b5ef8c..8e0d4c3 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -109,7 +109,7 @@
*/
struct ModBasket {
const JdwpLocation* pLoc; /* LocationOnly */
- const char* className; /* ClassMatch/ClassExclude */
+ std::string className; /* ClassMatch/ClassExclude */
ObjectId threadId; /* ThreadOnly */
RefTypeId classId; /* ClassOnly */
RefTypeId excepClassId; /* ExceptionOnly */
@@ -374,23 +374,22 @@
*
* ("Restricted name globbing" might have been a better term.)
*/
-static bool patternMatch(const char* pattern, const char* target) {
- int patLen = strlen(pattern);
+static bool patternMatch(const char* pattern, const std::string& target) {
+ size_t 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)) << "'";
+ LOG(ERROR) << ">>> comparing '" << (pattern + 1) << "' to '" << (target.c_str() + (target.size()-patLen)) << "'";
- if (targetLen < patLen) {
+ if (target.size() < patLen) {
return false;
}
- return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
+ return strcmp(pattern+1, target.c_str() + (target.size()-patLen)) == 0;
} else if (pattern[patLen-1] == '*') {
- return strncmp(pattern, target, patLen-1) == 0;
+ return strncmp(pattern, target.c_str(), patLen-1) == 0;
} else {
- return strcmp(pattern, target) == 0;
+ return strcmp(pattern, target.c_str()) == 0;
}
}
@@ -734,8 +733,8 @@
// 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);
+std::string dvmDescriptorToName(const std::string& descriptor) {
+ return descriptor;
}
/*
@@ -761,14 +760,13 @@
*/
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));
+ basket.className = dvmDescriptorToName(Dbg::GetClassDescriptor(pLoc->classId));
/*
* On rare occasions we may need to execute interpreted code in the VM
@@ -778,7 +776,6 @@
*/
if (basket.threadId == state->debugThreadId) {
LOG(VERBOSE) << "Ignoring location event in JDWP thread";
- free(nameAlloc);
return false;
}
@@ -793,7 +790,6 @@
*/
if (invokeInProgress(state)) {
LOG(VERBOSE) << "Not checking breakpoints during invoke (" << basket.className << ")";
- free(nameAlloc);
return false;
}
@@ -854,7 +850,6 @@
Dbg::ThreadContinuing(old_state);
}
- free(nameAlloc);
return matchCount != 0;
}
@@ -963,13 +958,12 @@
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.className = dvmDescriptorToName(Dbg::GetClassDescriptor(basket.classId));
basket.excepClassId = exceptionClassId;
basket.caught = (pCatchLoc->classId != 0);
basket.thisPtr = thisPtr;
@@ -977,7 +971,6 @@
/* 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;
}
@@ -998,14 +991,14 @@
<< " 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::GetClassDescriptor(pThrowLoc->classId).c_str(),
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::GetClassDescriptor(pCatchLoc->classId).c_str(),
Dbg::GetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
}
@@ -1047,7 +1040,6 @@
Dbg::ThreadContinuing(old_state);
}
- free(nameAlloc);
return matchCount != 0;
}
@@ -1059,17 +1051,15 @@
*/
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));
+ basket.className = 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;
}
@@ -1114,7 +1104,7 @@
expandBufAdd1(pReq, tag);
expandBufAdd8BE(pReq, refTypeId);
- expandBufAddUtf8String(pReq, (const uint8_t*) signature);
+ expandBufAddUtf8String(pReq, signature);
expandBufAdd4BE(pReq, status);
}
}
@@ -1135,7 +1125,6 @@
Dbg::ThreadContinuing(old_state);
}
- free(nameAlloc);
return matchCount != 0;
}
diff --git a/src/jdwp/jdwp_expand_buf.cc b/src/jdwp/jdwp_expand_buf.cc
index f5e24b2..30ebf00 100644
--- a/src/jdwp/jdwp_expand_buf.cc
+++ b/src/jdwp/jdwp_expand_buf.cc
@@ -153,8 +153,7 @@
pBuf->curLen += sizeof(val);
}
-static void SetUtf8String(uint8_t* buf, const uint8_t* str) {
- uint32_t strLen = strlen((const char*)str);
+static void SetUtf8String(uint8_t* buf, const char* str, size_t strLen) {
Set4BE(buf, strLen);
memcpy(buf + sizeof(uint32_t), str, strLen);
}
@@ -167,11 +166,11 @@
* they can be null-terminated (either they don't have null bytes or they
* have stored null bytes in a multi-byte encoding).
*/
-void expandBufAddUtf8String(ExpandBuf* pBuf, const uint8_t* str) {
- int strLen = strlen((const char*)str);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const char* str) {
+ int strLen = strlen(str);
ensureSpace(pBuf, sizeof(uint32_t) + strLen);
- SetUtf8String(pBuf->storage + pBuf->curLen, str);
+ SetUtf8String(pBuf->storage + pBuf->curLen, str, strLen);
pBuf->curLen += sizeof(uint32_t) + strLen;
}
diff --git a/src/jdwp/jdwp_expand_buf.h b/src/jdwp/jdwp_expand_buf.h
index 2c19f54..287f05e 100644
--- a/src/jdwp/jdwp_expand_buf.h
+++ b/src/jdwp/jdwp_expand_buf.h
@@ -56,7 +56,7 @@
void expandBufAdd2BE(ExpandBuf* pBuf, uint16_t val);
void expandBufAdd4BE(ExpandBuf* pBuf, uint32_t val);
void expandBufAdd8BE(ExpandBuf* pBuf, uint64_t val);
-void expandBufAddUtf8String(ExpandBuf* pBuf, const uint8_t* str);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const char* str);
} // namespace JDWP
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 04c4734..e5e18bb 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -109,7 +109,7 @@
uint32_t numArgs = Read4BE(&buf);
LOG(VERBOSE) << StringPrintf(" --> threadId=%llx objectId=%llx", threadId, objectId);
- LOG(VERBOSE) << StringPrintf(" classId=%llx methodId=%x %s.%s", classId, methodId, Dbg::GetClassDescriptor(classId), Dbg::GetMethodName(classId, methodId));
+ LOG(VERBOSE) << StringPrintf(" classId=%llx methodId=%x %s.%s", classId, methodId, Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId));
LOG(VERBOSE) << StringPrintf(" %d args:", numArgs);
uint64_t* argArray = NULL;
@@ -178,14 +178,14 @@
static JdwpError handleVM_Version(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
/* text information on runtime version */
std::string version(StringPrintf("Android Runtime %s", Runtime::Current()->GetVersion()));
- expandBufAddUtf8String(pReply, (const uint8_t*) version.c_str());
+ expandBufAddUtf8String(pReply, version.c_str());
/* JDWP version numbers */
expandBufAdd4BE(pReply, 1); // major
expandBufAdd4BE(pReply, 5); // minor
/* VM JRE version */
- expandBufAddUtf8String(pReply, (const uint8_t*) "1.6.0"); /* e.g. 1.6.0_22 */
+ expandBufAddUtf8String(pReply, "1.6.0"); /* e.g. 1.6.0_22 */
/* target VM name */
- expandBufAddUtf8String(pReply, (const uint8_t*) "DalvikVM");
+ expandBufAddUtf8String(pReply, "DalvikVM");
return ERR_NONE;
}
@@ -382,10 +382,10 @@
uint32_t classPaths = 1;
uint32_t bootClassPaths = 0;
- expandBufAddUtf8String(pReply, (const uint8_t*) baseDir);
+ expandBufAddUtf8String(pReply, baseDir);
expandBufAdd4BE(pReply, classPaths);
for (uint32_t i = 0; i < classPaths; i++) {
- expandBufAddUtf8String(pReply, (const uint8_t*) ".");
+ expandBufAddUtf8String(pReply, ".");
}
expandBufAdd4BE(pReply, bootClassPaths);
@@ -450,16 +450,16 @@
expandBufAdd4BE(pReply, numClasses);
for (uint32_t i = 0; i < numClasses; i++) {
- static const uint8_t genericSignature[1] = "";
+ static const char genericSignature[1] = "";
uint8_t refTypeTag;
- const char* signature;
+ std::string descriptor;
uint32_t status;
- Dbg::GetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
+ Dbg::GetClassInfo(classRefBuf[i], &refTypeTag, &status, &descriptor);
expandBufAdd1(pReply, refTypeTag);
expandBufAddRefTypeId(pReply, classRefBuf[i]);
- expandBufAddUtf8String(pReply, (const uint8_t*) signature);
+ expandBufAddUtf8String(pReply, descriptor.c_str());
expandBufAddUtf8String(pReply, genericSignature);
expandBufAdd4BE(pReply, status);
}
@@ -478,7 +478,7 @@
LOG(VERBOSE) << StringPrintf(" Req for signature of refTypeId=0x%llx", refTypeId);
const char* signature = Dbg::GetSignature(refTypeId);
- expandBufAddUtf8String(pReply, (const uint8_t*) signature);
+ expandBufAddUtf8String(pReply, signature);
return ERR_NONE;
}
@@ -519,7 +519,7 @@
const char* fileName = Dbg::GetSourceFile(refTypeId);
if (fileName != NULL) {
- expandBufAddUtf8String(pReply, (const uint8_t*) fileName);
+ expandBufAddUtf8String(pReply, fileName);
return ERR_NONE;
} else {
return ERR_ABSENT_INFORMATION;
@@ -546,8 +546,7 @@
static JdwpError handleRT_Interfaces(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
RefTypeId refTypeId = ReadRefTypeId(&buf);
- LOG(VERBOSE) << StringPrintf(" Req for interfaces in %llx (%s)", refTypeId,
- Dbg::GetClassDescriptor(refTypeId));
+ LOG(VERBOSE) << StringPrintf(" Req for interfaces in %llx (%s)", refTypeId, Dbg::GetClassDescriptor(refTypeId).c_str());
Dbg::OutputAllInterfaces(refTypeId, pReply);
@@ -582,17 +581,17 @@
* Like RT_Signature but with the possibility of a "generic signature".
*/
static JdwpError handleRT_SignatureWithGeneric(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
- static const uint8_t genericSignature[1] = "";
+ static const char genericSignature[1] = "";
RefTypeId refTypeId = ReadRefTypeId(&buf);
LOG(VERBOSE) << StringPrintf(" Req for signature of refTypeId=0x%llx", refTypeId);
const char* signature = Dbg::GetSignature(refTypeId);
if (signature != NULL) {
- expandBufAddUtf8String(pReply, (const uint8_t*) signature);
+ expandBufAddUtf8String(pReply, signature);
} else {
LOG(WARNING) << StringPrintf("No signature for refTypeId=0x%llx", refTypeId);
- expandBufAddUtf8String(pReply, (const uint8_t*) "Lunknown;");
+ expandBufAddUtf8String(pReply, "Lunknown;");
}
expandBufAddUtf8String(pReply, genericSignature);
@@ -714,7 +713,7 @@
RefTypeId arrayTypeId = ReadRefTypeId(&buf);
uint32_t length = Read4BE(&buf);
- LOG(VERBOSE) << StringPrintf("Creating array %s[%u]", Dbg::GetClassDescriptor(arrayTypeId), length);
+ LOG(VERBOSE) << StringPrintf("Creating array %s[%u]", Dbg::GetClassDescriptor(arrayTypeId).c_str(), length);
ObjectId objectId = Dbg::CreateArrayObject(arrayTypeId, length);
if (objectId == 0) {
return ERR_OUT_OF_MEMORY;
@@ -731,7 +730,7 @@
RefTypeId refTypeId = ReadRefTypeId(&buf);
MethodId methodId = ReadMethodId(&buf);
- LOG(VERBOSE) << StringPrintf(" Req for line table in %s.%s", Dbg::GetClassDescriptor(refTypeId), Dbg::GetMethodName(refTypeId,methodId));
+ LOG(VERBOSE) << StringPrintf(" Req for line table in %s.%s", Dbg::GetClassDescriptor(refTypeId).c_str(), Dbg::GetMethodName(refTypeId,methodId));
Dbg::OutputLineTable(refTypeId, methodId, pReply);
@@ -745,9 +744,7 @@
RefTypeId classId = ReadRefTypeId(&buf);
MethodId methodId = ReadMethodId(&buf);
- LOG(VERBOSE) << StringPrintf(" Req for LocalVarTab in class=%s method=%s",
- Dbg::GetClassDescriptor(classId),
- Dbg::GetMethodName(classId, methodId));
+ LOG(VERBOSE) << StringPrintf(" Req for LocalVarTab in class=%s method=%s", Dbg::GetClassDescriptor(classId).c_str(), Dbg::GetMethodName(classId, methodId));
/*
* We could return ERR_ABSENT_INFORMATION here if the DEX file was
@@ -884,7 +881,7 @@
LOG(VERBOSE) << StringPrintf(" Req for str %llx --> '%s'", stringObject, str);
- expandBufAddUtf8String(pReply, (uint8_t*) str);
+ expandBufAddUtf8String(pReply, str);
free(str);
return ERR_NONE;
@@ -901,7 +898,7 @@
if (name == NULL) {
return ERR_INVALID_THREAD;
}
- expandBufAddUtf8String(pReply, (uint8_t*) name);
+ expandBufAddUtf8String(pReply, name);
free(name);
return ERR_NONE;
@@ -1086,9 +1083,9 @@
char* name = Dbg::GetThreadGroupName(threadGroupId);
if (name != NULL) {
- expandBufAddUtf8String(pReply, (uint8_t*) name);
+ expandBufAddUtf8String(pReply, name);
} else {
- expandBufAddUtf8String(pReply, (uint8_t*) "BAD-GROUP-ID");
+ expandBufAddUtf8String(pReply, "BAD-GROUP-ID");
LOG(VERBOSE) << StringPrintf("bad thread group ID");
}
@@ -1310,7 +1307,7 @@
case MK_CLASS_ONLY: /* for ClassPrepare, MethodEntry */
{
RefTypeId clazzId = ReadRefTypeId(&buf);
- LOG(VERBOSE) << StringPrintf(" ClassOnly: %llx (%s)", clazzId, Dbg::GetClassDescriptor(clazzId));
+ LOG(VERBOSE) << StringPrintf(" ClassOnly: %llx (%s)", clazzId, Dbg::GetClassDescriptor(clazzId).c_str());
pEvent->mods[idx].classOnly.refTypeId = clazzId;
}
break;
@@ -1356,7 +1353,7 @@
caught = Read1(&buf);
uncaught = Read1(&buf);
LOG(VERBOSE) << StringPrintf(" ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d",
- exceptionOrNull, (exceptionOrNull == 0) ? "null" : Dbg::GetClassDescriptor(exceptionOrNull), caught, uncaught);
+ exceptionOrNull, (exceptionOrNull == 0) ? "null" : Dbg::GetClassDescriptor(exceptionOrNull).c_str(), caught, uncaught);
pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
pEvent->mods[idx].exceptionOnly.caught = caught;
@@ -1524,7 +1521,7 @@
static JdwpError handleCOR_ReflectedType(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
RefTypeId classObjectId = ReadRefTypeId(&buf);
- LOG(VERBOSE) << StringPrintf(" Req for refTypeId for class=%llx (%s)", classObjectId, Dbg::GetClassDescriptor(classObjectId));
+ LOG(VERBOSE) << StringPrintf(" Req for refTypeId for class=%llx (%s)", classObjectId, Dbg::GetClassDescriptor(classObjectId).c_str());
/* just hand the type back to them */
if (Dbg::IsInterface(classObjectId)) {
@@ -1732,7 +1729,7 @@
* active debugger session, and zero out the last-activity timestamp
* so waitForDebugger() doesn't return if we stall for a bit here.
*/
- Dbg::Active();
+ Dbg::GoActive();
QuasiAtomicSwap64(0, &lastActivityWhen);
}
diff --git a/src/thread.cc b/src/thread.cc
index b8df84f..e6dc2ff 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -407,7 +407,7 @@
priority = gThread_priority->GetInt(peer_);
is_daemon = gThread_daemon->GetBoolean(peer_);
- Object* thread_group = gThread_group->GetObject(peer_);
+ Object* thread_group = GetThreadGroup();
if (thread_group != NULL) {
String* group_name_string = reinterpret_cast<String*>(gThreadGroup_name->GetObject(thread_group));
group_name = (group_name_string != NULL) ? group_name_string->ToModifiedUtf8() : "<null>";
@@ -840,7 +840,7 @@
Object* handler = gThread_uncaughtHandler->GetObject(peer_);
if (handler == NULL) {
// Otherwise use the thread group's default handler.
- handler = gThread_group->GetObject(peer_);
+ handler = GetThreadGroup();
}
// Call the handler.
@@ -854,10 +854,14 @@
ClearException();
}
+Object* Thread::GetThreadGroup() const {
+ return gThread_group->GetObject(peer_);
+}
+
void Thread::RemoveFromThreadGroup() {
// this.group.removeThread(this);
// group can be null if we're in the compiler or a test.
- Object* group = gThread_group->GetObject(peer_);
+ Object* group = GetThreadGroup();
if (group != NULL) {
Method* m = group->GetClass()->FindVirtualMethodForVirtualOrInterface(gThreadGroup_removeThread);
Object* args = peer_;
diff --git a/src/thread.h b/src/thread.h
index 7b8c8b9..a4fd3bc 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -233,6 +233,8 @@
return peer_;
}
+ Object* GetThreadGroup() const;
+
RuntimeStats* GetStats() {
return &stats_;
}