/* * Copyright (C) 2014 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. */ #ifndef ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_ #define ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_ #include "memory_tool_malloc_space.h" #include "base/memory_tool.h" #include "memory_tool_settings.h" #include "mirror/object-inl.h" namespace art { namespace gc { namespace space { namespace memory_tool_details { template inline mirror::Object* AdjustForMemoryTool(void* obj_with_rdz, size_t num_bytes, size_t bytes_allocated, size_t usable_size, size_t bytes_tl_bulk_allocated, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { if (bytes_allocated_out != nullptr) { *bytes_allocated_out = bytes_allocated; } if (bytes_tl_bulk_allocated_out != nullptr) { *bytes_tl_bulk_allocated_out = bytes_tl_bulk_allocated; } // This cuts over-provision and is a trade-off between testing the over-provisioning code paths // vs checking overflows in the regular paths. if (usable_size_out != nullptr) { if (kUseObjSizeForUsable) { *usable_size_out = num_bytes; } else { *usable_size_out = usable_size - 2 * kMemoryToolRedZoneBytes; } } // Left redzone. MEMORY_TOOL_MAKE_NOACCESS(obj_with_rdz, kMemoryToolRedZoneBytes); // Make requested memory readable. // (If the allocator assumes memory is zeroed out, we might get UNDEFINED warnings, so make // everything DEFINED initially.) mirror::Object* result = reinterpret_cast( reinterpret_cast(obj_with_rdz) + kMemoryToolRedZoneBytes); MEMORY_TOOL_MAKE_DEFINED(result, num_bytes); // Right redzone. Assumes that if bytes_allocated > usable_size, then the difference is // management data at the upper end, and for simplicity we will not protect that. // At the moment, this fits RosAlloc (no management data in a slot, usable_size == alloc_size) // and DlMalloc (allocation_size = (usable_size == num_bytes) + 4, 4 is management) MEMORY_TOOL_MAKE_NOACCESS(reinterpret_cast(result) + num_bytes, usable_size - (num_bytes + kMemoryToolRedZoneBytes)); return result; } inline size_t GetObjSizeNoThreadSafety(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS { return obj->SizeOf(); } } // namespace memory_tool_details template mirror::Object* MemoryToolMallocSpace::AllocWithGrowth( Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; void* obj_with_rdz = S::AllocWithGrowth(self, num_bytes + 2 * kMemoryToolRedZoneBytes, &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } return memory_tool_details::AdjustForMemoryTool( obj_with_rdz, num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, bytes_tl_bulk_allocated_out); } template mirror::Object* MemoryToolMallocSpace::Alloc( Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; void* obj_with_rdz = S::Alloc(self, num_bytes + 2 * kMemoryToolRedZoneBytes, &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } return memory_tool_details::AdjustForMemoryTool( obj_with_rdz, num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, bytes_tl_bulk_allocated_out); } template mirror::Object* MemoryToolMallocSpace::AllocThreadUnsafe( Thread* self, size_t num_bytes, size_t* bytes_allocated_out, size_t* usable_size_out, size_t* bytes_tl_bulk_allocated_out) { size_t bytes_allocated; size_t usable_size; size_t bytes_tl_bulk_allocated; void* obj_with_rdz = S::AllocThreadUnsafe(self, num_bytes + 2 * kMemoryToolRedZoneBytes, &bytes_allocated, &usable_size, &bytes_tl_bulk_allocated); if (obj_with_rdz == nullptr) { return nullptr; } return memory_tool_details::AdjustForMemoryTool( obj_with_rdz, num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated, bytes_allocated_out, usable_size_out, bytes_tl_bulk_allocated_out); } template size_t MemoryToolMallocSpace::AllocationSize( mirror::Object* obj, size_t* usable_size) { size_t result = S::AllocationSize( reinterpret_cast( reinterpret_cast(obj) - (kAdjustForRedzoneInAllocSize ? kMemoryToolRedZoneBytes : 0)), usable_size); if (usable_size != nullptr) { if (kUseObjSizeForUsable) { *usable_size = memory_tool_details::GetObjSizeNoThreadSafety(obj); } else { *usable_size = *usable_size - 2 * kMemoryToolRedZoneBytes; } } return result; } template size_t MemoryToolMallocSpace::Free(Thread* self, mirror::Object* ptr) { void* obj_after_rdz = reinterpret_cast(ptr); uint8_t* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kMemoryToolRedZoneBytes; // Make redzones undefined. size_t usable_size; size_t allocation_size = AllocationSize(ptr, &usable_size); // Unprotect the allocation. // Use the obj-size-for-usable flag to determine whether usable_size is the more important one, // e.g., whether there's data in the allocation_size (and usable_size can't be trusted). if (kUseObjSizeForUsable) { MEMORY_TOOL_MAKE_UNDEFINED(obj_with_rdz, allocation_size); } else { MEMORY_TOOL_MAKE_UNDEFINED(obj_with_rdz, usable_size + 2 * kMemoryToolRedZoneBytes); } return S::Free(self, reinterpret_cast(obj_with_rdz)); } template size_t MemoryToolMallocSpace::FreeList( Thread* self, size_t num_ptrs, mirror::Object** ptrs) { size_t freed = 0; // Sort the pointers to free non class objects first. See b/131542326 for why this is necessary to // avoid crashes. std::sort(ptrs, ptrs + num_ptrs, [](mirror::Object* a, mirror::Object* b) REQUIRES_SHARED(Locks::mutator_lock_) { return a->IsClass() < b->IsClass(); }); for (size_t i = 0; i < num_ptrs; i++) { freed += Free(self, ptrs[i]); ptrs[i] = nullptr; } return freed; } template template MemoryToolMallocSpace::MemoryToolMallocSpace( MemMap&& mem_map, size_t initial_size, Params... params) : S(std::move(mem_map), initial_size, params...) { // Don't want to change the memory tool states of the mem map here as the allocator is already // initialized at this point and that may interfere with what the allocator does internally. Note // that the tail beyond the initial size is mprotected. } template size_t MemoryToolMallocSpace::MaxBytesBulkAllocatedFor(size_t num_bytes) { return S::MaxBytesBulkAllocatedFor(num_bytes + 2 * kMemoryToolRedZoneBytes); } } // namespace space } // namespace gc } // namespace art #endif // ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_