The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / test / cctest / test-mark-compact.cc @ f230a1cf

History | View | Annotate | Download (18.3 KB)

1
// Copyright 2012 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
4
// met:
5
//
6
//     * Redistributions of source code must retain the above copyright
7
//       notice, this list of conditions and the following disclaimer.
8
//     * Redistributions in binary form must reproduce the above
9
//       copyright notice, this list of conditions and the following
10
//       disclaimer in the documentation and/or other materials provided
11
//       with the distribution.
12
//     * Neither the name of Google Inc. nor the names of its
13
//       contributors may be used to endorse or promote products derived
14
//       from this software without specific prior written permission.
15
//
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27

    
28
#include <stdlib.h>
29

    
30
#ifdef __linux__
31
#include <sys/types.h>
32
#include <sys/stat.h>
33
#include <fcntl.h>
34
#include <unistd.h>
35
#include <errno.h>
36
#endif
37

    
38

    
39
#include "v8.h"
40

    
41
#include "global-handles.h"
42
#include "snapshot.h"
43
#include "cctest.h"
44

    
45
using namespace v8::internal;
46

    
47

    
48
TEST(MarkingDeque) {
49
  CcTest::InitializeVM();
50
  int mem_size = 20 * kPointerSize;
51
  byte* mem = NewArray<byte>(20*kPointerSize);
52
  Address low = reinterpret_cast<Address>(mem);
53
  Address high = low + mem_size;
54
  MarkingDeque s;
55
  s.Initialize(low, high);
56

    
57
  Address original_address = reinterpret_cast<Address>(&s);
58
  Address current_address = original_address;
59
  while (!s.IsFull()) {
60
    s.PushBlack(HeapObject::FromAddress(current_address));
61
    current_address += kPointerSize;
62
  }
63

    
64
  while (!s.IsEmpty()) {
65
    Address value = s.Pop()->address();
66
    current_address -= kPointerSize;
67
    CHECK_EQ(current_address, value);
68
  }
69

    
70
  CHECK_EQ(original_address, current_address);
71
  DeleteArray(mem);
72
}
73

    
74

    
75
TEST(Promotion) {
76
  CcTest::InitializeVM();
77
  Heap* heap = CcTest::heap();
78
  heap->ConfigureHeap(2*256*KB, 1*MB, 1*MB);
79

    
80
  v8::HandleScope sc(CcTest::isolate());
81

    
82
  // Allocate a fixed array in the new space.
83
  int array_length =
84
      (Page::kMaxNonCodeHeapObjectSize - FixedArray::kHeaderSize) /
85
      (4 * kPointerSize);
86
  Object* obj = heap->AllocateFixedArray(array_length)->ToObjectChecked();
87
  Handle<FixedArray> array(FixedArray::cast(obj));
88

    
89
  // Array should be in the new space.
90
  CHECK(heap->InSpace(*array, NEW_SPACE));
91

    
92
  // Call mark compact GC, so array becomes an old object.
93
  heap->CollectGarbage(OLD_POINTER_SPACE);
94

    
95
  // Array now sits in the old space
96
  CHECK(heap->InSpace(*array, OLD_POINTER_SPACE));
97
}
98

    
99

    
100
TEST(NoPromotion) {
101
  CcTest::InitializeVM();
102
  Heap* heap = CcTest::heap();
103
  heap->ConfigureHeap(2*256*KB, 1*MB, 1*MB);
104

    
105
  v8::HandleScope sc(CcTest::isolate());
106

    
107
  // Allocate a big fixed array in the new space.
108
  int array_length =
109
      (Page::kMaxNonCodeHeapObjectSize - FixedArray::kHeaderSize) /
110
      (2 * kPointerSize);
111
  Object* obj = heap->AllocateFixedArray(array_length)->ToObjectChecked();
112
  Handle<FixedArray> array(FixedArray::cast(obj));
113

    
114
  // Array should be in the new space.
115
  CHECK(heap->InSpace(*array, NEW_SPACE));
116

    
117
  // Simulate a full old space to make promotion fail.
118
  SimulateFullSpace(heap->old_pointer_space());
119

    
120
  // Call mark compact GC, and it should pass.
121
  heap->CollectGarbage(OLD_POINTER_SPACE);
122
}
123

    
124

    
125
TEST(MarkCompactCollector) {
126
  FLAG_incremental_marking = false;
127
  CcTest::InitializeVM();
128
  Isolate* isolate = CcTest::i_isolate();
129
  Heap* heap = isolate->heap();
130

    
131
  v8::HandleScope sc(CcTest::isolate());
132
  Handle<GlobalObject> global(isolate->context()->global_object());
133

    
134
  // call mark-compact when heap is empty
135
  heap->CollectGarbage(OLD_POINTER_SPACE, "trigger 1");
136

    
137
  // keep allocating garbage in new space until it fails
138
  const int ARRAY_SIZE = 100;
139
  Object* array;
140
  MaybeObject* maybe_array;
141
  do {
142
    maybe_array = heap->AllocateFixedArray(ARRAY_SIZE);
143
  } while (maybe_array->ToObject(&array));
144
  heap->CollectGarbage(NEW_SPACE, "trigger 2");
145

    
146
  array = heap->AllocateFixedArray(ARRAY_SIZE)->ToObjectChecked();
147

    
148
  // keep allocating maps until it fails
149
  Object* mapp;
150
  MaybeObject* maybe_mapp;
151
  do {
152
    maybe_mapp = heap->AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
153
  } while (maybe_mapp->ToObject(&mapp));
154
  heap->CollectGarbage(MAP_SPACE, "trigger 3");
155
  mapp = heap->AllocateMap(JS_OBJECT_TYPE,
156
                           JSObject::kHeaderSize)->ToObjectChecked();
157

    
158
  // allocate a garbage
159
  String* func_name = String::cast(
160
      heap->InternalizeUtf8String("theFunction")->ToObjectChecked());
161
  SharedFunctionInfo* function_share = SharedFunctionInfo::cast(
162
      heap->AllocateSharedFunctionInfo(func_name)->ToObjectChecked());
163
  JSFunction* function = JSFunction::cast(
164
      heap->AllocateFunction(*isolate->function_map(),
165
                             function_share,
166
                             heap->undefined_value())->ToObjectChecked());
167
  Map* initial_map =
168
      Map::cast(heap->AllocateMap(JS_OBJECT_TYPE,
169
                                  JSObject::kHeaderSize)->ToObjectChecked());
170
  function->set_initial_map(initial_map);
171
  JSReceiver::SetProperty(
172
      global, handle(func_name), handle(function), NONE, kNonStrictMode);
173

    
174
  JSObject* obj = JSObject::cast(
175
      heap->AllocateJSObject(function)->ToObjectChecked());
176
  heap->CollectGarbage(OLD_POINTER_SPACE, "trigger 4");
177

    
178
  func_name = String::cast(
179
      heap->InternalizeUtf8String("theFunction")->ToObjectChecked());
180
  CHECK(JSReceiver::HasLocalProperty(global, handle(func_name)));
181
  Object* func_value = isolate->context()->global_object()->
182
      GetProperty(func_name)->ToObjectChecked();
183
  CHECK(func_value->IsJSFunction());
184
  function = JSFunction::cast(func_value);
185

    
186
  obj = JSObject::cast(heap->AllocateJSObject(function)->ToObjectChecked());
187
  String* obj_name =
188
      String::cast(heap->InternalizeUtf8String("theObject")->ToObjectChecked());
189
  JSReceiver::SetProperty(
190
      global, handle(obj_name), handle(obj), NONE, kNonStrictMode);
191
  String* prop_name =
192
      String::cast(heap->InternalizeUtf8String("theSlot")->ToObjectChecked());
193
  Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
194
  JSReceiver::SetProperty(
195
      handle(obj), handle(prop_name), twenty_three, NONE, kNonStrictMode);
196

    
197
  heap->CollectGarbage(OLD_POINTER_SPACE, "trigger 5");
198

    
199
  obj_name =
200
      String::cast(heap->InternalizeUtf8String("theObject")->ToObjectChecked());
201
  CHECK(JSReceiver::HasLocalProperty(global, handle(obj_name)));
202
  CHECK(isolate->context()->global_object()->
203
        GetProperty(obj_name)->ToObjectChecked()->IsJSObject());
204
  obj = JSObject::cast(isolate->context()->global_object()->
205
                       GetProperty(obj_name)->ToObjectChecked());
206
  prop_name =
207
      String::cast(heap->InternalizeUtf8String("theSlot")->ToObjectChecked());
208
  CHECK(obj->GetProperty(prop_name) == Smi::FromInt(23));
209
}
210

    
211

    
212
// TODO(1600): compaction of map space is temporary removed from GC.
213
#if 0
214
static Handle<Map> CreateMap(Isolate* isolate) {
215
  return isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
216
}
217

218

219
TEST(MapCompact) {
220
  FLAG_max_map_space_pages = 16;
221
  CcTest::InitializeVM();
222
  Isolate* isolate = CcTest::i_isolate();
223
  Factory* factory = isolate->factory();
224

225
  {
226
    v8::HandleScope sc;
227
    // keep allocating maps while pointers are still encodable and thus
228
    // mark compact is permitted.
229
    Handle<JSObject> root = factory->NewJSObjectFromMap(CreateMap());
230
    do {
231
      Handle<Map> map = CreateMap();
232
      map->set_prototype(*root);
233
      root = factory->NewJSObjectFromMap(map);
234
    } while (CcTest::heap()->map_space()->MapPointersEncodable());
235
  }
236
  // Now, as we don't have any handles to just allocated maps, we should
237
  // be able to trigger map compaction.
238
  // To give an additional chance to fail, try to force compaction which
239
  // should be impossible right now.
240
  CcTest::heap()->CollectAllGarbage(Heap::kForceCompactionMask);
241
  // And now map pointers should be encodable again.
242
  CHECK(CcTest::heap()->map_space()->MapPointersEncodable());
243
}
244
#endif
245

    
246

    
247
static int NumberOfWeakCalls = 0;
248
static void WeakPointerCallback(v8::Isolate* isolate,
249
                                v8::Persistent<v8::Value>* handle,
250
                                void* id) {
251
  ASSERT(id == reinterpret_cast<void*>(1234));
252
  NumberOfWeakCalls++;
253
  handle->Dispose();
254
}
255

    
256

    
257
TEST(ObjectGroups) {
258
  FLAG_incremental_marking = false;
259
  CcTest::InitializeVM();
260
  GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
261
  Heap* heap = CcTest::heap();
262
  NumberOfWeakCalls = 0;
263
  v8::HandleScope handle_scope(CcTest::isolate());
264

    
265
  Handle<Object> g1s1 =
266
      global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
267
  Handle<Object> g1s2 =
268
      global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
269
  Handle<Object> g1c1 =
270
      global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
271
  global_handles->MakeWeak(g1s1.location(),
272
                           reinterpret_cast<void*>(1234),
273
                           &WeakPointerCallback);
274
  global_handles->MakeWeak(g1s2.location(),
275
                           reinterpret_cast<void*>(1234),
276
                           &WeakPointerCallback);
277
  global_handles->MakeWeak(g1c1.location(),
278
                           reinterpret_cast<void*>(1234),
279
                           &WeakPointerCallback);
280

    
281
  Handle<Object> g2s1 =
282
      global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
283
  Handle<Object> g2s2 =
284
    global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
285
  Handle<Object> g2c1 =
286
    global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
287
  global_handles->MakeWeak(g2s1.location(),
288
                           reinterpret_cast<void*>(1234),
289
                           &WeakPointerCallback);
290
  global_handles->MakeWeak(g2s2.location(),
291
                           reinterpret_cast<void*>(1234),
292
                           &WeakPointerCallback);
293
  global_handles->MakeWeak(g2c1.location(),
294
                           reinterpret_cast<void*>(1234),
295
                           &WeakPointerCallback);
296

    
297
  Handle<Object> root = global_handles->Create(*g1s1);  // make a root.
298

    
299
  // Connect group 1 and 2, make a cycle.
300
  Handle<FixedArray>::cast(g1s2)->set(0, *g2s2);
301
  Handle<FixedArray>::cast(g2s1)->set(0, *g1s1);
302

    
303
  {
304
    Object** g1_objects[] = { g1s1.location(), g1s2.location() };
305
    Object** g1_children[] = { g1c1.location() };
306
    Object** g2_objects[] = { g2s1.location(), g2s2.location() };
307
    Object** g2_children[] = { g2c1.location() };
308
    global_handles->AddObjectGroup(g1_objects, 2, NULL);
309
    global_handles->AddImplicitReferences(
310
        Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
311
    global_handles->AddObjectGroup(g2_objects, 2, NULL);
312
    global_handles->AddImplicitReferences(
313
        Handle<HeapObject>::cast(g2s1).location(), g2_children, 1);
314
  }
315
  // Do a full GC
316
  heap->CollectGarbage(OLD_POINTER_SPACE);
317

    
318
  // All object should be alive.
319
  CHECK_EQ(0, NumberOfWeakCalls);
320

    
321
  // Weaken the root.
322
  global_handles->MakeWeak(root.location(),
323
                           reinterpret_cast<void*>(1234),
324
                           &WeakPointerCallback);
325
  // But make children strong roots---all the objects (except for children)
326
  // should be collectable now.
327
  global_handles->ClearWeakness(g1c1.location());
328
  global_handles->ClearWeakness(g2c1.location());
329

    
330
  // Groups are deleted, rebuild groups.
331
  {
332
    Object** g1_objects[] = { g1s1.location(), g1s2.location() };
333
    Object** g1_children[] = { g1c1.location() };
334
    Object** g2_objects[] = { g2s1.location(), g2s2.location() };
335
    Object** g2_children[] = { g2c1.location() };
336
    global_handles->AddObjectGroup(g1_objects, 2, NULL);
337
    global_handles->AddImplicitReferences(
338
        Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
339
    global_handles->AddObjectGroup(g2_objects, 2, NULL);
340
    global_handles->AddImplicitReferences(
341
        Handle<HeapObject>::cast(g2s1).location(), g2_children, 1);
342
  }
343

    
344
  heap->CollectGarbage(OLD_POINTER_SPACE);
345

    
346
  // All objects should be gone. 5 global handles in total.
347
  CHECK_EQ(5, NumberOfWeakCalls);
348

    
349
  // And now make children weak again and collect them.
350
  global_handles->MakeWeak(g1c1.location(),
351
                           reinterpret_cast<void*>(1234),
352
                           &WeakPointerCallback);
353
  global_handles->MakeWeak(g2c1.location(),
354
                           reinterpret_cast<void*>(1234),
355
                           &WeakPointerCallback);
356

    
357
  heap->CollectGarbage(OLD_POINTER_SPACE);
358
  CHECK_EQ(7, NumberOfWeakCalls);
359
}
360

    
361

    
362
class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
363
 public:
364
  TestRetainedObjectInfo() : has_been_disposed_(false) {}
365

    
366
  bool has_been_disposed() { return has_been_disposed_; }
367

    
368
  virtual void Dispose() {
369
    ASSERT(!has_been_disposed_);
370
    has_been_disposed_ = true;
371
  }
372

    
373
  virtual bool IsEquivalent(v8::RetainedObjectInfo* other) {
374
    return other == this;
375
  }
376

    
377
  virtual intptr_t GetHash() { return 0; }
378

    
379
  virtual const char* GetLabel() { return "whatever"; }
380

    
381
 private:
382
  bool has_been_disposed_;
383
};
384

    
385

    
386
TEST(EmptyObjectGroups) {
387
  CcTest::InitializeVM();
388
  GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
389

    
390
  v8::HandleScope handle_scope(CcTest::isolate());
391

    
392
  Handle<Object> object = global_handles->Create(
393
      CcTest::heap()->AllocateFixedArray(1)->ToObjectChecked());
394

    
395
  TestRetainedObjectInfo info;
396
  global_handles->AddObjectGroup(NULL, 0, &info);
397
  ASSERT(info.has_been_disposed());
398

    
399
  global_handles->AddImplicitReferences(
400
        Handle<HeapObject>::cast(object).location(), NULL, 0);
401
}
402

    
403

    
404
#if defined(__has_feature)
405
#if __has_feature(address_sanitizer)
406
#define V8_WITH_ASAN 1
407
#endif
408
#endif
409

    
410

    
411
// Here is a memory use test that uses /proc, and is therefore Linux-only.  We
412
// do not care how much memory the simulator uses, since it is only there for
413
// debugging purposes. Testing with ASAN doesn't make sense, either.
414
#if defined(__linux__) && !defined(USE_SIMULATOR) && !defined(V8_WITH_ASAN)
415

    
416

    
417
static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) {
418
  char* end_address = buffer + *position;
419
  uintptr_t result = strtoul(buffer + *position, &end_address, base);
420
  CHECK(result != ULONG_MAX || errno != ERANGE);
421
  CHECK(end_address > buffer + *position);
422
  *position = end_address - buffer;
423
  return result;
424
}
425

    
426

    
427
// The memory use computed this way is not entirely accurate and depends on
428
// the way malloc allocates memory.  That's why the memory use may seem to
429
// increase even though the sum of the allocated object sizes decreases.  It
430
// also means that the memory use depends on the kernel and stdlib.
431
static intptr_t MemoryInUse() {
432
  intptr_t memory_use = 0;
433

    
434
  int fd = open("/proc/self/maps", O_RDONLY);
435
  if (fd < 0) return -1;
436

    
437
  const int kBufSize = 10000;
438
  char buffer[kBufSize];
439
  int length = read(fd, buffer, kBufSize);
440
  intptr_t line_start = 0;
441
  CHECK_LT(length, kBufSize);  // Make the buffer bigger.
442
  CHECK_GT(length, 0);  // We have to find some data in the file.
443
  while (line_start < length) {
444
    if (buffer[line_start] == '\n') {
445
      line_start++;
446
      continue;
447
    }
448
    intptr_t position = line_start;
449
    uintptr_t start = ReadLong(buffer, &position, 16);
450
    CHECK_EQ(buffer[position++], '-');
451
    uintptr_t end = ReadLong(buffer, &position, 16);
452
    CHECK_EQ(buffer[position++], ' ');
453
    CHECK(buffer[position] == '-' || buffer[position] == 'r');
454
    bool read_permission = (buffer[position++] == 'r');
455
    CHECK(buffer[position] == '-' || buffer[position] == 'w');
456
    bool write_permission = (buffer[position++] == 'w');
457
    CHECK(buffer[position] == '-' || buffer[position] == 'x');
458
    bool execute_permission = (buffer[position++] == 'x');
459
    CHECK(buffer[position] == '-' || buffer[position] == 'p');
460
    bool private_mapping = (buffer[position++] == 'p');
461
    CHECK_EQ(buffer[position++], ' ');
462
    uintptr_t offset = ReadLong(buffer, &position, 16);
463
    USE(offset);
464
    CHECK_EQ(buffer[position++], ' ');
465
    uintptr_t major = ReadLong(buffer, &position, 16);
466
    USE(major);
467
    CHECK_EQ(buffer[position++], ':');
468
    uintptr_t minor = ReadLong(buffer, &position, 16);
469
    USE(minor);
470
    CHECK_EQ(buffer[position++], ' ');
471
    uintptr_t inode = ReadLong(buffer, &position, 10);
472
    while (position < length && buffer[position] != '\n') position++;
473
    if ((read_permission || write_permission || execute_permission) &&
474
        private_mapping && inode == 0) {
475
      memory_use += (end - start);
476
    }
477

    
478
    line_start = position;
479
  }
480
  close(fd);
481
  return memory_use;
482
}
483

    
484

    
485
TEST(BootUpMemoryUse) {
486
  intptr_t initial_memory = MemoryInUse();
487
  // Avoid flakiness.
488
  FLAG_crankshaft = false;
489
  FLAG_concurrent_recompilation = false;
490

    
491
  // Only Linux has the proc filesystem and only if it is mapped.  If it's not
492
  // there we just skip the test.
493
  if (initial_memory >= 0) {
494
    CcTest::InitializeVM();
495
    intptr_t delta = MemoryInUse() - initial_memory;
496
    printf("delta: %" V8_PTR_PREFIX "d kB\n", delta / 1024);
497
    if (sizeof(initial_memory) == 8) {  // 64-bit.
498
      if (v8::internal::Snapshot::IsEnabled()) {
499
        CHECK_LE(delta, 4000 * 1024);
500
      } else {
501
        CHECK_LE(delta, 4500 * 1024);
502
      }
503
    } else {                            // 32-bit.
504
      if (v8::internal::Snapshot::IsEnabled()) {
505
        CHECK_LE(delta, 3100 * 1024);
506
      } else {
507
        CHECK_LE(delta, 3450 * 1024);
508
      }
509
    }
510
  }
511
}
512

    
513

    
514
intptr_t ShortLivingIsolate() {
515
  v8::Isolate* isolate = v8::Isolate::New();
516
  { v8::Isolate::Scope isolate_scope(isolate);
517
    v8::Locker lock(isolate);
518
    v8::HandleScope handle_scope(isolate);
519
    v8::Local<v8::Context> context = v8::Context::New(isolate);
520
    CHECK(!context.IsEmpty());
521
  }
522
  isolate->Dispose();
523
  return MemoryInUse();
524
}
525

    
526

    
527
TEST(RegressJoinThreadsOnIsolateDeinit) {
528
  intptr_t size_limit = ShortLivingIsolate() * 2;
529
  for (int i = 0; i < 10; i++) {
530
    CHECK_GT(size_limit, ShortLivingIsolate());
531
  }
532
}
533

    
534
#endif  // __linux__ and !USE_SIMULATOR