408 lines
11 KiB
C++
408 lines
11 KiB
C++
|
/*
|
||
|
* 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 <utils/MemoryDealer.h>
|
||
|
|
||
|
#include <utils/Log.h>
|
||
|
#include <utils/IPCThreadState.h>
|
||
|
#include <utils/SortedVector.h>
|
||
|
#include <utils/String8.h>
|
||
|
#include <utils/MemoryBase.h>
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <sys/file.h>
|
||
|
|
||
|
namespace android {
|
||
|
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
class SimpleMemory : public MemoryBase {
|
||
|
public:
|
||
|
SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
|
||
|
virtual ~SimpleMemory();
|
||
|
};
|
||
|
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
MemoryDealer::Allocation::Allocation(
|
||
|
const sp<MemoryDealer>& dealer, ssize_t offset, size_t size,
|
||
|
const sp<IMemory>& 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<IMemoryHeap> 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<HeapInterface>& heap)
|
||
|
: mHeap(heap),
|
||
|
mAllocator(new SimpleBestFitAllocator(heap->virtualSize()))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap,
|
||
|
const sp<AllocatorInterface>& allocator)
|
||
|
: mHeap(heap), mAllocator(allocator)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
MemoryDealer::~MemoryDealer()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags)
|
||
|
{
|
||
|
sp<IMemory> memory;
|
||
|
const ssize_t offset = allocator()->allocate(size, flags);
|
||
|
if (offset >= 0) {
|
||
|
sp<IMemory> 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<HeapInterface>& MemoryDealer::heap() const {
|
||
|
return mHeap;
|
||
|
}
|
||
|
|
||
|
const sp<AllocatorInterface>& 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<IMemory> SharedHeap::mapMemory(size_t offset, size_t size)
|
||
|
{
|
||
|
return new SimpleMemory(this, offset, size);
|
||
|
}
|
||
|
|
||
|
|
||
|
SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& 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);
|
||
|
|
||
|
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
|