diff options
-rw-r--r-- | src/class_linker.cc | 15 | ||||
-rw-r--r-- | src/class_linker.h | 4 | ||||
-rw-r--r-- | src/debugger.cc | 302 | ||||
-rw-r--r-- | src/debugger.h | 10 | ||||
-rw-r--r-- | src/jdwp/jdwp_event.cc | 43 | ||||
-rw-r--r-- | src/jdwp/jdwp_expand_buf.cc | 9 | ||||
-rw-r--r-- | src/jdwp/jdwp_expand_buf.h | 2 | ||||
-rw-r--r-- | src/jdwp/jdwp_handler.cc | 57 | ||||
-rw-r--r-- | src/thread.cc | 10 | ||||
-rw-r--r-- | src/thread.h | 2 | ||||
-rwxr-xr-x | test/etc/push-and-run-test-jar | 4 |
11 files changed, 285 insertions, 173 deletions
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 @@ void ClassLinker::VisitRoots(Heap::RootVisitor* visitor, void* arg) const { 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 ClassLoader; 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 @@ class ClassLinker { 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 @@ class ObjectRegistry { 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 @@ void Dbg::StartJdwp() { // 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 @@ void Dbg::Connected() { 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 @@ void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) { } } -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 @@ bool Dbg::IsInterface(JDWP::RefTypeId id) { 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 @@ bool Dbg::IsSuspended(JDWP::ObjectId threadId) { //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 @@ JDWP::ObjectId Dbg::GetThreadSelfId() { } 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 @@ bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, 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 DdmBroadcast(bool connect) { } void Dbg::DdmConnected() { - DdmBroadcast(true); + Dbg::DdmBroadcast(true); } void Dbg::DdmDisconnected() { - DdmBroadcast(false); + Dbg::DdmBroadcast(false); gDdmThreadNotification = false; } @@ -859,7 +959,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { } } -void DdmSendThreadStartCallback(Thread* t, void*) { +static void DdmSendThreadStartCallback(Thread* t, void*) { Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR")); } @@ -875,7 +975,7 @@ void Dbg::DdmSetThreadNotification(bool enable) { } } -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 PostThreadStartOrStop(Thread* t, uint32_t type) { } 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 @@ struct HeapChunkContext { 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 @@ struct HeapChunkContext { pieceLenField = NULL; } - DISALLOW_COPY_AND_ASSIGN(HeapChunkContext); -}; - -#define ALLOCATION_UNIT_SIZE 8 - -uint8_t ExamineObject(const Object* o, bool is_native_heap) { - if (o == NULL) { - return HPSG_STATE(SOLIDITY_FREE, 0); - } - - // It's an allocated chunk. Figure out what it is. + 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); - // 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); - } + /* 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(); + } - if (c->IsArrayClass()) { - if (o->IsObjectArray()) { - return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); + 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; + } } - 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); + + // 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; } - return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); -} + uint8_t ExamineObject(const Object* o, bool is_native_heap) { + if (o == NULL) { + return HPSG_STATE(SOLIDITY_FREE, 0); + } -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); + // It's an allocated chunk. Figure out what it is. - CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U); + // 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); + } - /* 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(); + 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); } - 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; + if (!Heap::IsHeapAddress(c)) { + LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c; + return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN); } - } - // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range. - context->EnsureHeader(chunk_ptr); + if (c->IsClassClass()) { + return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT); + } - // 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"))); + 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); + } + } - // 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; + return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } - *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 @@ void Dbg::DdmSendHeapSegments(bool native) { 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 @@ public: * when the debugger attaches. */ static void Connected(); - static void Active(); + static void GoActive(); static void Disconnected(); /* @@ -125,7 +125,7 @@ public: /* * 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 @@ public: 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 @@ public: 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 @@ namespace JDWP { */ 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 @@ static void cleanupMatchList(JdwpState* state, JdwpEvent** matchList, int matchC * * ("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 @@ bool JdwpState::PostVMStart() { // 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 @@ char* dvmDescriptorToName(const char* descriptor) { */ 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 @@ bool PostLocationEvent(JdwpState* state, const JdwpLocation* pLoc, ObjectId this */ if (basket.threadId == state->debugThreadId) { LOG(VERBOSE) << "Ignoring location event in JDWP thread"; - free(nameAlloc); return false; } @@ -793,7 +790,6 @@ bool PostLocationEvent(JdwpState* state, const JdwpLocation* pLoc, ObjectId this */ if (invokeInProgress(state)) { LOG(VERBOSE) << "Not checking breakpoints during invoke (" << basket.className << ")"; - free(nameAlloc); return false; } @@ -854,7 +850,6 @@ bool PostLocationEvent(JdwpState* state, const JdwpLocation* pLoc, ObjectId this Dbg::ThreadContinuing(old_state); } - free(nameAlloc); return matchCount != 0; } @@ -963,13 +958,12 @@ bool PostException(JdwpState* state, const JdwpLocation* pThrowLoc, 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 @@ bool PostException(JdwpState* state, const JdwpLocation* pThrowLoc, /* 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 @@ bool PostException(JdwpState* state, const JdwpLocation* pThrowLoc, << " 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 @@ bool PostException(JdwpState* state, const JdwpLocation* pThrowLoc, Dbg::ThreadContinuing(old_state); } - free(nameAlloc); return matchCount != 0; } @@ -1059,17 +1051,15 @@ bool PostException(JdwpState* state, const JdwpLocation* pThrowLoc, */ 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 @@ bool PostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId, const char expandBufAdd1(pReq, tag); expandBufAdd8BE(pReq, refTypeId); - expandBufAddUtf8String(pReq, (const uint8_t*) signature); + expandBufAddUtf8String(pReq, signature); expandBufAdd4BE(pReq, status); } } @@ -1135,7 +1125,6 @@ bool PostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId, const char 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 @@ void expandBufAdd8BE(ExpandBuf* pBuf, uint64_t val) { 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 @@ static void SetUtf8String(uint8_t* buf, const uint8_t* str) { * 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 expandBufAdd1(ExpandBuf* pBuf, uint8_t val); 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 @@ static JdwpError finishInvoke(JdwpState* state, 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 @@ bail: 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 @@ static JdwpError handleVM_ClassPaths(JdwpState* state, const uint8_t* buf, int d 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 @@ static JdwpError handleVM_AllClassesWithGeneric(JdwpState* state, const uint8_t* 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 @@ static JdwpError handleRT_Signature(JdwpState* state, const uint8_t* buf, int da 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 @@ static JdwpError handleRT_SourceFile(JdwpState* state, const uint8_t* buf, int d 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_Status(JdwpState* state, const uint8_t* buf, int dataL 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 @@ static JdwpError handleRT_SourceDebugExtension(JdwpState* state, const uint8_t* * 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 @@ static JdwpError handleAT_newInstance(JdwpState* state, const uint8_t* buf, int 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 @@ static JdwpError handleM_LineTable(JdwpState* state, const uint8_t* buf, int dat 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 @@ static JdwpError handleM_VariableTableWithGeneric(JdwpState* state, const uint8_ 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 @@ static JdwpError handleSR_Value(JdwpState* state, const uint8_t* buf, int dataLe 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 @@ static JdwpError handleTR_Name(JdwpState* state, const uint8_t* buf, int dataLen if (name == NULL) { return ERR_INVALID_THREAD; } - expandBufAddUtf8String(pReply, (uint8_t*) name); + expandBufAddUtf8String(pReply, name); free(name); return ERR_NONE; @@ -1086,9 +1083,9 @@ static JdwpError handleTGR_Name(JdwpState* state, const uint8_t* buf, int dataLe 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 @@ static JdwpError handleER_Set(JdwpState* state, const uint8_t* buf, int dataLen, 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 @@ static JdwpError handleER_Set(JdwpState* state, const uint8_t* buf, int dataLen, 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 handleSF_ThisObject(JdwpState* state, const uint8_t* buf, int d 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 @@ void JdwpState::ProcessRequest(const JdwpReqHeader* pHeader, const uint8_t* buf, * 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 @@ void Thread::DumpState(std::ostream& os) const { 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 @@ void Thread::HandleUncaughtExceptions() { 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 @@ void Thread::HandleUncaughtExceptions() { 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 @@ class PACKED Thread { return peer_; } + Object* GetThreadGroup() const; + RuntimeStats* GetStats() { return &stats_; } diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar index 53d7a76..1d10e4d 100755 --- a/test/etc/push-and-run-test-jar +++ b/test/etc/push-and-run-test-jar @@ -99,11 +99,11 @@ fi if [ "$DEBUG" = "y" ]; then # This is for ddms: - DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y" + #DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y" # Connect by running 'ddms'. # This is for jdb: - #DEX_DEBUG="-agentlib:jdwp=transport=dt_socket,address=12345,server=y,suspend=y" + DEX_DEBUG="-agentlib:jdwp=transport=dt_socket,address=12345,server=y,suspend=y" # Connect thus: # adb forward tcp:12345 tcp:12345 # jdb -attach localhost:12345 |