/* * Copyright (C) 2007 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. */ #define LOG_TAG "MemoryDealer" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class SimpleMemory : public MemoryBase { public: SimpleMemory(const sp& heap, ssize_t offset, size_t size); virtual ~SimpleMemory(); }; // ---------------------------------------------------------------------------- MemoryDealer::Allocation::Allocation( const sp& dealer, ssize_t offset, size_t size, const sp& memory) : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) { } MemoryDealer::Allocation::~Allocation() { if (mSize) { /* NOTE: it's VERY important to not free allocations of size 0 because * they're special as they don't have any record in the allocator * and could alias some real allocation (their offset is zero). */ mDealer->deallocate(mOffset); } } sp MemoryDealer::Allocation::getMemory( ssize_t* offset, size_t* size) const { return mMemory->getMemory(offset, size); } // ---------------------------------------------------------------------------- MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) : mHeap(new SharedHeap(size, flags, name)), mAllocator(new SimpleBestFitAllocator(size)) { } MemoryDealer::MemoryDealer(const sp& heap) : mHeap(heap), mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) { } MemoryDealer::MemoryDealer( const sp& heap, const sp& allocator) : mHeap(heap), mAllocator(allocator) { } MemoryDealer::~MemoryDealer() { } sp MemoryDealer::allocate(size_t size, uint32_t flags) { sp memory; const ssize_t offset = allocator()->allocate(size, flags); if (offset >= 0) { sp new_memory = heap()->mapMemory(offset, size); if (new_memory != 0) { memory = new Allocation(this, offset, size, new_memory); } else { LOGE("couldn't map [%8x, %d]", offset, size); if (size) { /* NOTE: it's VERY important to not free allocations of size 0 * because they're special as they don't have any record in the * allocator and could alias some real allocation * (their offset is zero). */ allocator()->deallocate(offset); } } } return memory; } void MemoryDealer::deallocate(size_t offset) { allocator()->deallocate(offset); } void MemoryDealer::dump(const char* what, uint32_t flags) const { allocator()->dump(what, flags); } const sp& MemoryDealer::heap() const { return mHeap; } const sp& MemoryDealer::allocator() const { return mAllocator; } // ---------------------------------------------------------------------------- // align all the memory blocks on a cache-line boundary const int SimpleBestFitAllocator::kMemoryAlign = 32; SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) { size_t pagesize = getpagesize(); mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); mList.insertHead(node); } SimpleBestFitAllocator::~SimpleBestFitAllocator() { while(!mList.isEmpty()) { delete mList.remove(mList.head()); } } size_t SimpleBestFitAllocator::size() const { return mHeapSize; } size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) { Mutex::Autolock _l(mLock); ssize_t offset = alloc(size, flags); return offset; } status_t SimpleBestFitAllocator::deallocate(size_t offset) { Mutex::Autolock _l(mLock); chunk_t const * const freed = dealloc(offset); if (freed) { return NO_ERROR; } return NAME_NOT_FOUND; } ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) { if (size == 0) { return 0; } size = (size + kMemoryAlign-1) / kMemoryAlign; chunk_t* free_chunk = 0; chunk_t* cur = mList.head(); size_t pagesize = getpagesize(); while (cur) { int extra = 0; if (flags & PAGE_ALIGNED) extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; // best fit if (cur->free && (cur->size >= (size+extra))) { if ((!free_chunk) || (cur->size < free_chunk->size)) { free_chunk = cur; } if (cur->size == size) { break; } } cur = cur->next; } if (free_chunk) { const size_t free_size = free_chunk->size; free_chunk->free = 0; free_chunk->size = size; if (free_size > size) { int extra = 0; if (flags & PAGE_ALIGNED) extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; if (extra) { chunk_t* split = new chunk_t(free_chunk->start, extra); free_chunk->start += extra; mList.insertBefore(free_chunk, split); } LOGE_IF((flags&PAGE_ALIGNED) && ((free_chunk->start*kMemoryAlign)&(pagesize-1)), "PAGE_ALIGNED requested, but page is not aligned!!!"); const ssize_t tail_free = free_size - (size+extra); if (tail_free > 0) { chunk_t* split = new chunk_t( free_chunk->start + free_chunk->size, tail_free); mList.insertAfter(free_chunk, split); } } return (free_chunk->start)*kMemoryAlign; } return NO_MEMORY; } SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) { start = start / kMemoryAlign; chunk_t* cur = mList.head(); while (cur) { if (cur->start == start) { LOG_FATAL_IF(cur->free, "block at offset 0x%08lX of size 0x%08lX already freed", cur->start*kMemoryAlign, cur->size*kMemoryAlign); // merge freed blocks together chunk_t* freed = cur; cur->free = 1; do { chunk_t* const p = cur->prev; chunk_t* const n = cur->next; if (p && (p->free || !cur->size)) { freed = p; p->size += cur->size; mList.remove(cur); delete cur; } cur = n; } while (cur && cur->free); #ifndef NDEBUG if (!freed->free) { dump_l("dealloc (!freed->free)"); } #endif LOG_FATAL_IF(!freed->free, "freed block at offset 0x%08lX of size 0x%08lX is not free!", freed->start * kMemoryAlign, freed->size * kMemoryAlign); return freed; } cur = cur->next; } return 0; } void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const { Mutex::Autolock _l(mLock); dump_l(what, flags); } void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const { String8 result; dump_l(result, what, flags); LOGD("%s", result.string()); } void SimpleBestFitAllocator::dump(String8& result, const char* what, uint32_t flags) const { Mutex::Autolock _l(mLock); dump_l(result, what, flags); } void SimpleBestFitAllocator::dump_l(String8& result, const char* what, uint32_t flags) const { size_t size = 0; int32_t i = 0; chunk_t const* cur = mList.head(); const size_t SIZE = 256; char buffer[SIZE]; snprintf(buffer, SIZE, " %s (%p, size=%u)\n", what, this, (unsigned int)mHeapSize); result.append(buffer); while (cur) { const char* errs[] = {"", "| link bogus NP", "| link bogus PN", "| link bogus NP+PN" }; int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", i, int(cur), int(cur->start*kMemoryAlign), int(cur->size*kMemoryAlign), int(cur->free) ? "F" : "A", errs[np|pn]); result.append(buffer); if (!cur->free) size += cur->size*kMemoryAlign; i++; cur = cur->next; } snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); result.append(buffer); } // ---------------------------------------------------------------------------- SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) : MemoryHeapBase(size, flags, name) { } SharedHeap::~SharedHeap() { } sp SharedHeap::mapMemory(size_t offset, size_t size) { return new SimpleMemory(this, offset, size); } SimpleMemory::SimpleMemory(const sp& heap, ssize_t offset, size_t size) : MemoryBase(heap, offset, size) { #ifndef NDEBUG void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); memset(start_ptr, 0xda, size); #endif } SimpleMemory::~SimpleMemory() { size_t freedOffset = getOffset(); size_t freedSize = getSize(); // keep the size to unmap in excess size_t pagesize = getpagesize(); size_t start = freedOffset; size_t end = start + freedSize; start &= ~(pagesize-1); end = (end + pagesize-1) & ~(pagesize-1); // give back to the kernel the pages we don't need size_t free_start = freedOffset; size_t free_end = free_start + freedSize; if (start < free_start) start = free_start; if (end > free_end) end = free_end; start = (start + pagesize-1) & ~(pagesize-1); end &= ~(pagesize-1); if (start < end) { void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); size_t size = end-start; #ifndef NDEBUG memset(start_ptr, 0xdf, size); #endif // MADV_REMOVE is not defined on Dapper based Goobuntu #ifdef MADV_REMOVE if (size) { int err = madvise(start_ptr, size, MADV_REMOVE); LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", start_ptr, size, err<0 ? strerror(errno) : "Ok"); } #endif } } }; // namespace android