1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "large_object_space.h"
18
19#include "base/time_utils.h"
20#include "space_test.h"
21
22namespace art {
23namespace gc {
24namespace space {
25
26class LargeObjectSpaceTest : public SpaceTest<CommonRuntimeTest> {
27 public:
28  void LargeObjectTest();
29
30  static constexpr size_t kNumThreads = 10;
31  static constexpr size_t kNumIterations = 1000;
32  void RaceTest();
33};
34
35
36void LargeObjectSpaceTest::LargeObjectTest() {
37  size_t rand_seed = 0;
38  Thread* const self = Thread::Current();
39  for (size_t i = 0; i < 2; ++i) {
40    LargeObjectSpace* los = nullptr;
41    const size_t capacity = 128 * MB;
42    if (i == 0) {
43      los = space::LargeObjectMapSpace::Create("large object space");
44    } else {
45      los = space::FreeListSpace::Create("large object space", nullptr, capacity);
46    }
47
48    // Make sure the bitmap is not empty and actually covers at least how much we expect.
49    CHECK_LT(static_cast<uintptr_t>(los->GetLiveBitmap()->HeapBegin()),
50             static_cast<uintptr_t>(los->GetLiveBitmap()->HeapLimit()));
51    CHECK_LE(static_cast<uintptr_t>(los->GetLiveBitmap()->HeapBegin() + capacity),
52             static_cast<uintptr_t>(los->GetLiveBitmap()->HeapLimit()));
53
54    static const size_t num_allocations = 64;
55    static const size_t max_allocation_size = 0x100000;
56    std::vector<std::pair<mirror::Object*, size_t>> requests;
57
58    for (size_t phase = 0; phase < 2; ++phase) {
59      while (requests.size() < num_allocations) {
60        size_t request_size = test_rand(&rand_seed) % max_allocation_size;
61        size_t allocation_size = 0;
62        size_t bytes_tl_bulk_allocated;
63        mirror::Object* obj = los->Alloc(self, request_size, &allocation_size, nullptr,
64                                         &bytes_tl_bulk_allocated);
65        ASSERT_TRUE(obj != nullptr);
66        ASSERT_EQ(allocation_size, los->AllocationSize(obj, nullptr));
67        ASSERT_GE(allocation_size, request_size);
68        ASSERT_EQ(allocation_size, bytes_tl_bulk_allocated);
69        // Fill in our magic value.
70        uint8_t magic = (request_size & 0xFF) | 1;
71        memset(obj, magic, request_size);
72        requests.push_back(std::make_pair(obj, request_size));
73      }
74
75      // "Randomly" shuffle the requests.
76      for (size_t k = 0; k < 10; ++k) {
77        for (size_t j = 0; j < requests.size(); ++j) {
78          std::swap(requests[j], requests[test_rand(&rand_seed) % requests.size()]);
79        }
80      }
81
82      // Check the zygote flag for the first phase.
83      if (phase == 0) {
84        for (const auto& pair : requests) {
85          mirror::Object* obj = pair.first;
86          ASSERT_FALSE(los->IsZygoteLargeObject(self, obj));
87        }
88        los->SetAllLargeObjectsAsZygoteObjects(self);
89        for (const auto& pair : requests) {
90          mirror::Object* obj = pair.first;
91          ASSERT_TRUE(los->IsZygoteLargeObject(self, obj));
92        }
93      }
94
95      // Free 1 / 2 the allocations the first phase, and all the second phase.
96      size_t limit = phase == 0 ? requests.size() / 2 : 0;
97      while (requests.size() > limit) {
98        mirror::Object* obj = requests.back().first;
99        size_t request_size = requests.back().second;
100        requests.pop_back();
101        uint8_t magic = (request_size & 0xFF) | 1;
102        for (size_t k = 0; k < request_size; ++k) {
103          ASSERT_EQ(reinterpret_cast<const uint8_t*>(obj)[k], magic);
104        }
105        ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
106      }
107    }
108    // Test that dump doesn't crash.
109    std::ostringstream oss;
110    los->Dump(oss);
111    LOG(INFO) << oss.str();
112
113    size_t bytes_allocated = 0, bytes_tl_bulk_allocated;
114    // Checks that the coalescing works.
115    mirror::Object* obj = los->Alloc(self, 100 * MB, &bytes_allocated, nullptr,
116                                     &bytes_tl_bulk_allocated);
117    EXPECT_TRUE(obj != nullptr);
118    los->Free(Thread::Current(), obj);
119
120    EXPECT_EQ(0U, los->GetBytesAllocated());
121    EXPECT_EQ(0U, los->GetObjectsAllocated());
122    delete los;
123  }
124}
125
126class AllocRaceTask : public Task {
127 public:
128  AllocRaceTask(size_t id, size_t iterations, size_t size, LargeObjectSpace* los) :
129    id_(id), iterations_(iterations), size_(size), los_(los) {}
130
131  void Run(Thread* self) {
132    for (size_t i = 0; i < iterations_ ; ++i) {
133      size_t alloc_size, bytes_tl_bulk_allocated;
134      mirror::Object* ptr = los_->Alloc(self, size_, &alloc_size, nullptr,
135                                        &bytes_tl_bulk_allocated);
136
137      NanoSleep((id_ + 3) * 1000);  // (3+id) mu s
138
139      los_->Free(self, ptr);
140    }
141  }
142
143  virtual void Finalize() {
144    delete this;
145  }
146
147 private:
148  size_t id_;
149  size_t iterations_;
150  size_t size_;
151  LargeObjectSpace* los_;
152};
153
154void LargeObjectSpaceTest::RaceTest() {
155  for (size_t los_type = 0; los_type < 2; ++los_type) {
156    LargeObjectSpace* los = nullptr;
157    if (los_type == 0) {
158      los = space::LargeObjectMapSpace::Create("large object space");
159    } else {
160      los = space::FreeListSpace::Create("large object space", nullptr, 128 * MB);
161    }
162
163    Thread* self = Thread::Current();
164    ThreadPool thread_pool("Large object space test thread pool", kNumThreads);
165    for (size_t i = 0; i < kNumThreads; ++i) {
166      thread_pool.AddTask(self, new AllocRaceTask(i, kNumIterations, 16 * KB, los));
167    }
168
169    thread_pool.StartWorkers(self);
170
171    thread_pool.Wait(self, true, false);
172
173    delete los;
174  }
175}
176
177TEST_F(LargeObjectSpaceTest, LargeObjectTest) {
178  LargeObjectTest();
179}
180
181TEST_F(LargeObjectSpaceTest, RaceTest) {
182  RaceTest();
183}
184
185}  // namespace space
186}  // namespace gc
187}  // namespace art
188