// Copyright 2018 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. #include "base/SubAllocator.h" #include "base/address_space.h" #include "base/Stream.h" #include #include #include namespace android { namespace base { class SubAllocator::Impl { public: Impl( void* _buffer, uint64_t _totalSize, uint64_t _pageSize) : buffer(_buffer), totalSize(_totalSize), pageSize(_pageSize), startAddr((uintptr_t)buffer), endAddr(startAddr + totalSize) { address_space_allocator_init( &addr_alloc, totalSize, 32); } ~Impl() { address_space_allocator_destroy_nocleanup(&addr_alloc); } void clear() { address_space_allocator_destroy_nocleanup(&addr_alloc); address_space_allocator_init( &addr_alloc, totalSize, 32); } bool save(Stream* stream) { address_space_allocator_iter_func_t allocatorSaver = [](void* context, struct address_space_allocator* allocator) { Stream* stream = reinterpret_cast(context); stream->putBe32(allocator->size); stream->putBe32(allocator->capacity); stream->putBe64(allocator->total_bytes); }; address_block_iter_func_t allocatorBlockSaver = [](void* context, struct address_block* block) { Stream* stream = reinterpret_cast(context); stream->putBe64(block->offset); stream->putBe64(block->size_available); }; address_space_allocator_run( &addr_alloc, (void*)stream, allocatorSaver, allocatorBlockSaver); stream->putBe64(pageSize); stream->putBe64(totalSize); stream->putBe32(allocCount); return true; } bool load(Stream* stream) { clear(); address_space_allocator_iter_func_t allocatorLoader = [](void* context, struct address_space_allocator* allocator) { Stream* stream = reinterpret_cast(context); allocator->size = stream->getBe32(); allocator->capacity = stream->getBe32(); allocator->total_bytes = stream->getBe64(); }; address_block_iter_func_t allocatorBlockLoader = [](void* context, struct address_block* block) { Stream* stream = reinterpret_cast(context); block->offset = stream->getBe64(); block->size_available = stream->getBe64(); }; address_space_allocator_run( &addr_alloc, (void*)stream, allocatorLoader, allocatorBlockLoader); pageSize = stream->getBe64(); totalSize = stream->getBe64(); allocCount = stream->getBe32(); return true; } bool postLoad(void* postLoadBuffer) { buffer = postLoadBuffer; startAddr = (uint64_t)(uintptr_t)postLoadBuffer; return true; } void rangeCheck(const char* task, void* ptr) { uint64_t addr = (uintptr_t)ptr; if (addr < startAddr || addr > endAddr) { std::stringstream ss; ss << "SubAllocator " << task << ": "; ss << "Out of range: " << std::hex << addr << " "; ss << "Range: " << std::hex << startAddr << " " << std::hex << endAddr; std::string msg = ss.str(); fprintf(stderr, "%s: range check failed: [%s]\n", __func__, msg.c_str()); } } uint64_t getOffset(void* checkedPtr) { uint64_t addr = (uintptr_t)checkedPtr; return addr - startAddr; } bool free(void* ptr) { if (!ptr) return false; rangeCheck("free", ptr); if (EINVAL == address_space_allocator_deallocate( &addr_alloc, getOffset(ptr))) { return false; } --allocCount; return true; } void freeAll() { address_space_allocator_reset(&addr_alloc); allocCount = 0; } void* alloc(size_t wantedSize) { if (wantedSize == 0) return nullptr; size_t toPageSize = pageSize * ((wantedSize + pageSize - 1) / pageSize); uint64_t offset = address_space_allocator_allocate( &addr_alloc, toPageSize); if (offset == ANDROID_EMU_ADDRESS_SPACE_BAD_OFFSET) { return nullptr; } ++allocCount; return (void*)(uintptr_t)(startAddr + offset); } void* allocFixed(size_t wantedSize, uint64_t offset) { if (wantedSize == 0) return nullptr; size_t toPageSize = pageSize * ((wantedSize + pageSize - 1) / pageSize); if (address_space_allocator_allocate_fixed( &addr_alloc, toPageSize, offset)) { return nullptr; } ++allocCount; return (void*)(uintptr_t)(startAddr + offset); } bool empty() const { return allocCount == 0; } void* buffer; uint64_t totalSize; uint64_t pageSize; uint64_t startAddr; uint64_t endAddr; struct address_space_allocator addr_alloc; uint32_t allocCount = 0; }; SubAllocator::SubAllocator( void* buffer, uint64_t totalSize, uint64_t pageSize) : mImpl( new SubAllocator::Impl(buffer, totalSize, pageSize)) { } SubAllocator::~SubAllocator() { delete mImpl; } // Snapshotting bool SubAllocator::save(Stream* stream) { return mImpl->save(stream); } bool SubAllocator::load(Stream* stream) { return mImpl->load(stream); } bool SubAllocator::postLoad(void* postLoadBuffer) { return mImpl->postLoad(postLoadBuffer); } void* SubAllocator::alloc(size_t wantedSize) { return mImpl->alloc(wantedSize); } void* SubAllocator::allocFixed(size_t wantedSize, uint64_t offset) { return mImpl->allocFixed(wantedSize, offset); } bool SubAllocator::free(void* ptr) { return mImpl->free(ptr); } void SubAllocator::freeAll() { mImpl->freeAll(); } uint64_t SubAllocator::getOffset(void* ptr) { return mImpl->getOffset(ptr); } bool SubAllocator::empty() const { return mImpl->empty(); } } // namespace base } // namespace android