362 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2015 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 "space_test.h"
 | |
| 
 | |
| #include "dlmalloc_space.h"
 | |
| #include "rosalloc_space.h"
 | |
| #include "scoped_thread_state_change-inl.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace gc {
 | |
| namespace space {
 | |
| 
 | |
| enum MallocSpaceType {
 | |
|   kMallocSpaceDlMalloc,
 | |
|   kMallocSpaceRosAlloc,
 | |
| };
 | |
| 
 | |
| class SpaceCreateTest : public SpaceTest<CommonRuntimeTestWithParam<MallocSpaceType>> {
 | |
|  public:
 | |
|   MallocSpace* CreateSpace(const std::string& name,
 | |
|                            size_t initial_size,
 | |
|                            size_t growth_limit,
 | |
|                            size_t capacity) {
 | |
|     const MallocSpaceType type = GetParam();
 | |
|     if (type == kMallocSpaceDlMalloc) {
 | |
|       return DlMallocSpace::Create(name,
 | |
|                                    initial_size,
 | |
|                                    growth_limit,
 | |
|                                    capacity,
 | |
|                                    /*can_move_objects=*/ false);
 | |
|     }
 | |
|     DCHECK_EQ(static_cast<uint32_t>(type), static_cast<uint32_t>(kMallocSpaceRosAlloc));
 | |
|     return RosAllocSpace::Create(name,
 | |
|                                  initial_size,
 | |
|                                  growth_limit,
 | |
|                                  capacity,
 | |
|                                  Runtime::Current()->GetHeap()->IsLowMemoryMode(),
 | |
|                                  /*can_move_objects=*/ false);
 | |
|   }
 | |
| };
 | |
| 
 | |
| TEST_P(SpaceCreateTest, InitTestBody) {
 | |
|   // This will lead to error messages in the log.
 | |
|   ScopedLogSeverity sls(LogSeverity::FATAL);
 | |
| 
 | |
|   {
 | |
|     // Init < max == growth
 | |
|     std::unique_ptr<Space> space(CreateSpace("test", 16 * MB, 32 * MB, 32 * MB));
 | |
|     EXPECT_TRUE(space != nullptr);
 | |
|     // Init == max == growth
 | |
|     space.reset(CreateSpace("test", 16 * MB, 16 * MB, 16 * MB));
 | |
|     EXPECT_TRUE(space != nullptr);
 | |
|     // Init > max == growth
 | |
|     space.reset(CreateSpace("test", 32 * MB, 16 * MB, 16 * MB));
 | |
|     EXPECT_TRUE(space == nullptr);
 | |
|     // Growth == init < max
 | |
|     space.reset(CreateSpace("test", 16 * MB, 16 * MB, 32 * MB));
 | |
|     EXPECT_TRUE(space != nullptr);
 | |
|     // Growth < init < max
 | |
|     space.reset(CreateSpace("test", 16 * MB, 8 * MB, 32 * MB));
 | |
|     EXPECT_TRUE(space == nullptr);
 | |
|     // Init < growth < max
 | |
|     space.reset(CreateSpace("test", 8 * MB, 16 * MB, 32 * MB));
 | |
|     EXPECT_TRUE(space != nullptr);
 | |
|     // Init < max < growth
 | |
|     space.reset(CreateSpace("test", 8 * MB, 32 * MB, 16 * MB));
 | |
|     EXPECT_TRUE(space == nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // TODO: This test is not very good, we should improve it.
 | |
| // The test should do more allocations before the creation of the ZygoteSpace, and then do
 | |
| // allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
 | |
| // the GC works with the ZygoteSpace.
 | |
| TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
 | |
|   size_t unused;
 | |
|   MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB));
 | |
|   ASSERT_TRUE(space != nullptr);
 | |
| 
 | |
|   // Make space findable to the heap, will also delete space when runtime is cleaned up
 | |
|   AddSpace(space);
 | |
|   Thread* self = Thread::Current();
 | |
|   ScopedObjectAccess soa(self);
 | |
| 
 | |
|   // Succeeds, fits without adjusting the footprint limit.
 | |
|   size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated;
 | |
|   StackHandleScope<3> hs(soa.Self());
 | |
|   MutableHandle<mirror::Object> ptr1(hs.NewHandle(Alloc(space,
 | |
|                                                         self,
 | |
|                                                         1 * MB,
 | |
|                                                         &ptr1_bytes_allocated,
 | |
|                                                         &ptr1_usable_size,
 | |
|                                                         &ptr1_bytes_tl_bulk_allocated)));
 | |
|   EXPECT_TRUE(ptr1 != nullptr);
 | |
|   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
 | |
|   EXPECT_LE(1U * MB, ptr1_usable_size);
 | |
|   EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
 | |
|   EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated);
 | |
| 
 | |
|   // Fails, requires a higher footprint limit.
 | |
|   mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr2 == nullptr);
 | |
| 
 | |
|   // Succeeds, adjusts the footprint.
 | |
|   size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated;
 | |
|   MutableHandle<mirror::Object> ptr3(hs.NewHandle(AllocWithGrowth(space,
 | |
|                                                                   self,
 | |
|                                                                   8 * MB,
 | |
|                                                                   &ptr3_bytes_allocated,
 | |
|                                                                   &ptr3_usable_size,
 | |
|                                                                   &ptr3_bytes_tl_bulk_allocated)));
 | |
|   EXPECT_TRUE(ptr3 != nullptr);
 | |
|   EXPECT_LE(8U * MB, ptr3_bytes_allocated);
 | |
|   EXPECT_LE(8U * MB, ptr3_usable_size);
 | |
|   EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
 | |
|   EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated);
 | |
| 
 | |
|   // Fails, requires a higher footprint limit.
 | |
|   mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr4 == nullptr);
 | |
| 
 | |
|   // Also fails, requires a higher allowed footprint.
 | |
|   mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr5 == nullptr);
 | |
| 
 | |
|   // Release some memory.
 | |
|   size_t free3 = space->AllocationSize(ptr3.Get(), nullptr);
 | |
|   EXPECT_EQ(free3, ptr3_bytes_allocated);
 | |
|   EXPECT_EQ(free3, space->Free(self, ptr3.Assign(nullptr)));
 | |
|   EXPECT_LE(8U * MB, free3);
 | |
| 
 | |
|   // Succeeds, now that memory has been freed.
 | |
|   size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated;
 | |
|   Handle<mirror::Object> ptr6(hs.NewHandle(AllocWithGrowth(space,
 | |
|                                                            self,
 | |
|                                                            9 * MB,
 | |
|                                                            &ptr6_bytes_allocated,
 | |
|                                                            &ptr6_usable_size,
 | |
|                                                            &ptr6_bytes_tl_bulk_allocated)));
 | |
|   EXPECT_TRUE(ptr6 != nullptr);
 | |
|   EXPECT_LE(9U * MB, ptr6_bytes_allocated);
 | |
|   EXPECT_LE(9U * MB, ptr6_usable_size);
 | |
|   EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
 | |
|   EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated);
 | |
| 
 | |
|   // Final clean up.
 | |
|   size_t free1 = space->AllocationSize(ptr1.Get(), nullptr);
 | |
|   space->Free(self, ptr1.Assign(nullptr));
 | |
|   EXPECT_LE(1U * MB, free1);
 | |
| 
 | |
|   // Make sure that the zygote space isn't directly at the start of the space.
 | |
|   EXPECT_TRUE(space->Alloc(self, 1U * MB, &unused, nullptr, &unused) != nullptr);
 | |
| 
 | |
|   gc::Heap* heap = Runtime::Current()->GetHeap();
 | |
|   space::Space* old_space = space;
 | |
|   {
 | |
|     ScopedThreadSuspension sts(self, ThreadState::kSuspended);
 | |
|     ScopedSuspendAll ssa("Add image space");
 | |
|     heap->RemoveSpace(old_space);
 | |
|   }
 | |
|   heap->RevokeAllThreadLocalBuffers();
 | |
|   space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space",
 | |
|                                                               heap->IsLowMemoryMode(),
 | |
|                                                               &space);
 | |
|   delete old_space;
 | |
|   // Add the zygote space.
 | |
|   AddSpace(zygote_space, false);
 | |
| 
 | |
|   // Make space findable to the heap, will also delete space when runtime is cleaned up
 | |
|   AddSpace(space, false);
 | |
| 
 | |
|   // Succeeds, fits without adjusting the footprint limit.
 | |
|   ptr1.Assign(Alloc(space,
 | |
|                     self,
 | |
|                     1 * MB,
 | |
|                     &ptr1_bytes_allocated,
 | |
|                     &ptr1_usable_size,
 | |
|                     &ptr1_bytes_tl_bulk_allocated));
 | |
|   EXPECT_TRUE(ptr1 != nullptr);
 | |
|   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
 | |
|   EXPECT_LE(1U * MB, ptr1_usable_size);
 | |
|   EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
 | |
|   EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated);
 | |
| 
 | |
|   // Fails, requires a higher footprint limit.
 | |
|   ptr2 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr2 == nullptr);
 | |
| 
 | |
|   // Succeeds, adjusts the footprint.
 | |
|   ptr3.Assign(AllocWithGrowth(space,
 | |
|                               self,
 | |
|                               2 * MB,
 | |
|                               &ptr3_bytes_allocated,
 | |
|                               &ptr3_usable_size,
 | |
|                               &ptr3_bytes_tl_bulk_allocated));
 | |
|   EXPECT_TRUE(ptr3 != nullptr);
 | |
|   EXPECT_LE(2U * MB, ptr3_bytes_allocated);
 | |
|   EXPECT_LE(2U * MB, ptr3_usable_size);
 | |
|   EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
 | |
|   EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated);
 | |
|   space->Free(self, ptr3.Assign(nullptr));
 | |
| 
 | |
|   // Final clean up.
 | |
|   free1 = space->AllocationSize(ptr1.Get(), nullptr);
 | |
|   space->Free(self, ptr1.Assign(nullptr));
 | |
|   EXPECT_LE(1U * MB, free1);
 | |
| }
 | |
| 
 | |
| TEST_P(SpaceCreateTest, AllocAndFreeTestBody) {
 | |
|   size_t unused = 0;
 | |
|   MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB));
 | |
|   ASSERT_TRUE(space != nullptr);
 | |
|   Thread* self = Thread::Current();
 | |
|   ScopedObjectAccess soa(self);
 | |
| 
 | |
|   // Make space findable to the heap, will also delete space when runtime is cleaned up
 | |
|   AddSpace(space);
 | |
| 
 | |
|   // Succeeds, fits without adjusting the footprint limit.
 | |
|   size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated;
 | |
|   StackHandleScope<3> hs(soa.Self());
 | |
|   MutableHandle<mirror::Object> ptr1(hs.NewHandle(Alloc(space,
 | |
|                                                         self,
 | |
|                                                         1 * MB,
 | |
|                                                         &ptr1_bytes_allocated,
 | |
|                                                         &ptr1_usable_size,
 | |
|                                                         &ptr1_bytes_tl_bulk_allocated)));
 | |
|   EXPECT_TRUE(ptr1 != nullptr);
 | |
|   EXPECT_LE(1U * MB, ptr1_bytes_allocated);
 | |
|   EXPECT_LE(1U * MB, ptr1_usable_size);
 | |
|   EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
 | |
|   EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated);
 | |
| 
 | |
|   // Fails, requires a higher footprint limit.
 | |
|   mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr2 == nullptr);
 | |
| 
 | |
|   // Succeeds, adjusts the footprint.
 | |
|   size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated;
 | |
|   MutableHandle<mirror::Object> ptr3(hs.NewHandle(AllocWithGrowth(space,
 | |
|                                                                   self,
 | |
|                                                                   8 * MB,
 | |
|                                                                   &ptr3_bytes_allocated,
 | |
|                                                                   &ptr3_usable_size,
 | |
|                                                                   &ptr3_bytes_tl_bulk_allocated)));
 | |
|   EXPECT_TRUE(ptr3 != nullptr);
 | |
|   EXPECT_LE(8U * MB, ptr3_bytes_allocated);
 | |
|   EXPECT_LE(8U * MB, ptr3_usable_size);
 | |
|   EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
 | |
|   EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated);
 | |
| 
 | |
|   // Fails, requires a higher footprint limit.
 | |
|   mirror::Object* ptr4 = Alloc(space, self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr4 == nullptr);
 | |
| 
 | |
|   // Also fails, requires a higher allowed footprint.
 | |
|   mirror::Object* ptr5 = AllocWithGrowth(space, self, 8 * MB, &unused, nullptr, &unused);
 | |
|   EXPECT_TRUE(ptr5 == nullptr);
 | |
| 
 | |
|   // Release some memory.
 | |
|   size_t free3 = space->AllocationSize(ptr3.Get(), nullptr);
 | |
|   EXPECT_EQ(free3, ptr3_bytes_allocated);
 | |
|   space->Free(self, ptr3.Assign(nullptr));
 | |
|   EXPECT_LE(8U * MB, free3);
 | |
| 
 | |
|   // Succeeds, now that memory has been freed.
 | |
|   size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated;
 | |
|   Handle<mirror::Object> ptr6(hs.NewHandle(AllocWithGrowth(space,
 | |
|                                                            self,
 | |
|                                                            9 * MB,
 | |
|                                                            &ptr6_bytes_allocated,
 | |
|                                                            &ptr6_usable_size,
 | |
|                                                            &ptr6_bytes_tl_bulk_allocated)));
 | |
|   EXPECT_TRUE(ptr6 != nullptr);
 | |
|   EXPECT_LE(9U * MB, ptr6_bytes_allocated);
 | |
|   EXPECT_LE(9U * MB, ptr6_usable_size);
 | |
|   EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
 | |
|   EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated);
 | |
| 
 | |
|   // Final clean up.
 | |
|   size_t free1 = space->AllocationSize(ptr1.Get(), nullptr);
 | |
|   space->Free(self, ptr1.Assign(nullptr));
 | |
|   EXPECT_LE(1U * MB, free1);
 | |
| }
 | |
| 
 | |
| TEST_P(SpaceCreateTest, AllocAndFreeListTestBody) {
 | |
|   MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB));
 | |
|   ASSERT_TRUE(space != nullptr);
 | |
| 
 | |
|   // Make space findable to the heap, will also delete space when runtime is cleaned up
 | |
|   AddSpace(space);
 | |
|   Thread* self = Thread::Current();
 | |
|   ScopedObjectAccess soa(self);
 | |
| 
 | |
|   // Succeeds, fits without adjusting the max allowed footprint.
 | |
|   mirror::Object* lots_of_objects[1024];
 | |
|   for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
 | |
|     size_t allocation_size, usable_size, bytes_tl_bulk_allocated;
 | |
|     size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
 | |
|     lots_of_objects[i] = Alloc(space,
 | |
|                                self,
 | |
|                                size_of_zero_length_byte_array,
 | |
|                                &allocation_size,
 | |
|                                &usable_size,
 | |
|                                &bytes_tl_bulk_allocated);
 | |
|     EXPECT_TRUE(lots_of_objects[i] != nullptr);
 | |
|     size_t computed_usable_size;
 | |
|     EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size));
 | |
|     EXPECT_EQ(usable_size, computed_usable_size);
 | |
|     EXPECT_TRUE(bytes_tl_bulk_allocated == 0 ||
 | |
|                 bytes_tl_bulk_allocated >= allocation_size);
 | |
|   }
 | |
| 
 | |
|   // Release memory.
 | |
|   space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
 | |
| 
 | |
|   // Succeeds, fits by adjusting the max allowed footprint.
 | |
|   for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
 | |
|     size_t allocation_size, usable_size, bytes_tl_bulk_allocated;
 | |
|     lots_of_objects[i] = AllocWithGrowth(space,
 | |
|                                          self,
 | |
|                                          1024,
 | |
|                                          &allocation_size,
 | |
|                                          &usable_size,
 | |
|                                          &bytes_tl_bulk_allocated);
 | |
|     EXPECT_TRUE(lots_of_objects[i] != nullptr);
 | |
|     size_t computed_usable_size;
 | |
|     EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size));
 | |
|     EXPECT_EQ(usable_size, computed_usable_size);
 | |
|     EXPECT_TRUE(bytes_tl_bulk_allocated == 0 ||
 | |
|                 bytes_tl_bulk_allocated >= allocation_size);
 | |
|   }
 | |
| 
 | |
|   // Release memory.
 | |
|   space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
 | |
| }
 | |
| 
 | |
| INSTANTIATE_TEST_CASE_P(CreateRosAllocSpace,
 | |
|                         SpaceCreateTest,
 | |
|                         testing::Values(kMallocSpaceRosAlloc));
 | |
| INSTANTIATE_TEST_CASE_P(CreateDlMallocSpace,
 | |
|                         SpaceCreateTest,
 | |
|                         testing::Values(kMallocSpaceDlMalloc));
 | |
| 
 | |
| }  // namespace space
 | |
| }  // namespace gc
 | |
| }  // namespace art
 |