1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/zone.h"
6#include "platform/assert.h"
7#include "vm/dart.h"
8#include "vm/isolate.h"
9#include "vm/unit_test.h"
10
11namespace dart {
12
13VM_UNIT_TEST_CASE(AllocateZone) {
14#if defined(DEBUG)
15 FLAG_trace_zones = true;
16#endif
17 TestCase::CreateTestIsolate();
18 Thread* thread = Thread::Current();
19 EXPECT(thread->zone() == NULL);
20 {
21 TransitionNativeToVM transition(thread);
22 StackZone stack_zone(thread);
23 EXPECT(thread->zone() != NULL);
24 Zone* zone = stack_zone.GetZone();
25 uintptr_t allocated_size = 0;
26
27 // The loop is to make sure we overflow one segment and go on
28 // to the next segment.
29 for (int i = 0; i < 1000; i++) {
30 uword first = zone->AllocUnsafe(2 * kWordSize);
31 uword second = zone->AllocUnsafe(3 * kWordSize);
32 EXPECT(first != second);
33 allocated_size = ((2 + 3) * kWordSize);
34 }
35 EXPECT_LE(allocated_size, zone->SizeInBytes());
36
37 // Test for allocation of large segments.
38 const uword kLargeSize = 1 * MB;
39 const uword kSegmentSize = 64 * KB;
40 ASSERT(kLargeSize > kSegmentSize);
41 for (int i = 0; i < 10; i++) {
42 EXPECT(zone->AllocUnsafe(kLargeSize) != 0);
43 allocated_size += kLargeSize;
44 }
45 EXPECT_LE(allocated_size, zone->SizeInBytes());
46
47 // Test corner cases of kSegmentSize.
48 uint8_t* buffer = NULL;
49 buffer =
50 reinterpret_cast<uint8_t*>(zone->AllocUnsafe(kSegmentSize - kWordSize));
51 EXPECT(buffer != NULL);
52 buffer[(kSegmentSize - kWordSize) - 1] = 0;
53 allocated_size += (kSegmentSize - kWordSize);
54 EXPECT_LE(allocated_size, zone->SizeInBytes());
55
56 buffer = reinterpret_cast<uint8_t*>(
57 zone->AllocUnsafe(kSegmentSize - (2 * kWordSize)));
58 EXPECT(buffer != NULL);
59 buffer[(kSegmentSize - (2 * kWordSize)) - 1] = 0;
60 allocated_size += (kSegmentSize - (2 * kWordSize));
61 EXPECT_LE(allocated_size, zone->SizeInBytes());
62
63 buffer =
64 reinterpret_cast<uint8_t*>(zone->AllocUnsafe(kSegmentSize + kWordSize));
65 EXPECT(buffer != NULL);
66 buffer[(kSegmentSize + kWordSize) - 1] = 0;
67 allocated_size += (kSegmentSize + kWordSize);
68 EXPECT_LE(allocated_size, zone->SizeInBytes());
69 }
70 EXPECT(thread->zone() == NULL);
71 Dart_ShutdownIsolate();
72}
73
74VM_UNIT_TEST_CASE(AllocGeneric_Success) {
75#if defined(DEBUG)
76 FLAG_trace_zones = true;
77#endif
78 TestCase::CreateTestIsolate();
79 Thread* thread = Thread::Current();
80 EXPECT(thread->zone() == NULL);
81 {
82 TransitionNativeToVM transition(thread);
83 StackZone zone(thread);
84 EXPECT(thread->zone() != NULL);
85 uintptr_t allocated_size = 0;
86
87 const intptr_t kNumElements = 1000;
88 zone.GetZone()->Alloc<uint32_t>(kNumElements);
89 allocated_size += sizeof(uint32_t) * kNumElements;
90 EXPECT_LE(allocated_size, zone.SizeInBytes());
91 }
92 EXPECT(thread->zone() == NULL);
93 Dart_ShutdownIsolate();
94}
95
96// This test is expected to crash.
97VM_UNIT_TEST_CASE_WITH_EXPECTATION(AllocGeneric_Overflow, "Crash") {
98#if defined(DEBUG)
99 FLAG_trace_zones = true;
100#endif
101 TestCase::CreateTestIsolate();
102 Thread* thread = Thread::Current();
103 EXPECT(thread->zone() == NULL);
104 {
105 StackZone zone(thread);
106 EXPECT(thread->zone() != NULL);
107
108 const intptr_t kNumElements = (kIntptrMax / sizeof(uint32_t)) + 1;
109 zone.GetZone()->Alloc<uint32_t>(kNumElements);
110 }
111 Dart_ShutdownIsolate();
112}
113
114VM_UNIT_TEST_CASE(ZoneAllocated) {
115#if defined(DEBUG)
116 FLAG_trace_zones = true;
117#endif
118 TestCase::CreateTestIsolate();
119 Thread* thread = Thread::Current();
120 EXPECT(thread->zone() == NULL);
121 static int marker;
122
123 class SimpleZoneObject : public ZoneAllocated {
124 public:
125 SimpleZoneObject() : slot(marker++) {}
126 virtual ~SimpleZoneObject() {}
127 virtual int GetSlot() { return slot; }
128 int slot;
129 };
130
131 // Reset the marker.
132 marker = 0;
133
134 // Create a few zone allocated objects.
135 {
136 TransitionNativeToVM transition(thread);
137 StackZone zone(thread);
138 EXPECT_EQ(0UL, zone.SizeInBytes());
139 SimpleZoneObject* first = new SimpleZoneObject();
140 EXPECT(first != NULL);
141 SimpleZoneObject* second = new SimpleZoneObject();
142 EXPECT(second != NULL);
143 EXPECT(first != second);
144 uintptr_t expected_size = (2 * sizeof(SimpleZoneObject));
145 EXPECT_LE(expected_size, zone.SizeInBytes());
146
147 // Make sure the constructors were invoked.
148 EXPECT_EQ(0, first->slot);
149 EXPECT_EQ(1, second->slot);
150
151 // Make sure we can write to the members of the zone objects.
152 first->slot = 42;
153 second->slot = 87;
154 EXPECT_EQ(42, first->slot);
155 EXPECT_EQ(87, second->slot);
156 }
157 EXPECT(thread->zone() == NULL);
158 Dart_ShutdownIsolate();
159}
160
161TEST_CASE(PrintToString) {
162 TransitionNativeToVM transition(Thread::Current());
163 StackZone zone(Thread::Current());
164 const char* result = zone.GetZone()->PrintToString("Hello %s!", "World");
165 EXPECT_STREQ("Hello World!", result);
166}
167
168VM_UNIT_TEST_CASE(NativeScopeZoneAllocation) {
169 ASSERT(ApiNativeScope::Current() == NULL);
170 ASSERT(Thread::Current() == NULL);
171 EXPECT_EQ(0UL, ApiNativeScope::current_memory_usage());
172 {
173 ApiNativeScope scope;
174 EXPECT_EQ(scope.zone()->CapacityInBytes(),
175 ApiNativeScope::current_memory_usage());
176 (void)Dart_ScopeAllocate(2048);
177 EXPECT_EQ(scope.zone()->CapacityInBytes(),
178 ApiNativeScope::current_memory_usage());
179 }
180 EXPECT_EQ(0UL, ApiNativeScope::current_memory_usage());
181}
182
183#if !defined(PRODUCT)
184// Allow for pooling in the malloc implementation.
185static const int64_t kRssSlack = 20 * MB;
186#endif // !defined(PRODUCT)
187
188// clang-format off
189static const size_t kSizes[] = {
190 64 * KB,
191 64 * KB + 2 * kWordSize,
192 64 * KB - 2 * kWordSize,
193 128 * KB,
194 128 * KB + 2 * kWordSize,
195 128 * KB - 2 * kWordSize,
196 256 * KB,
197 256 * KB + 2 * kWordSize,
198 256 * KB - 2 * kWordSize,
199 512 * KB,
200 512 * KB + 2 * kWordSize,
201 512 * KB - 2 * kWordSize,
202};
203// clang-format on
204
205TEST_CASE(StressMallocDirectly) {
206#if !defined(PRODUCT)
207 int64_t start_rss = Service::CurrentRSS();
208#endif // !defined(PRODUCT)
209
210 void* allocations[ARRAY_SIZE(kSizes)];
211 for (size_t i = 0; i < ((3u * GB) / (512u * KB)); i++) {
212 for (size_t j = 0; j < ARRAY_SIZE(kSizes); j++) {
213 allocations[j] = malloc(kSizes[j]);
214 }
215 for (size_t j = 0; j < ARRAY_SIZE(kSizes); j++) {
216 free(allocations[j]);
217 }
218 }
219
220#if !defined(PRODUCT)
221 int64_t stop_rss = Service::CurrentRSS();
222 EXPECT_LT(stop_rss, start_rss + kRssSlack);
223#endif // !defined(PRODUCT)
224}
225
226ISOLATE_UNIT_TEST_CASE(StressMallocThroughZones) {
227#if !defined(PRODUCT)
228 int64_t start_rss = Service::CurrentRSS();
229#endif // !defined(PRODUCT)
230
231 for (size_t i = 0; i < ((3u * GB) / (512u * KB)); i++) {
232 StackZone stack_zone(Thread::Current());
233 Zone* zone = stack_zone.GetZone();
234 for (size_t j = 0; j < ARRAY_SIZE(kSizes); j++) {
235 zone->Alloc<uint8_t>(kSizes[j]);
236 }
237 }
238
239#if !defined(PRODUCT)
240 int64_t stop_rss = Service::CurrentRSS();
241 EXPECT_LT(stop_rss, start_rss + kRssSlack);
242#endif // !defined(PRODUCT)
243}
244
245} // namespace dart
246