83c0446f27
- make sure that all binder Bn classes define a ctor and dtor in their respective library. This avoids duplication of the ctor/dtor in libraries where these objects are instantiated. This is also cleaner, should we want these ctor/dtor to do something one day. - same change as above for some Bp classes and various other non-binder classes - moved the definition of CHECK_INTERFACE() in IInterface.h instead of having it everywhere. - improved the CHECK_INTERFACE() macro so it calls a single method in Parcel, instead of inlining its code everywhere - IBinder::getInterfaceDescriptor() now returns a "const String16&" instead of String16, which saves calls to String16 and ~String16 - implemented a cache for BpBinder::getInterfaceDescriptor(), since this does an IPC. HOWEVER, this method never seems to be called. The cache makes BpBinder bigger, so we need to figure out if we need this method at all.
422 lines
12 KiB
C++
422 lines
12 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 <binder/MemoryDealer.h>
|
|
|
|
#include <utils/Log.h>
|
|
#include <binder/IPCThreadState.h>
|
|
#include <utils/SortedVector.h>
|
|
#include <utils/String8.h>
|
|
#include <binder/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 {
|
|
// ----------------------------------------------------------------------------
|
|
|
|
HeapInterface::HeapInterface() { }
|
|
HeapInterface::~HeapInterface() { }
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
AllocatorInterface::AllocatorInterface() { }
|
|
AllocatorInterface::~AllocatorInterface() { }
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
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 [%8lx, %u]", 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()
|
|
: HeapInterface(), MemoryHeapBase()
|
|
{
|
|
}
|
|
|
|
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);
|
|
|
|
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
|