diff options
Diffstat (limited to 'runtime/debugger.cc')
| -rw-r--r-- | runtime/debugger.cc | 179 |
1 files changed, 90 insertions, 89 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc index a9b70cbaa1..7cc52c3063 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -4238,10 +4238,15 @@ class HeapChunkContext { Reset(); } - static void HeapChunkCallback(void* start, void* end, size_t used_bytes, void* arg) + static void HeapChunkJavaCallback(void* start, void* end, size_t used_bytes, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { - reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(start, end, used_bytes); + reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkJavaCallback(start, end, used_bytes); + } + + static void HeapChunkNativeCallback(void* start, void* end, size_t used_bytes, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkNativeCallback(start, end, used_bytes); } private: @@ -4255,72 +4260,85 @@ class HeapChunkContext { pieceLenField_ = nullptr; } - void HeapChunkCallback(void* start, void* /*end*/, size_t used_bytes) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, - Locks::mutator_lock_) { + bool IsNative() const { + return type_ == CHUNK_TYPE("NHSG"); + } + + // Returns true if the object is not an empty chunk. + bool ProcessRecord(void* start, size_t used_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Note: heap call backs cannot manipulate the heap upon which they are crawling, care is taken // in the following code not to allocate memory, by ensuring buf_ is of the correct size if (used_bytes == 0) { - if (start == nullptr) { - // Reset for start of new heap. - startOfNextMemoryChunk_ = nullptr; - Flush(); - } - // Only process in use memory so that free region information - // also includes dlmalloc book keeping. - return; + if (start == nullptr) { + // Reset for start of new heap. + startOfNextMemoryChunk_ = nullptr; + Flush(); + } + // Only process in use memory so that free region information + // also includes dlmalloc book keeping. + return false; } - - /* If we're looking at the native heap, we'll just return - * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks - */ - bool native = type_ == CHUNK_TYPE("NHSG"); - - // TODO: I'm not sure using start of next chunk works well with multiple spaces. We shouldn't - // count gaps inbetween spaces as free memory. if (startOfNextMemoryChunk_ != nullptr) { - // Transmit any pending free memory. Native free memory of - // over kMaxFreeLen could be because of the use of mmaps, so - // don't report. If not free memory then start a new segment. - bool flush = true; - if (start > startOfNextMemoryChunk_) { - const size_t kMaxFreeLen = 2 * kPageSize; - void* freeStart = startOfNextMemoryChunk_; - void* freeEnd = start; - size_t freeLen = reinterpret_cast<char*>(freeEnd) - reinterpret_cast<char*>(freeStart); - if (!native || freeLen < kMaxFreeLen) { - AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), freeStart, freeLen); - flush = false; - } - } - if (flush) { - startOfNextMemoryChunk_ = nullptr; - Flush(); + // Transmit any pending free memory. Native free memory of over kMaxFreeLen could be because + // of the use of mmaps, so don't report. If not free memory then start a new segment. + bool flush = true; + if (start > startOfNextMemoryChunk_) { + const size_t kMaxFreeLen = 2 * kPageSize; + void* free_start = startOfNextMemoryChunk_; + void* free_end = start; + const size_t free_len = + reinterpret_cast<uintptr_t>(free_end) - reinterpret_cast<uintptr_t>(free_start); + if (!IsNative() || free_len < kMaxFreeLen) { + AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), free_start, free_len, IsNative()); + flush = false; } + } + if (flush) { + startOfNextMemoryChunk_ = nullptr; + Flush(); + } } - mirror::Object* obj = reinterpret_cast<mirror::Object*>(start); + return true; + } - // 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(obj, native); - AppendChunk(state, start, used_bytes + chunk_overhead_); - startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_; + void HeapChunkNativeCallback(void* start, void* /*end*/, size_t used_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (ProcessRecord(start, used_bytes)) { + uint8_t state = ExamineNativeObject(start); + AppendChunk(state, start, used_bytes + chunk_overhead_, true /*is_native*/); + startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_; + } } - void AppendChunk(uint8_t state, void* ptr, size_t length) + void HeapChunkJavaCallback(void* start, void* /*end*/, size_t used_bytes) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { + if (ProcessRecord(start, used_bytes)) { + // 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 = ExamineJavaObject(reinterpret_cast<mirror::Object*>(start)); + AppendChunk(state, start, used_bytes + chunk_overhead_, false /*is_native*/); + startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_; + } + } + + void AppendChunk(uint8_t state, void* ptr, size_t length, bool is_native) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // 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 plus // 17 bytes for any header. - size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17; - size_t bytesLeft = buf_.size() - (size_t)(p_ - &buf_[0]); - if (bytesLeft < needed) { + const size_t needed = ((RoundUp(length / ALLOCATION_UNIT_SIZE, 256) / 256) * 2) + 17; + size_t byte_left = &buf_.back() - p_; + if (byte_left < needed) { + if (is_native) { + // Cannot trigger memory allocation while walking native heap. + return; + } Flush(); } - bytesLeft = buf_.size() - (size_t)(p_ - &buf_[0]); - if (bytesLeft < needed) { + byte_left = &buf_.back() - p_; + if (byte_left < needed) { LOG(WARNING) << "Chunk is too big to transmit (chunk_len=" << length << ", " << needed << " bytes)"; return; @@ -4338,43 +4356,34 @@ class HeapChunkContext { *p_++ = length - 1; } - uint8_t ExamineObject(mirror::Object* o, bool is_native_heap) + uint8_t ExamineNativeObject(const void* p) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return p == nullptr ? HPSG_STATE(SOLIDITY_FREE, 0) : HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE); + } + + uint8_t ExamineJavaObject(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { if (o == nullptr) { return HPSG_STATE(SOLIDITY_FREE, 0); } - // It's an allocated chunk. Figure out what it is. - - // If we're looking at the native heap, we'll just return - // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks. - if (is_native_heap) { - return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE); - } - - if (!Runtime::Current()->GetHeap()->IsLiveObjectLocked(o)) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (!heap->IsLiveObjectLocked(o)) { + LOG(ERROR) << "Invalid object in managed heap: " << o; return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE); } - mirror::Class* c = o->GetClass(); if (c == nullptr) { // The object was probably just created but hasn't been initialized yet. return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } - - if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) { + if (!heap->IsValidObjectAddress(c)) { LOG(ERROR) << "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); @@ -4382,7 +4391,6 @@ class HeapChunkContext { case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8); } } - return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } @@ -4401,41 +4409,33 @@ class HeapChunkContext { static void BumpPointerSpaceCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { const size_t size = RoundUp(obj->SizeOf(), kObjectAlignment); - HeapChunkContext::HeapChunkCallback( + HeapChunkContext::HeapChunkJavaCallback( obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + size), size, arg); } void Dbg::DdmSendHeapSegments(bool native) { - Dbg::HpsgWhen when; - Dbg::HpsgWhat what; - if (!native) { - when = gDdmHpsgWhen; - what = gDdmHpsgWhat; - } else { - when = gDdmNhsgWhen; - what = gDdmNhsgWhat; - } + Dbg::HpsgWhen when = native ? gDdmNhsgWhen : gDdmHpsgWhen; + Dbg::HpsgWhat what = native ? gDdmNhsgWhat : gDdmHpsgWhat; if (when == HPSG_WHEN_NEVER) { return; } - // Figure out what kind of chunks we'll be sending. - CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what); + CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) + << static_cast<int>(what); // First, send a heap start chunk. uint8_t heap_id[4]; JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap). Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id); - Thread* self = Thread::Current(); - Locks::mutator_lock_->AssertSharedHeld(self); // Send a series of heap segment chunks. - HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native); + HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native); if (native) { #if defined(HAVE_ANDROID_OS) && defined(USE_DLMALLOC) - dlmalloc_inspect_all(HeapChunkContext::HeapChunkCallback, &context); + dlmalloc_inspect_all(HeapChunkContext::HeapChunkNativeCallback, &context); + HeapChunkContext::HeapChunkNativeCallback(nullptr, nullptr, 0, &context); // Indicate end of a space. #else UNIMPLEMENTED(WARNING) << "Native heap inspection is only supported with dlmalloc"; #endif @@ -4447,7 +4447,7 @@ void Dbg::DdmSendHeapSegments(bool native) { // dlmalloc's chunk header is 2 * sizeof(size_t), but if the previous chunk is in use for an // allocation then the first sizeof(size_t) may belong to it. context.SetChunkOverhead(sizeof(size_t)); - space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); + space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context); } else if (space->IsRosAllocSpace()) { context.SetChunkOverhead(0); // Need to acquire the mutator lock before the heap bitmap lock with exclusive access since @@ -4457,7 +4457,7 @@ void Dbg::DdmSendHeapSegments(bool native) { tl->SuspendAll(); { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); + space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context); } tl->ResumeAll(); self->TransitionFromSuspendedToRunnable(); @@ -4465,6 +4465,7 @@ void Dbg::DdmSendHeapSegments(bool native) { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); context.SetChunkOverhead(0); space->AsBumpPointerSpace()->Walk(BumpPointerSpaceCallback, &context); + HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context); } else { UNIMPLEMENTED(WARNING) << "Not counting objects in space " << *space; } @@ -4473,7 +4474,7 @@ void Dbg::DdmSendHeapSegments(bool native) { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); // Walk the large objects, these are not in the AllocSpace. context.SetChunkOverhead(0); - heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); + heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context); } // Finally, send a heap end chunk. |