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

30 31
#include <ctype.h>

32
#include "src/v8.h"
33

34
#include "include/v8-profiler.h"
lpy's avatar
lpy committed
35
#include "src/base/hashmap.h"
36
#include "src/collector.h"
37
#include "src/debug/debug.h"
38 39 40
#include "src/profiler/allocation-tracker.h"
#include "src/profiler/heap-profiler.h"
#include "src/profiler/heap-snapshot-generator-inl.h"
41
#include "test/cctest/cctest.h"
42

43 44 45
using i::AllocationTraceNode;
using i::AllocationTraceTree;
using i::AllocationTracker;
46
using i::ArrayVector;
47 48
using i::Vector;

49 50 51 52 53
namespace {

class NamedEntriesDetector {
 public:
  NamedEntriesDetector()
54
      : has_A2(false), has_B2(false), has_C2(false) {
55 56
  }

57 58 59 60
  void CheckEntry(i::HeapEntry* entry) {
    if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
    if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
    if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
61 62
  }

63 64 65 66
  static bool AddressesMatch(void* key1, void* key2) {
    return key1 == key2;
  }

67
  void CheckAllReachables(i::HeapEntry* root) {
lpy's avatar
lpy committed
68
    v8::base::HashMap visited(AddressesMatch);
69 70 71 72 73
    i::List<i::HeapEntry*> list(10);
    list.Add(root);
    CheckEntry(root);
    while (!list.is_empty()) {
      i::HeapEntry* entry = list.RemoveLast();
74
      i::Vector<i::HeapGraphEdge*> children = entry->children();
75
      for (int i = 0; i < children.length(); ++i) {
76 77
        if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
        i::HeapEntry* child = children[i]->to();
lpy's avatar
lpy committed
78
        v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
79
            reinterpret_cast<void*>(child),
80
            static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)));
81 82 83 84 85
        if (entry->value)
          continue;
        entry->value = reinterpret_cast<void*>(1);
        list.Add(child);
        CheckEntry(child);
86 87
      }
    }
88 89 90 91 92 93 94 95 96
  }

  bool has_A2;
  bool has_B2;
  bool has_C2;
};

}  // namespace

97 98 99

static const v8::HeapGraphNode* GetGlobalObject(
    const v8::HeapSnapshot* snapshot) {
100 101
  CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
  // The 0th-child is (GC Roots), 1st is the user root.
102
  const v8::HeapGraphNode* global_obj =
103
      snapshot->GetRoot()->GetChild(1)->GetToNode();
104 105
  CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
      reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
106
  return global_obj;
107 108 109 110 111 112 113 114
}


static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
                                            v8::HeapGraphEdge::Type type,
                                            const char* name) {
  for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = node->GetChild(i);
115
    v8::String::Utf8Value prop_name(prop->GetName());
116 117 118 119 120 121 122 123 124 125 126
    if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
      return prop->GetToNode();
  }
  return NULL;
}


static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
  for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = node->GetChild(i);
    const v8::HeapGraphNode* node = prop->GetToNode();
127
    if (node->GetType() == v8::HeapGraphNode::kString) {
128
      v8::String::Utf8Value node_name(node->GetName());
129 130 131 132 133 134 135
      if (strcmp(contents, *node_name) == 0) return true;
    }
  }
  return false;
}


136 137 138 139 140 141
static bool AddressesMatch(void* key1, void* key2) {
  return key1 == key2;
}


// Check that snapshot has no unretained entries except root.
142
static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
143 144 145
  i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
      reinterpret_cast<const i::HeapSnapshot*>(snapshot));

lpy's avatar
lpy committed
146
  v8::base::HashMap visited(AddressesMatch);
147 148
  i::List<i::HeapGraphEdge>& edges = heap_snapshot->edges();
  for (int i = 0; i < edges.length(); ++i) {
lpy's avatar
lpy committed
149
    v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
150
        reinterpret_cast<void*>(edges[i].to()),
151
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())));
152 153 154 155 156 157 158
    uint32_t ref_count = static_cast<uint32_t>(
        reinterpret_cast<uintptr_t>(entry->value));
    entry->value = reinterpret_cast<void*>(ref_count + 1);
  }
  uint32_t unretained_entries_count = 0;
  i::List<i::HeapEntry>& entries = heap_snapshot->entries();
  for (int i = 0; i < entries.length(); ++i) {
lpy's avatar
lpy committed
159
    v8::base::HashMap::Entry* entry = visited.Lookup(
160
        reinterpret_cast<void*>(&entries[i]),
161
        static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entries[i])));
162 163 164 165 166
    if (!entry && entries[i].id() != 1) {
        entries[i].Print("entry with no retainer", "", depth, 0);
        ++unretained_entries_count;
    }
  }
167
  return unretained_entries_count == 0;
168 169 170
}


171
TEST(HeapSnapshot) {
172
  LocalContext env2;
173
  v8::HandleScope scope(env2->GetIsolate());
174
  v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
175

176
  CompileRun(
177 178 179 180 181 182
      "function A2() {}\n"
      "function B2(x) { return function() { return typeof x; }; }\n"
      "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
      "var a2 = new A2();\n"
      "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
      "var c2 = new C2(a2);");
183
  const v8::HeapSnapshot* snapshot_env2 = heap_profiler->TakeHeapSnapshot();
184
  CHECK(ValidateSnapshot(snapshot_env2));
185
  const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
186

187
  // Verify, that JS global object of env2 has '..2' properties.
188
  const v8::HeapGraphNode* a2_node =
189
      GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
190 191 192 193
  CHECK(a2_node);
  CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
  CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
  CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
194 195

  NamedEntriesDetector det;
196 197
  det.CheckAllReachables(const_cast<i::HeapEntry*>(
      reinterpret_cast<const i::HeapEntry*>(global_env2)));
198 199 200 201 202
  CHECK(det.has_A2);
  CHECK(det.has_B2);
  CHECK(det.has_C2);
}

203

204 205
TEST(HeapSnapshotObjectSizes) {
  LocalContext env;
206
  v8::HandleScope scope(env->GetIsolate());
207
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
208 209 210

  //   -a-> X1 --a
  // x -b-> X2 <-|
211
  CompileRun(
212 213
      "function X(a, b) { this.a = a; this.b = b; }\n"
      "x = new X(new X(), new X());\n"
214
      "dummy = new X();\n"
215
      "(function() { x.a.a = x.b; })();");
216
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
217
  CHECK(ValidateSnapshot(snapshot));
218 219
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* x =
220
      GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
221
  CHECK(x);
222 223
  const v8::HeapGraphNode* x1 =
      GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
224
  CHECK(x1);
225 226
  const v8::HeapGraphNode* x2 =
      GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
227
  CHECK(x2);
228

229
  // Test sizes.
230 231 232
  CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
  CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
  CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
233 234 235
}


236 237
TEST(BoundFunctionInSnapshot) {
  LocalContext env;
238
  v8::HandleScope scope(env->GetIsolate());
239
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
240 241 242 243
  CompileRun(
      "function myFunction(a, b) { this.a = a; this.b = b; }\n"
      "function AAAAA() {}\n"
      "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
244
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
245
  CHECK(ValidateSnapshot(snapshot));
246 247
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* f =
248
      GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
249
  CHECK(f);
250
  CHECK(v8_str("native_bind")->Equals(env.local(), f->GetName()).FromJust());
251 252
  const v8::HeapGraphNode* bindings =
      GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
253
  CHECK(bindings);
254
  CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
255
  CHECK_EQ(1, bindings->GetChildrenCount());
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273

  const v8::HeapGraphNode* bound_this = GetProperty(
      f, v8::HeapGraphEdge::kShortcut, "bound_this");
  CHECK(bound_this);
  CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());

  const v8::HeapGraphNode* bound_function = GetProperty(
      f, v8::HeapGraphEdge::kShortcut, "bound_function");
  CHECK(bound_function);
  CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());

  const v8::HeapGraphNode* bound_argument = GetProperty(
      f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
  CHECK(bound_argument);
  CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
}


274 275
TEST(HeapSnapshotEntryChildren) {
  LocalContext env;
276
  v8::HandleScope scope(env->GetIsolate());
277
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
278

279
  CompileRun(
280 281
      "function A() { }\n"
      "a = new A;");
282
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
283
  CHECK(ValidateSnapshot(snapshot));
284 285 286 287 288 289 290
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = global->GetChild(i);
    CHECK_EQ(global, prop->GetFromNode());
  }
  const v8::HeapGraphNode* a =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
291
  CHECK(a);
292 293 294 295 296 297 298
  for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = a->GetChild(i);
    CHECK_EQ(a, prop->GetFromNode());
  }
}


299
TEST(HeapSnapshotCodeObjects) {
300
  LocalContext env;
301
  v8::HandleScope scope(env->GetIsolate());
302
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
303

304
  CompileRun(
305 306
      "function lazy(x) { return x - 1; }\n"
      "function compiled(x) { return x + 1; }\n"
307
      "var anonymous = (function() { return function() { return 0; } })();\n"
308
      "compiled(1)");
309
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
310
  CHECK(ValidateSnapshot(snapshot));
311 312 313

  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* compiled =
314
      GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
315
  CHECK(compiled);
316
  CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
317
  const v8::HeapGraphNode* lazy =
318
      GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
319
  CHECK(lazy);
320
  CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
321
  const v8::HeapGraphNode* anonymous =
322
      GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
323
  CHECK(anonymous);
324
  CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
325
  v8::String::Utf8Value anonymous_name(anonymous->GetName());
326
  CHECK_EQ(0, strcmp("", *anonymous_name));
327 328 329

  // Find references to code.
  const v8::HeapGraphNode* compiled_code =
330
      GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
331
  CHECK(compiled_code);
332
  const v8::HeapGraphNode* lazy_code =
333
      GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
334
  CHECK(lazy_code);
335

336 337 338 339
  // Check that there's no strong next_code_link. There might be a weak one
  // but might be not, so we can't check that fact.
  const v8::HeapGraphNode* code =
      GetProperty(compiled_code, v8::HeapGraphEdge::kInternal, "code");
340
  CHECK(code);
341 342
  const v8::HeapGraphNode* next_code_link =
      GetProperty(code, v8::HeapGraphEdge::kInternal, "code");
343
  CHECK(!next_code_link);
344

345
  // Verify that non-compiled code doesn't contain references to "x"
346 347
  // literal, while compiled code does. The scope info is stored in FixedArray
  // objects attached to the SharedFunctionInfo.
348 349 350 351
  bool compiled_references_x = false, lazy_references_x = false;
  for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
    const v8::HeapGraphNode* node = prop->GetToNode();
352
    if (node->GetType() == v8::HeapGraphNode::kArray) {
353 354 355 356 357 358 359 360 361
      if (HasString(node, "x")) {
        compiled_references_x = true;
        break;
      }
    }
  }
  for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
    const v8::HeapGraphNode* node = prop->GetToNode();
362
    if (node->GetType() == v8::HeapGraphNode::kArray) {
363 364 365 366 367 368 369
      if (HasString(node, "x")) {
        lazy_references_x = true;
        break;
      }
    }
  }
  CHECK(compiled_references_x);
370 371 372
  if (i::FLAG_lazy && !(i::FLAG_ignition && i::FLAG_ignition_eager)) {
    CHECK(!lazy_references_x);
  }
373 374
}

375

376 377
TEST(HeapSnapshotHeapNumbers) {
  LocalContext env;
378
  v8::HandleScope scope(env->GetIsolate());
379
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
380 381 382
  CompileRun(
      "a = 1;    // a is Smi\n"
      "b = 2.5;  // b is HeapNumber");
383
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
384
  CHECK(ValidateSnapshot(snapshot));
385
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
386
  CHECK(!GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
387
  const v8::HeapGraphNode* b =
388
      GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
389
  CHECK(b);
390 391 392
  CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
}

393

394 395
TEST(HeapSnapshotSlicedString) {
  LocalContext env;
396
  v8::HandleScope scope(env->GetIsolate());
397
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
398 399 400 401
  CompileRun(
      "parent_string = \"123456789.123456789.123456789.123456789.123456789."
      "123456789.123456789.123456789.123456789.123456789."
      "123456789.123456789.123456789.123456789.123456789."
402 403 404 405
      "123456789.123456789.123456789.123456789.123456789."
      "123456789.123456789.123456789.123456789.123456789."
      "123456789.123456789.123456789.123456789.123456789."
      "123456789.123456789.123456789.123456789.123456789."
406 407
      "123456789.123456789.123456789.123456789.123456789.\";"
      "child_string = parent_string.slice(100);");
408
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
409
  CHECK(ValidateSnapshot(snapshot));
410 411
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* parent_string =
412
      GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
413
  CHECK(parent_string);
414
  const v8::HeapGraphNode* child_string =
415
      GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
416
  CHECK(child_string);
417
  CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
418 419 420
  const v8::HeapGraphNode* parent =
      GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
  CHECK_EQ(parent_string, parent);
421
  heap_profiler->DeleteAllHeapSnapshots();
422
}
423

424

425
TEST(HeapSnapshotConsString) {
426
  v8::Isolate* isolate = CcTest::isolate();
427
  v8::HandleScope scope(isolate);
428 429
  v8::Local<v8::ObjectTemplate> global_template =
      v8::ObjectTemplate::New(isolate);
430 431
  global_template->SetInternalFieldCount(1);
  LocalContext env(NULL, global_template);
432 433
  v8::Local<v8::Object> global_proxy = env->Global();
  v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
434 435
  CHECK_EQ(1, global->InternalFieldCount());

436
  i::Factory* factory = CcTest::i_isolate()->factory();
437 438
  i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
  i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
439 440
  i::Handle<i::String> cons_string =
      factory->NewConsString(first, second).ToHandleChecked();
441 442 443 444

  global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));

  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
445
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
446 447 448 449 450
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);

  const v8::HeapGraphNode* string_node =
      GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0");
451
  CHECK(string_node);
452 453 454 455 456 457 458 459 460 461 462 463 464 465
  CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());

  const v8::HeapGraphNode* first_node =
      GetProperty(string_node, v8::HeapGraphEdge::kInternal, "first");
  CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());

  const v8::HeapGraphNode* second_node =
      GetProperty(string_node, v8::HeapGraphEdge::kInternal, "second");
  CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());

  heap_profiler->DeleteAllHeapSnapshots();
}


466 467 468 469 470 471
TEST(HeapSnapshotSymbol) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  CompileRun("a = Symbol('mySymbol');\n");
472
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
473 474 475 476
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* a =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
477
  CHECK(a);
478
  CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
479
  CHECK(v8_str("symbol")->Equals(env.local(), a->GetName()).FromJust());
480 481
  const v8::HeapGraphNode* name =
      GetProperty(a, v8::HeapGraphEdge::kInternal, "name");
482
  CHECK(name);
483
  CHECK(v8_str("mySymbol")->Equals(env.local(), name->GetName()).FromJust());
484 485
}

486

487
void CheckSimdSnapshot(const char* program, const char* var_name) {
488 489 490 491 492
  i::FLAG_harmony_simd = true;
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

493
  CompileRun(program);
494 495 496
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
497 498 499 500 501 502 503 504 505 506
  const v8::HeapGraphNode* var =
      GetProperty(global, v8::HeapGraphEdge::kProperty, var_name);
  CHECK(var);
  CHECK_EQ(var->GetType(), v8::HeapGraphNode::kSimdValue);
}


TEST(HeapSnapshotSimd) {
  CheckSimdSnapshot("a = SIMD.Float32x4();\n", "a");
  CheckSimdSnapshot("a = SIMD.Int32x4();\n", "a");
507
  CheckSimdSnapshot("a = SIMD.Uint32x4();\n", "a");
508 509
  CheckSimdSnapshot("a = SIMD.Bool32x4();\n", "a");
  CheckSimdSnapshot("a = SIMD.Int16x8();\n", "a");
510
  CheckSimdSnapshot("a = SIMD.Uint16x8();\n", "a");
511 512
  CheckSimdSnapshot("a = SIMD.Bool16x8();\n", "a");
  CheckSimdSnapshot("a = SIMD.Int8x16();\n", "a");
513
  CheckSimdSnapshot("a = SIMD.Uint8x16();\n", "a");
514
  CheckSimdSnapshot("a = SIMD.Bool8x16();\n", "a");
515 516 517
}


518 519 520 521 522
TEST(HeapSnapshotWeakCollection) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

523 524 525 526
  CompileRun(
      "k = {}; v = {}; s = 'str';\n"
      "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
      "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
527
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
528 529 530 531
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* k =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
532
  CHECK(k);
533 534
  const v8::HeapGraphNode* v =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
535
  CHECK(v);
536 537
  const v8::HeapGraphNode* s =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
538
  CHECK(s);
539 540 541

  const v8::HeapGraphNode* ws =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "ws");
542
  CHECK(ws);
543
  CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
544
  CHECK(v8_str("WeakSet")->Equals(env.local(), ws->GetName()).FromJust());
545 546 547 548 549 550 551 552 553 554 555 556 557 558

  const v8::HeapGraphNode* ws_table =
      GetProperty(ws, v8::HeapGraphEdge::kInternal, "table");
  CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
  CHECK_GT(ws_table->GetChildrenCount(), 0);
  int weak_entries = 0;
  for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
    if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
    if (k->GetId() == prop->GetToNode()->GetId()) {
      ++weak_entries;
    }
  }
  CHECK_EQ(1, weak_entries);
559 560
  const v8::HeapGraphNode* ws_s =
      GetProperty(ws, v8::HeapGraphEdge::kProperty, "str");
561 562
  CHECK(ws_s);
  CHECK_EQ(s->GetId(), ws_s->GetId());
563 564 565

  const v8::HeapGraphNode* wm =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "wm");
566
  CHECK(wm);
567
  CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
568
  CHECK(v8_str("WeakMap")->Equals(env.local(), wm->GetName()).FromJust());
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

  const v8::HeapGraphNode* wm_table =
      GetProperty(wm, v8::HeapGraphEdge::kInternal, "table");
  CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
  CHECK_GT(wm_table->GetChildrenCount(), 0);
  weak_entries = 0;
  for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
    if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
    const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
    if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
      ++weak_entries;
    }
  }
  CHECK_EQ(2, weak_entries);
584 585
  const v8::HeapGraphNode* wm_s =
      GetProperty(wm, v8::HeapGraphEdge::kProperty, "str");
586 587
  CHECK(wm_s);
  CHECK_EQ(s->GetId(), wm_s->GetId());
588 589 590 591 592 593 594 595 596 597 598 599
}


TEST(HeapSnapshotCollection) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  CompileRun(
      "k = {}; v = {}; s = 'str';\n"
      "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
      "map = new Map(); map.set(k, v); map[s] = s;\n");
600
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
601 602 603 604
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* k =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
605
  CHECK(k);
606 607
  const v8::HeapGraphNode* v =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
608
  CHECK(v);
609 610
  const v8::HeapGraphNode* s =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
611
  CHECK(s);
612 613 614

  const v8::HeapGraphNode* set =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "set");
615
  CHECK(set);
616
  CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
617
  CHECK(v8_str("Set")->Equals(env.local(), set->GetName()).FromJust());
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633

  const v8::HeapGraphNode* set_table =
      GetProperty(set, v8::HeapGraphEdge::kInternal, "table");
  CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
  CHECK_GT(set_table->GetChildrenCount(), 0);
  int entries = 0;
  for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = set_table->GetChild(i);
    const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
    if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
      ++entries;
    }
  }
  CHECK_EQ(2, entries);
  const v8::HeapGraphNode* set_s =
      GetProperty(set, v8::HeapGraphEdge::kProperty, "str");
634 635
  CHECK(set_s);
  CHECK_EQ(s->GetId(), set_s->GetId());
636 637 638

  const v8::HeapGraphNode* map =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "map");
639
  CHECK(map);
640
  CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
641
  CHECK(v8_str("Map")->Equals(env.local(), map->GetName()).FromJust());
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657

  const v8::HeapGraphNode* map_table =
      GetProperty(map, v8::HeapGraphEdge::kInternal, "table");
  CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
  CHECK_GT(map_table->GetChildrenCount(), 0);
  entries = 0;
  for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = map_table->GetChild(i);
    const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
    if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
      ++entries;
    }
  }
  CHECK_EQ(2, entries);
  const v8::HeapGraphNode* map_s =
      GetProperty(map, v8::HeapGraphEdge::kProperty, "str");
658 659
  CHECK(map_s);
  CHECK_EQ(s->GetId(), map_s->GetId());
660 661 662
}


663
TEST(HeapSnapshotInternalReferences) {
664
  v8::Isolate* isolate = CcTest::isolate();
665
  v8::HandleScope scope(isolate);
666 667
  v8::Local<v8::ObjectTemplate> global_template =
      v8::ObjectTemplate::New(isolate);
668 669
  global_template->SetInternalFieldCount(2);
  LocalContext env(NULL, global_template);
670 671
  v8::Local<v8::Object> global_proxy = env->Global();
  v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
672
  CHECK_EQ(2, global->InternalFieldCount());
673
  v8::Local<v8::Object> obj = v8::Object::New(isolate);
674 675
  global->SetInternalField(0, v8_num(17));
  global->SetInternalField(1, obj);
676
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
677
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
678
  CHECK(ValidateSnapshot(snapshot));
679 680
  const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
  // The first reference will not present, because it's a Smi.
681
  CHECK(!GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
682
  // The second reference is to an object.
683
  CHECK(GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
684 685 686
}


687 688
TEST(HeapSnapshotAddressReuse) {
  LocalContext env;
689
  v8::HandleScope scope(env->GetIsolate());
690
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
691 692 693 694 695 696

  CompileRun(
      "function A() {}\n"
      "var a = [];\n"
      "for (var i = 0; i < 10000; ++i)\n"
      "  a[i] = new A();\n");
697
  const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
698
  CHECK(ValidateSnapshot(snapshot1));
699 700 701 702 703
  v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();

  CompileRun(
      "for (var i = 0; i < 10000; ++i)\n"
      "  a[i] = new A();\n");
704
  CcTest::heap()->CollectAllGarbage();
705

706
  const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
707
  CHECK(ValidateSnapshot(snapshot2));
708 709 710 711
  const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);

  const v8::HeapGraphNode* array_node =
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
712
  CHECK(array_node);
713 714 715 716 717 718 719 720 721
  int wrong_count = 0;
  for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = array_node->GetChild(i);
    if (prop->GetType() != v8::HeapGraphEdge::kElement)
      continue;
    v8::SnapshotObjectId id = prop->GetToNode()->GetId();
    if (id < maxId1)
      ++wrong_count;
  }
722
  CHECK_EQ(0, wrong_count);
723 724 725
}


726 727
TEST(HeapEntryIdsAndArrayShift) {
  LocalContext env;
728
  v8::HandleScope scope(env->GetIsolate());
729
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
730 731 732 733 734 735 736 737 738

  CompileRun(
      "function AnObject() {\n"
      "    this.first = 'first';\n"
      "    this.second = 'second';\n"
      "}\n"
      "var a = new Array();\n"
      "for (var i = 0; i < 10; ++i)\n"
      "  a.push(new AnObject());\n");
739
  const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
740
  CHECK(ValidateSnapshot(snapshot1));
741 742 743 744 745

  CompileRun(
      "for (var i = 0; i < 1; ++i)\n"
      "  a.shift();\n");

746
  CcTest::heap()->CollectAllGarbage();
747

748
  const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
749
  CHECK(ValidateSnapshot(snapshot2));
750 751 752

  const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
  const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
753 754
  CHECK_NE(0u, global1->GetId());
  CHECK_EQ(global1->GetId(), global2->GetId());
755 756 757

  const v8::HeapGraphNode* a1 =
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
758
  CHECK(a1);
759
  const v8::HeapGraphNode* k1 =
760
      GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
761
  CHECK(k1);
762 763
  const v8::HeapGraphNode* a2 =
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
764
  CHECK(a2);
765
  const v8::HeapGraphNode* k2 =
766
      GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
767
  CHECK(k2);
768

769 770
  CHECK_EQ(a1->GetId(), a2->GetId());
  CHECK_EQ(k1->GetId(), k2->GetId());
771 772
}

773

774 775
TEST(HeapEntryIdsAndGC) {
  LocalContext env;
776
  v8::HandleScope scope(env->GetIsolate());
777
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
778

779
  CompileRun(
780 781 782 783
      "function A() {}\n"
      "function B(x) { this.x = x; }\n"
      "var a = new A();\n"
      "var b = new B(a);");
784
  const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
785
  CHECK(ValidateSnapshot(snapshot1));
786

787
  CcTest::heap()->CollectAllGarbage();
788

789
  const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
790
  CHECK(ValidateSnapshot(snapshot2));
791

792
  CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000u);
793 794
  CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
        snapshot2->GetMaxSnapshotJSObjectId());
795 796 797

  const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
  const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
798 799
  CHECK_NE(0u, global1->GetId());
  CHECK_EQ(global1->GetId(), global2->GetId());
800
  const v8::HeapGraphNode* A1 =
801
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
802
  CHECK(A1);
803
  const v8::HeapGraphNode* A2 =
804
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
805 806 807
  CHECK(A2);
  CHECK_NE(0u, A1->GetId());
  CHECK_EQ(A1->GetId(), A2->GetId());
808
  const v8::HeapGraphNode* B1 =
809
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
810
  CHECK(B1);
811
  const v8::HeapGraphNode* B2 =
812
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
813 814 815
  CHECK(B2);
  CHECK_NE(0u, B1->GetId());
  CHECK_EQ(B1->GetId(), B2->GetId());
816
  const v8::HeapGraphNode* a1 =
817
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
818
  CHECK(a1);
819
  const v8::HeapGraphNode* a2 =
820
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
821 822 823
  CHECK(a2);
  CHECK_NE(0u, a1->GetId());
  CHECK_EQ(a1->GetId(), a2->GetId());
824
  const v8::HeapGraphNode* b1 =
825
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
826
  CHECK(b1);
827
  const v8::HeapGraphNode* b2 =
828
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
829 830 831
  CHECK(b2);
  CHECK_NE(0u, b1->GetId());
  CHECK_EQ(b1->GetId(), b2->GetId());
832 833 834
}


835 836
TEST(HeapSnapshotRootPreservedAfterSorting) {
  LocalContext env;
837
  v8::HandleScope scope(env->GetIsolate());
838
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
839
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
840
  CHECK(ValidateSnapshot(snapshot));
841 842 843 844 845 846 847 848
  const v8::HeapGraphNode* root1 = snapshot->GetRoot();
  const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
      snapshot))->GetSortedEntriesList();
  const v8::HeapGraphNode* root2 = snapshot->GetRoot();
  CHECK_EQ(root1, root2);
}


849 850 851 852
namespace {

class TestJSONStream : public v8::OutputStream {
 public:
853 854 855
  TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
  explicit TestJSONStream(int abort_countdown)
      : eos_signaled_(0), abort_countdown_(abort_countdown) {}
856 857
  virtual ~TestJSONStream() {}
  virtual void EndOfStream() { ++eos_signaled_; }
858 859 860
  virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
    if (abort_countdown_ > 0) --abort_countdown_;
    if (abort_countdown_ == 0) return kAbort;
861 862
    CHECK_GT(chars_written, 0);
    i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
863
    i::MemCopy(chunk.start(), buffer, chars_written);
864
    return kContinue;
865
  }
866
  virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
867
    CHECK(false);
868 869
    return kAbort;
  }
870 871 872
  void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
  int eos_signaled() { return eos_signaled_; }
  int size() { return buffer_.size(); }
873

874 875 876
 private:
  i::Collector<char> buffer_;
  int eos_signaled_;
877
  int abort_countdown_;
878 879
};

880
class OneByteResource : public v8::String::ExternalOneByteStringResource {
881
 public:
882
  explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
883 884 885 886 887 888 889 890 891 892 893 894
    length_ = string.length();
  }
  virtual const char* data() const { return data_; }
  virtual size_t length() const { return length_; }
 private:
  const char* data_;
  size_t length_;
};

}  // namespace

TEST(HeapSnapshotJSONSerialization) {
895
  v8::Isolate* isolate = CcTest::isolate();
896
  LocalContext env;
897 898
  v8::HandleScope scope(isolate);
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
899 900 901

#define STRING_LITERAL_FOR_TEST \
  "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
902
  CompileRun(
903 904 905 906
      "function A(s) { this.s = s; }\n"
      "function B(x) { this.x = x; }\n"
      "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
      "var b = new B(a);");
907
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
908
  CHECK(ValidateSnapshot(snapshot));
909

910 911 912 913 914 915 916 917
  TestJSONStream stream;
  snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
  CHECK_GT(stream.size(), 0);
  CHECK_EQ(1, stream.eos_signaled());
  i::ScopedVector<char> json(stream.size());
  stream.WriteTo(json);

  // Verify that snapshot string is valid JSON.
918
  OneByteResource* json_res = new OneByteResource(json);
919
  v8::Local<v8::String> json_string =
920 921 922 923 924
      v8::String::NewExternalOneByte(env->GetIsolate(), json_res)
          .ToLocalChecked();
  env->Global()
      ->Set(env.local(), v8_str("json_snapshot"), json_string)
      .FromJust();
925 926 927 928 929 930
  v8::Local<v8::Value> snapshot_parse_result = CompileRun(
      "var parsed = JSON.parse(json_snapshot); true;");
  CHECK(!snapshot_parse_result.IsEmpty());

  // Verify that snapshot object has required fields.
  v8::Local<v8::Object> parsed_snapshot =
931 932 933 934 935 936 937 938 939
      env->Global()
          ->Get(env.local(), v8_str("parsed"))
          .ToLocalChecked()
          ->ToObject(env.local())
          .ToLocalChecked();
  CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust());
  CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust());
  CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust());
  CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust());
940 941 942

  // Get node and edge "member" offsets.
  v8::Local<v8::Value> meta_analysis_result = CompileRun(
943
      "var meta = parsed.snapshot.meta;\n"
944
      "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
945 946 947 948 949
      "var node_fields_count = meta.node_fields.length;\n"
      "var edge_fields_count = meta.edge_fields.length;\n"
      "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
      "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
      "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
950
      "var property_type ="
951
      "    meta.edge_types[edge_type_offset].indexOf('property');\n"
952
      "var shortcut_type ="
953
      "    meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
954 955 956 957 958 959
      "var node_count = parsed.nodes.length / node_fields_count;\n"
      "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
      "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
      "  first_edge_indexes[i] = first_edge_index;\n"
      "  first_edge_index += edge_fields_count *\n"
      "      parsed.nodes[i * node_fields_count + edge_count_offset];\n"
960 961
      "}\n"
      "first_edge_indexes[node_count] = first_edge_index;\n");
962 963 964 965
  CHECK(!meta_analysis_result.IsEmpty());

  // A helper function for processing encoded nodes.
  CompileRun(
966
      "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
967
      "  var nodes = parsed.nodes;\n"
968
      "  var edges = parsed.edges;\n"
969
      "  var strings = parsed.strings;\n"
970 971 972
      "  var node_ordinal = pos / node_fields_count;\n"
      "  for (var i = parsed.first_edge_indexes[node_ordinal],\n"
      "      count = parsed.first_edge_indexes[node_ordinal + 1];\n"
973 974 975 976
      "      i < count; i += edge_fields_count) {\n"
      "    if (edges[i + edge_type_offset] === prop_type\n"
      "        && strings[edges[i + edge_name_offset]] === prop_name)\n"
      "      return edges[i + edge_to_node_offset];\n"
977 978 979 980 981 982 983 984
      "  }\n"
      "  return null;\n"
      "}\n");
  // Get the string index using the path: <root> -> <global>.b.x.s
  v8::Local<v8::Value> string_obj_pos_val = CompileRun(
      "GetChildPosByProperty(\n"
      "  GetChildPosByProperty(\n"
      "    GetChildPosByProperty("
985
      "      parsed.edges[edge_fields_count + edge_to_node_offset],"
986
      "      \"b\", property_type),\n"
987 988
      "    \"x\", property_type),"
      "  \"s\", property_type)");
989
  CHECK(!string_obj_pos_val.IsEmpty());
990 991
  int string_obj_pos = static_cast<int>(
      string_obj_pos_val->ToNumber(env.local()).ToLocalChecked()->Value());
992
  v8::Local<v8::Object> nodes_array =
993 994 995 996 997 998 999 1000 1001 1002
      parsed_snapshot->Get(env.local(), v8_str("nodes"))
          .ToLocalChecked()
          ->ToObject(env.local())
          .ToLocalChecked();
  int string_index =
      static_cast<int>(nodes_array->Get(env.local(), string_obj_pos + 1)
                           .ToLocalChecked()
                           ->ToNumber(env.local())
                           .ToLocalChecked()
                           ->Value());
1003 1004
  CHECK_GT(string_index, 0);
  v8::Local<v8::Object> strings_array =
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
      parsed_snapshot->Get(env.local(), v8_str("strings"))
          .ToLocalChecked()
          ->ToObject(env.local())
          .ToLocalChecked();
  v8::Local<v8::String> string = strings_array->Get(env.local(), string_index)
                                     .ToLocalChecked()
                                     ->ToString(env.local())
                                     .ToLocalChecked();
  v8::Local<v8::String> ref_string = CompileRun(STRING_LITERAL_FOR_TEST)
                                         ->ToString(env.local())
                                         .ToLocalChecked();
1016
#undef STRING_LITERAL_FOR_TEST
1017
  CHECK_EQ(0, strcmp(*v8::String::Utf8Value(ref_string),
1018
                     *v8::String::Utf8Value(string)));
1019 1020
}

1021 1022 1023

TEST(HeapSnapshotJSONSerializationAborting) {
  LocalContext env;
1024
  v8::HandleScope scope(env->GetIsolate());
1025
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1026
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1027
  CHECK(ValidateSnapshot(snapshot));
1028 1029 1030 1031 1032 1033
  TestJSONStream stream(5);
  snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
  CHECK_GT(stream.size(), 0);
  CHECK_EQ(0, stream.eos_signaled());
}

1034 1035 1036 1037 1038 1039
namespace {

class TestStatsStream : public v8::OutputStream {
 public:
  TestStatsStream()
    : eos_signaled_(0),
1040
      updates_written_(0),
1041
      entries_count_(0),
1042
      entries_size_(0),
1043 1044 1045
      intervals_count_(0),
      first_interval_index_(-1) { }
  TestStatsStream(const TestStatsStream& stream)
loislo@chromium.org's avatar
loislo@chromium.org committed
1046 1047
    : v8::OutputStream(stream),
      eos_signaled_(stream.eos_signaled_),
1048
      updates_written_(stream.updates_written_),
1049
      entries_count_(stream.entries_count_),
1050
      entries_size_(stream.entries_size_),
1051 1052 1053 1054 1055
      intervals_count_(stream.intervals_count_),
      first_interval_index_(stream.first_interval_index_) { }
  virtual ~TestStatsStream() {}
  virtual void EndOfStream() { ++eos_signaled_; }
  virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1056
    CHECK(false);
1057 1058
    return kAbort;
  }
1059 1060
  virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
                                          int updates_written) {
1061
    ++intervals_count_;
1062
    CHECK(updates_written);
1063
    updates_written_ += updates_written;
1064
    entries_count_ = 0;
1065 1066 1067 1068 1069
    if (first_interval_index_ == -1 && updates_written != 0)
      first_interval_index_ = buffer[0].index;
    for (int i = 0; i < updates_written; ++i) {
      entries_count_ += buffer[i].count;
      entries_size_ += buffer[i].size;
1070
    }
1071 1072 1073 1074

    return kContinue;
  }
  int eos_signaled() { return eos_signaled_; }
1075
  int updates_written() { return updates_written_; }
1076
  uint32_t entries_count() const { return entries_count_; }
1077
  uint32_t entries_size() const { return entries_size_; }
1078 1079 1080 1081 1082
  int intervals_count() const { return intervals_count_; }
  int first_interval_index() const { return first_interval_index_; }

 private:
  int eos_signaled_;
1083
  int updates_written_;
1084
  uint32_t entries_count_;
1085
  uint32_t entries_size_;
1086 1087 1088 1089 1090 1091
  int intervals_count_;
  int first_interval_index_;
};

}  // namespace

1092
static TestStatsStream GetHeapStatsUpdate(
1093
    v8::HeapProfiler* heap_profiler,
1094
    v8::SnapshotObjectId* object_id = NULL) {
1095
  TestStatsStream stream;
1096 1097 1098
  int64_t timestamp = -1;
  v8::SnapshotObjectId last_seen_id =
      heap_profiler->GetHeapStats(&stream, &timestamp);
1099 1100
  if (object_id)
    *object_id = last_seen_id;
1101
  CHECK_NE(-1, timestamp);
1102 1103 1104 1105 1106 1107 1108
  CHECK_EQ(1, stream.eos_signaled());
  return stream;
}


TEST(HeapSnapshotObjectsStats) {
  LocalContext env;
1109
  v8::HandleScope scope(env->GetIsolate());
1110
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1111

1112
  heap_profiler->StartTrackingHeapObjects();
1113
  // We have to call GC 6 times. In other case the garbage will be
1114
  // the reason of flakiness.
1115
  for (int i = 0; i < 6; ++i) {
1116
    CcTest::heap()->CollectAllGarbage();
1117 1118
  }

1119
  v8::SnapshotObjectId initial_id;
1120 1121
  {
    // Single chunk of data expected in update. Initial data.
1122 1123
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
                                                      &initial_id);
1124
    CHECK_EQ(1, stats_update.intervals_count());
1125
    CHECK_EQ(1, stats_update.updates_written());
1126
    CHECK_LT(0u, stats_update.entries_size());
1127 1128 1129 1130
    CHECK_EQ(0, stats_update.first_interval_index());
  }

  // No data expected in update because nothing has happened.
1131
  v8::SnapshotObjectId same_id;
1132
  CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
1133
  CHECK_EQ(initial_id, same_id);
1134

1135
  {
1136
    v8::SnapshotObjectId additional_string_id;
1137
    v8::HandleScope inner_scope_1(env->GetIsolate());
1138
    v8_str("string1");
1139 1140
    {
      // Single chunk of data with one new entry expected in update.
1141 1142
      TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
                                                        &additional_string_id);
1143
      CHECK_LT(same_id, additional_string_id);
1144
      CHECK_EQ(1, stats_update.intervals_count());
1145
      CHECK_EQ(1, stats_update.updates_written());
1146 1147
      CHECK_LT(0u, stats_update.entries_size());
      CHECK_EQ(1u, stats_update.entries_count());
1148 1149 1150 1151
      CHECK_EQ(2, stats_update.first_interval_index());
    }

    // No data expected in update because nothing happened.
1152
    v8::SnapshotObjectId last_id;
1153
    CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
1154
    CHECK_EQ(additional_string_id, last_id);
1155 1156

    {
1157
      v8::HandleScope inner_scope_2(env->GetIsolate());
1158
      v8_str("string2");
1159

1160
      uint32_t entries_size;
1161
      {
1162
        v8::HandleScope inner_scope_3(env->GetIsolate());
1163 1164
        v8_str("string3");
        v8_str("string4");
1165 1166 1167

        {
          // Single chunk of data with three new entries expected in update.
1168
          TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1169
          CHECK_EQ(1, stats_update.intervals_count());
1170
          CHECK_EQ(1, stats_update.updates_written());
1171 1172
          CHECK_LT(0u, entries_size = stats_update.entries_size());
          CHECK_EQ(3u, stats_update.entries_count());
1173 1174 1175 1176 1177 1178
          CHECK_EQ(4, stats_update.first_interval_index());
        }
      }

      {
        // Single chunk of data with two left entries expected in update.
1179
        TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1180
        CHECK_EQ(1, stats_update.intervals_count());
1181
        CHECK_EQ(1, stats_update.updates_written());
1182
        CHECK_GT(entries_size, stats_update.entries_size());
1183
        CHECK_EQ(1u, stats_update.entries_count());
1184 1185 1186 1187 1188 1189 1190
        // Two strings from forth interval were released.
        CHECK_EQ(4, stats_update.first_interval_index());
      }
    }

    {
      // Single chunk of data with 0 left entries expected in update.
1191
      TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1192
      CHECK_EQ(1, stats_update.intervals_count());
1193
      CHECK_EQ(1, stats_update.updates_written());
1194 1195
      CHECK_EQ(0u, stats_update.entries_size());
      CHECK_EQ(0u, stats_update.entries_count());
1196 1197 1198 1199 1200 1201
      // The last string from forth interval was released.
      CHECK_EQ(4, stats_update.first_interval_index());
    }
  }
  {
    // Single chunk of data with 0 left entries expected in update.
1202
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1203
    CHECK_EQ(1, stats_update.intervals_count());
1204
    CHECK_EQ(1, stats_update.updates_written());
1205 1206
    CHECK_EQ(0u, stats_update.entries_size());
    CHECK_EQ(0u, stats_update.entries_count());
1207 1208 1209 1210
    // The only string from the second interval was released.
    CHECK_EQ(2, stats_update.first_interval_index());
  }

1211
  v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
1212
  CHECK_EQ(0u, array->Length());
1213
  // Force array's buffer allocation.
1214
  array->Set(env.local(), 2, v8_num(7)).FromJust();
1215 1216 1217 1218

  uint32_t entries_size;
  {
    // Single chunk of data with 2 entries expected in update.
1219
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1220
    CHECK_EQ(1, stats_update.intervals_count());
1221
    CHECK_EQ(1, stats_update.updates_written());
1222
    CHECK_LT(0u, entries_size = stats_update.entries_size());
1223
    // They are the array and its buffer.
1224
    CHECK_EQ(2u, stats_update.entries_count());
1225 1226 1227 1228
    CHECK_EQ(8, stats_update.first_interval_index());
  }

  for (int i = 0; i < 100; ++i)
1229
    array->Set(env.local(), i, v8_num(i)).FromJust();
1230 1231 1232

  {
    // Single chunk of data with 1 entry expected in update.
1233
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1234 1235 1236
    CHECK_EQ(1, stats_update.intervals_count());
    // The first interval was changed because old buffer was collected.
    // The second interval was changed because new buffer was allocated.
1237
    CHECK_EQ(2, stats_update.updates_written());
1238
    CHECK_LT(entries_size, stats_update.entries_size());
1239
    CHECK_EQ(2u, stats_update.entries_count());
1240 1241 1242
    CHECK_EQ(8, stats_update.first_interval_index());
  }

1243
  heap_profiler->StopTrackingHeapObjects();
1244 1245
}

1246

1247 1248 1249 1250 1251 1252 1253
TEST(HeapObjectIds) {
  LocalContext env;
  v8::Isolate* isolate = env->GetIsolate();
  v8::HandleScope scope(isolate);
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  const int kLength = 10;
1254
  v8::Local<v8::Object> objects[kLength];
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
  v8::SnapshotObjectId ids[kLength];

  heap_profiler->StartTrackingHeapObjects(false);

  for (int i = 0; i < kLength; i++) {
    objects[i] = v8::Object::New(isolate);
  }
  GetHeapStatsUpdate(heap_profiler);

  for (int i = 0; i < kLength; i++) {
    v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1266
    CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1267 1268 1269 1270 1271 1272 1273 1274
    ids[i] = id;
  }

  heap_profiler->StopTrackingHeapObjects();
  CcTest::heap()->CollectAllAvailableGarbage();

  for (int i = 0; i < kLength; i++) {
    v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1275
    CHECK_EQ(ids[i], id);
1276 1277
    v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
    CHECK(objects[i]->Equals(env.local(), obj).FromJust());
1278 1279 1280 1281 1282
  }

  heap_profiler->ClearObjectIds();
  for (int i = 0; i < kLength; i++) {
    v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1283
    CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, id);
1284
    v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1285 1286 1287 1288 1289
    CHECK(obj.IsEmpty());
  }
}


1290 1291 1292 1293 1294 1295 1296 1297 1298
static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
                             const v8::HeapGraphNode* node,
                             int level, int max_level) {
  if (level > max_level) return;
  CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
  for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = node->GetChild(i);
    const v8::HeapGraphNode* child =
        snapshot->GetNodeById(prop->GetToNode()->GetId());
1299
    CHECK_EQ(prop->GetToNode()->GetId(), child->GetId());
1300 1301 1302 1303 1304 1305
    CHECK_EQ(prop->GetToNode(), child);
    CheckChildrenIds(snapshot, child, level + 1, max_level);
  }
}


1306 1307
TEST(HeapSnapshotGetNodeById) {
  LocalContext env;
1308
  v8::HandleScope scope(env->GetIsolate());
1309
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1310

1311
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1312
  CHECK(ValidateSnapshot(snapshot));
1313
  const v8::HeapGraphNode* root = snapshot->GetRoot();
1314
  CheckChildrenIds(snapshot, root, 0, 3);
1315
  // Check a big id, which should not exist yet.
1316
  CHECK(!snapshot->GetNodeById(0x1000000UL));
1317 1318
}

1319

1320 1321
TEST(HeapSnapshotGetSnapshotObjectId) {
  LocalContext env;
1322
  v8::HandleScope scope(env->GetIsolate());
1323
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1324
  CompileRun("globalObject = {};\n");
1325
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1326
  CHECK(ValidateSnapshot(snapshot));
1327 1328 1329 1330 1331
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* global_object =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
  CHECK(global_object);

1332 1333
  v8::Local<v8::Value> globalObjectHandle =
      env->Global()->Get(env.local(), v8_str("globalObject")).ToLocalChecked();
1334 1335 1336
  CHECK(!globalObjectHandle.IsEmpty());
  CHECK(globalObjectHandle->IsObject());

1337
  v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
1338 1339
  CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
  CHECK_EQ(id, global_object->GetId());
1340 1341 1342 1343 1344
}


TEST(HeapSnapshotUnknownSnapshotObjectId) {
  LocalContext env;
1345
  v8::HandleScope scope(env->GetIsolate());
1346
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1347
  CompileRun("globalObject = {};\n");
1348
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1349
  CHECK(ValidateSnapshot(snapshot));
1350 1351
  const v8::HeapGraphNode* node =
      snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
1352
  CHECK(!node);
1353 1354 1355
}


1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
namespace {

class TestActivityControl : public v8::ActivityControl {
 public:
  explicit TestActivityControl(int abort_count)
      : done_(0), total_(0), abort_count_(abort_count) {}
  ControlOption ReportProgressValue(int done, int total) {
    done_ = done;
    total_ = total;
    return --abort_count_ != 0 ? kContinue : kAbort;
  }
  int done() { return done_; }
  int total() { return total_; }

 private:
  int done_;
  int total_;
  int abort_count_;
};
1375 1376

}  // namespace
1377

1378

1379 1380
TEST(TakeHeapSnapshotAborting) {
  LocalContext env;
1381
  v8::HandleScope scope(env->GetIsolate());
1382

1383 1384
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  const int snapshots_count = heap_profiler->GetSnapshotCount();
1385
  TestActivityControl aborting_control(1);
1386
  const v8::HeapSnapshot* no_snapshot =
1387
      heap_profiler->TakeHeapSnapshot(&aborting_control);
1388
  CHECK(!no_snapshot);
1389
  CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1390 1391 1392
  CHECK_GT(aborting_control.total(), aborting_control.done());

  TestActivityControl control(-1);  // Don't abort.
1393
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
1394
  CHECK(ValidateSnapshot(snapshot));
1395

1396
  CHECK(snapshot);
1397
  CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1398 1399 1400 1401
  CHECK_EQ(control.total(), control.done());
  CHECK_GT(control.total(), 0);
}

1402 1403 1404 1405 1406 1407

namespace {

class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
 public:
  TestRetainedObjectInfo(int hash,
1408
                         const char* group_label,
1409 1410 1411 1412 1413
                         const char* label,
                         intptr_t element_count = -1,
                         intptr_t size = -1)
      : disposed_(false),
        hash_(hash),
1414
        group_label_(group_label),
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
        label_(label),
        element_count_(element_count),
        size_(size) {
    instances.Add(this);
  }
  virtual ~TestRetainedObjectInfo() {}
  virtual void Dispose() {
    CHECK(!disposed_);
    disposed_ = true;
  }
  virtual bool IsEquivalent(RetainedObjectInfo* other) {
    return GetHash() == other->GetHash();
  }
  virtual intptr_t GetHash() { return hash_; }
1429
  virtual const char* GetGroupLabel() { return group_label_; }
1430 1431 1432 1433 1434 1435
  virtual const char* GetLabel() { return label_; }
  virtual intptr_t GetElementCount() { return element_count_; }
  virtual intptr_t GetSizeInBytes() { return size_; }
  bool disposed() { return disposed_; }

  static v8::RetainedObjectInfo* WrapperInfoCallback(
1436
      uint16_t class_id, v8::Local<v8::Value> wrapper) {
1437 1438
    if (class_id == 1) {
      if (wrapper->IsString()) {
1439 1440
        v8::String::Utf8Value utf8(wrapper);
        if (strcmp(*utf8, "AAA") == 0)
1441
          return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1442
        else if (strcmp(*utf8, "BBB") == 0)
1443
          return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1444 1445 1446
      }
    } else if (class_id == 2) {
      if (wrapper->IsString()) {
1447 1448
        v8::String::Utf8Value utf8(wrapper);
        if (strcmp(*utf8, "CCC") == 0)
1449
          return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
      }
    }
    CHECK(false);
    return NULL;
  }

  static i::List<TestRetainedObjectInfo*> instances;

 private:
  bool disposed_;
  int hash_;
1461
  const char* group_label_;
1462 1463 1464 1465 1466 1467 1468
  const char* label_;
  intptr_t element_count_;
  intptr_t size_;
};


i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1469 1470

}  // namespace
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489


static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
                                        v8::HeapGraphNode::Type type,
                                        const char* name) {
  for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
    if (node->GetType() == type && strcmp(name,
               const_cast<i::HeapEntry*>(
                   reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
      return node;
    }
  }
  return NULL;
}


TEST(HeapSnapshotRetainedObjectInfo) {
  LocalContext env;
1490
  v8::Isolate* isolate = env->GetIsolate();
1491
  v8::HandleScope scope(isolate);
1492
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1493

1494
  heap_profiler->SetWrapperClassInfoProvider(
1495
      1, TestRetainedObjectInfo::WrapperInfoCallback);
1496
  heap_profiler->SetWrapperClassInfoProvider(
1497
      2, TestRetainedObjectInfo::WrapperInfoCallback);
1498
  v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1499
  p_AAA.SetWrapperClassId(1);
1500
  v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1501
  p_BBB.SetWrapperClassId(1);
1502
  v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1503
  p_CCC.SetWrapperClassId(2);
1504
  CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1505
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1506
  CHECK(ValidateSnapshot(snapshot));
1507 1508 1509 1510 1511 1512 1513

  CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
  for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
    CHECK(TestRetainedObjectInfo::instances[i]->disposed());
    delete TestRetainedObjectInfo::instances[i];
  }

1514
  const v8::HeapGraphNode* native_group_aaa = GetNode(
1515
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1516
  CHECK(native_group_aaa);
1517
  CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1518
  const v8::HeapGraphNode* aaa = GetNode(
1519
      native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1520
  CHECK(aaa);
1521 1522 1523
  CHECK_EQ(2, aaa->GetChildrenCount());

  const v8::HeapGraphNode* native_group_ccc = GetNode(
1524
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1525
  const v8::HeapGraphNode* ccc = GetNode(
1526
      native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1527
  CHECK(ccc);
1528 1529 1530

  const v8::HeapGraphNode* n_AAA = GetNode(
      aaa, v8::HeapGraphNode::kString, "AAA");
1531
  CHECK(n_AAA);
1532 1533
  const v8::HeapGraphNode* n_BBB = GetNode(
      aaa, v8::HeapGraphNode::kString, "BBB");
1534
  CHECK(n_BBB);
1535 1536 1537
  CHECK_EQ(1, ccc->GetChildrenCount());
  const v8::HeapGraphNode* n_CCC = GetNode(
      ccc, v8::HeapGraphNode::kString, "CCC");
1538
  CHECK(n_CCC);
1539

1540 1541 1542
  CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
  CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
  CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1543 1544
}

1545

1546 1547 1548 1549
class GraphWithImplicitRefs {
 public:
  static const int kObjectsCount = 4;
  explicit GraphWithImplicitRefs(LocalContext* env) {
1550
    CHECK(!instance_);
1551
    instance_ = this;
1552
    isolate_ = (*env)->GetIsolate();
1553
    for (int i = 0; i < kObjectsCount; i++) {
1554
      objects_[i].Reset(isolate_, v8::Object::New(isolate_));
1555
    }
1556 1557 1558 1559 1560
    (*env)
        ->Global()
        ->Set(isolate_->GetCurrentContext(), v8_str("root_object"),
              v8::Local<v8::Value>::New(isolate_, objects_[0]))
        .FromJust();
1561 1562 1563 1564 1565
  }
  ~GraphWithImplicitRefs() {
    instance_ = NULL;
  }

1566 1567
  static void gcPrologue(v8::Isolate* isolate, v8::GCType type,
                         v8::GCCallbackFlags flags) {
1568 1569 1570 1571 1572 1573
    instance_->AddImplicitReferences();
  }

 private:
  void AddImplicitReferences() {
    // 0 -> 1
1574
    isolate_->SetObjectGroupId(objects_[0],
1575 1576
                               v8::UniqueId(1));
    isolate_->SetReferenceFromGroup(
1577
        v8::UniqueId(1), objects_[1]);
1578
    // Adding two more references: 1 -> 2, 1 -> 3
1579 1580 1581 1582
    isolate_->SetReference(objects_[1].As<v8::Object>(),
                           objects_[2]);
    isolate_->SetReference(objects_[1].As<v8::Object>(),
                           objects_[3]);
1583 1584 1585 1586
  }

  v8::Persistent<v8::Value> objects_[kObjectsCount];
  static GraphWithImplicitRefs* instance_;
1587
  v8::Isolate* isolate_;
1588 1589 1590 1591 1592 1593 1594
};

GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;


TEST(HeapSnapshotImplicitReferences) {
  LocalContext env;
1595
  v8::HandleScope scope(env->GetIsolate());
1596
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1597 1598

  GraphWithImplicitRefs graph(&env);
1599
  env->GetIsolate()->AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1600

1601
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1602
  CHECK(ValidateSnapshot(snapshot));
1603 1604 1605

  const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* obj0 = GetProperty(
1606
      global_object, v8::HeapGraphEdge::kProperty, "root_object");
1607 1608 1609 1610 1611 1612 1613 1614
  CHECK(obj0);
  CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
  const v8::HeapGraphNode* obj1 = GetProperty(
      obj0, v8::HeapGraphEdge::kInternal, "native");
  CHECK(obj1);
  int implicit_targets_count = 0;
  for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
    const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1615
    v8::String::Utf8Value prop_name(prop->GetName());
1616 1617 1618 1619 1620 1621
    if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
        strcmp("native", *prop_name) == 0) {
      ++implicit_targets_count;
    }
  }
  CHECK_EQ(2, implicit_targets_count);
1622 1623
  env->GetIsolate()->RemoveGCPrologueCallback(
      &GraphWithImplicitRefs::gcPrologue);
1624 1625 1626
}


1627 1628
TEST(DeleteAllHeapSnapshots) {
  LocalContext env;
1629
  v8::HandleScope scope(env->GetIsolate());
1630 1631 1632 1633 1634
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
  heap_profiler->DeleteAllHeapSnapshots();
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1635
  CHECK(heap_profiler->TakeHeapSnapshot());
1636 1637 1638
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
  heap_profiler->DeleteAllHeapSnapshots();
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1639 1640
  CHECK(heap_profiler->TakeHeapSnapshot());
  CHECK(heap_profiler->TakeHeapSnapshot());
1641 1642 1643
  CHECK_EQ(2, heap_profiler->GetSnapshotCount());
  heap_profiler->DeleteAllHeapSnapshots();
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1644 1645 1646
}


1647 1648
static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
                             const v8::HeapSnapshot* snapshot) {
1649 1650
  int length = profiler->GetSnapshotCount();
  for (int i = 0; i < length; i++) {
1651
    if (snapshot == profiler->GetHeapSnapshot(i)) return true;
1652
  }
1653
  return false;
1654 1655 1656
}


1657 1658
TEST(DeleteHeapSnapshot) {
  LocalContext env;
1659
  v8::HandleScope scope(env->GetIsolate());
1660
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1661

1662
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1663
  const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
1664

1665
  CHECK(s1);
1666
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1667
  CHECK(FindHeapSnapshot(heap_profiler, s1));
1668
  const_cast<v8::HeapSnapshot*>(s1)->Delete();
1669
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1670
  CHECK(!FindHeapSnapshot(heap_profiler, s1));
1671

1672
  const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
1673
  CHECK(s2);
1674
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1675 1676
  CHECK(FindHeapSnapshot(heap_profiler, s2));
  const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
1677
  CHECK(s3);
1678
  CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1679 1680
  CHECK_NE(s2, s3);
  CHECK(FindHeapSnapshot(heap_profiler, s3));
1681
  const_cast<v8::HeapSnapshot*>(s2)->Delete();
1682
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1683 1684
  CHECK(!FindHeapSnapshot(heap_profiler, s2));
  CHECK(FindHeapSnapshot(heap_profiler, s3));
1685
  const_cast<v8::HeapSnapshot*>(s3)->Delete();
1686
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1687
  CHECK(!FindHeapSnapshot(heap_profiler, s3));
1688 1689
}

1690

1691 1692
class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
 public:
1693
  virtual const char* GetName(v8::Local<v8::Object> object) {
1694 1695 1696 1697
    return "Global object name";
  }
};

1698

1699 1700
TEST(GlobalObjectName) {
  LocalContext env;
1701
  v8::HandleScope scope(env->GetIsolate());
1702
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1703 1704 1705 1706 1707

  CompileRun("document = { URL:\"abcdefgh\" };");

  NameResolver name_resolver;
  const v8::HeapSnapshot* snapshot =
1708
      heap_profiler->TakeHeapSnapshot(NULL, &name_resolver);
1709
  CHECK(ValidateSnapshot(snapshot));
1710
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1711 1712 1713 1714 1715
  CHECK(global);
  CHECK_EQ(0,
           strcmp("Object / Global object name",
                  const_cast<i::HeapEntry*>(
                      reinterpret_cast<const i::HeapEntry*>(global))->name()));
1716 1717 1718
}


1719 1720 1721 1722 1723
TEST(GlobalObjectFields) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  CompileRun("obj = {};");
1724
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1725 1726 1727 1728
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* native_context =
      GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
1729
  CHECK(native_context);
1730 1731
  const v8::HeapGraphNode* global_proxy =
      GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
1732
  CHECK(global_proxy);
1733 1734 1735
}


1736 1737
TEST(NoHandleLeaks) {
  LocalContext env;
1738
  v8::HandleScope scope(env->GetIsolate());
1739
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1740 1741 1742

  CompileRun("document = { URL:\"abcdefgh\" };");

1743
  i::Isolate* isolate = CcTest::i_isolate();
1744
  int count_before = i::HandleScope::NumberOfHandles(isolate);
1745
  heap_profiler->TakeHeapSnapshot();
1746
  int count_after = i::HandleScope::NumberOfHandles(isolate);
1747 1748 1749 1750
  CHECK_EQ(count_before, count_after);
}


1751 1752
TEST(NodesIteration) {
  LocalContext env;
1753
  v8::HandleScope scope(env->GetIsolate());
1754
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1755
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1756
  CHECK(ValidateSnapshot(snapshot));
1757
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1758
  CHECK(global);
1759 1760 1761 1762 1763 1764 1765 1766 1767
  // Verify that we can find this object by iteration.
  const int nodes_count = snapshot->GetNodesCount();
  int count = 0;
  for (int i = 0; i < nodes_count; ++i) {
    if (snapshot->GetNode(i) == global)
      ++count;
  }
  CHECK_EQ(1, count);
}
1768 1769


1770
TEST(GetHeapValueForNode) {
1771
  LocalContext env;
1772
  v8::HandleScope scope(env->GetIsolate());
1773
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1774

1775
  CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
1776
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1777
  CHECK(ValidateSnapshot(snapshot));
1778
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1779
  CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
1780 1781
  v8::Local<v8::Object> js_global =
      env->Global()->GetPrototype().As<v8::Object>();
1782
  CHECK(js_global == heap_profiler->FindObjectById(global->GetId()));
1783
  const v8::HeapGraphNode* obj = GetProperty(
1784
      global, v8::HeapGraphEdge::kProperty, "a");
1785
  CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
1786 1787 1788
  v8::Local<v8::Object> js_obj = js_global->Get(env.local(), v8_str("a"))
                                     .ToLocalChecked()
                                     .As<v8::Object>();
1789
  CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId()));
1790 1791
  const v8::HeapGraphNode* s_prop =
      GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1792 1793 1794
  v8::Local<v8::String> js_s_prop = js_obj->Get(env.local(), v8_str("s_prop"))
                                        .ToLocalChecked()
                                        .As<v8::String>();
1795
  CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
1796 1797
  const v8::HeapGraphNode* n_prop =
      GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1798 1799 1800
  v8::Local<v8::String> js_n_prop = js_obj->Get(env.local(), v8_str("n_prop"))
                                        .ToLocalChecked()
                                        .As<v8::String>();
1801
  CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
1802 1803 1804 1805 1806
}


TEST(GetHeapValueForDeletedObject) {
  LocalContext env;
1807
  v8::HandleScope scope(env->GetIsolate());
1808
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1809 1810 1811 1812 1813

  // It is impossible to delete a global property, so we are about to delete a
  // property of the "a" object. Also, the "p" object can't be an empty one
  // because the empty object is static and isn't actually deleted.
  CompileRun("a = { p: { r: {} } };");
1814
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1815
  CHECK(ValidateSnapshot(snapshot));
1816 1817
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* obj = GetProperty(
1818
      global, v8::HeapGraphEdge::kProperty, "a");
1819 1820 1821 1822 1823
  const v8::HeapGraphNode* prop = GetProperty(
      obj, v8::HeapGraphEdge::kProperty, "p");
  {
    // Perform the check inside a nested local scope to avoid creating a
    // reference to the object we are deleting.
1824
    v8::HandleScope scope(env->GetIsolate());
1825
    CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
1826 1827
  }
  CompileRun("delete a.p;");
1828
  CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
1829 1830 1831
}


1832
static int StringCmp(const char* ref, i::String* act) {
rmcilroy's avatar
rmcilroy committed
1833
  v8::base::SmartArrayPointer<char> s_act = act->ToCString();
1834
  int result = strcmp(ref, s_act.get());
1835
  if (result != 0)
1836
    fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
1837 1838 1839 1840 1841 1842
  return result;
}


TEST(GetConstructorName) {
  LocalContext env;
1843
  v8::HandleScope scope(env->GetIsolate());
1844 1845 1846 1847 1848 1849 1850

  CompileRun(
      "function Constructor1() {};\n"
      "var obj1 = new Constructor1();\n"
      "var Constructor2 = function() {};\n"
      "var obj2 = new Constructor2();\n"
      "var obj3 = {};\n"
1851
      "obj3.__proto__ = { constructor: function Constructor3() {} };\n"
1852 1853 1854
      "var obj4 = {};\n"
      "// Slow properties\n"
      "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1855
      "obj4.__proto__ = { constructor: function Constructor4() {} };\n"
1856 1857 1858 1859 1860
      "var obj5 = {};\n"
      "var obj6 = {};\n"
      "obj6.constructor = 6;");
  v8::Local<v8::Object> js_global =
      env->Global()->GetPrototype().As<v8::Object>();
1861 1862 1863
  v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
                                   .ToLocalChecked()
                                   .As<v8::Object>();
1864 1865
  i::Handle<i::JSObject> js_obj1 =
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
1866 1867
  CHECK_EQ(0, StringCmp(
      "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1868 1869 1870
  v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
                                   .ToLocalChecked()
                                   .As<v8::Object>();
1871 1872
  i::Handle<i::JSObject> js_obj2 =
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
1873 1874
  CHECK_EQ(0, StringCmp(
      "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1875 1876 1877
  v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
                                   .ToLocalChecked()
                                   .As<v8::Object>();
1878 1879
  i::Handle<i::JSObject> js_obj3 =
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
1880 1881
  CHECK_EQ(0, StringCmp("Constructor3",
                        i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1882 1883 1884
  v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
                                   .ToLocalChecked()
                                   .As<v8::Object>();
1885 1886
  i::Handle<i::JSObject> js_obj4 =
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
1887 1888
  CHECK_EQ(0, StringCmp("Constructor4",
                        i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1889 1890 1891
  v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
                                   .ToLocalChecked()
                                   .As<v8::Object>();
1892 1893
  i::Handle<i::JSObject> js_obj5 =
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
1894 1895
  CHECK_EQ(0, StringCmp(
      "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1896 1897 1898
  v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
                                   .ToLocalChecked()
                                   .As<v8::Object>();
1899 1900
  i::Handle<i::JSObject> js_obj6 =
      i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
1901 1902 1903
  CHECK_EQ(0, StringCmp(
      "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
}
1904

1905

1906
TEST(FastCaseAccessors) {
1907
  LocalContext env;
1908
  v8::HandleScope scope(env->GetIsolate());
1909
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1910 1911 1912 1913 1914 1915 1916 1917

  CompileRun("var obj1 = {};\n"
             "obj1.__defineGetter__('propWithGetter', function Y() {\n"
             "  return 42;\n"
             "});\n"
             "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
             "  return this.value_ = value;\n"
             "});\n");
1918
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1919
  CHECK(ValidateSnapshot(snapshot));
1920 1921

  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1922
  CHECK(global);
1923
  const v8::HeapGraphNode* obj1 =
1924
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1925
  CHECK(obj1);
1926 1927
  const v8::HeapGraphNode* func;
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1928
  CHECK(func);
1929
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1930
  CHECK(!func);
1931
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1932
  CHECK(func);
1933
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1934
  CHECK(!func);
1935
}
1936

1937

1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958
TEST(FastCaseRedefinedAccessors) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  CompileRun(
      "var obj1 = {};\n"
      "Object.defineProperty(obj1, 'prop', { "
      "  get: function() { return 42; },\n"
      "  set: function(value) { return this.prop_ = value; },\n"
      "  configurable: true,\n"
      "  enumerable: true,\n"
      "});\n"
      "Object.defineProperty(obj1, 'prop', { "
      "  get: function() { return 153; },\n"
      "  set: function(value) { return this.prop_ = value; },\n"
      "  configurable: true,\n"
      "  enumerable: true,\n"
      "});\n");
  v8::Local<v8::Object> js_global =
      env->Global()->GetPrototype().As<v8::Object>();
1959
  i::Handle<i::JSReceiver> js_obj1 =
1960 1961 1962
      v8::Utils::OpenHandle(*js_global->Get(env.local(), v8_str("obj1"))
                                 .ToLocalChecked()
                                 .As<v8::Object>());
1963 1964
  USE(js_obj1);

1965
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1966 1967
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1968
  CHECK(global);
1969 1970
  const v8::HeapGraphNode* obj1 =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1971
  CHECK(obj1);
1972 1973
  const v8::HeapGraphNode* func;
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get prop");
1974
  CHECK(func);
1975
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set prop");
1976
  CHECK(func);
1977 1978 1979
}


1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992
TEST(SlowCaseAccessors) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  CompileRun("var obj1 = {};\n"
             "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
             "obj1.__defineGetter__('propWithGetter', function Y() {\n"
             "  return 42;\n"
             "});\n"
             "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
             "  return this.value_ = value;\n"
             "});\n");
1993
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1994
  CHECK(ValidateSnapshot(snapshot));
1995 1996

  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1997
  CHECK(global);
1998 1999
  const v8::HeapGraphNode* obj1 =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
2000
  CHECK(obj1);
2001 2002
  const v8::HeapGraphNode* func;
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
2003
  CHECK(func);
2004
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
2005
  CHECK(!func);
2006
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
2007
  CHECK(func);
2008
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
2009
  CHECK(!func);
2010 2011 2012
}


2013
TEST(HiddenPropertiesFastCase) {
2014
  v8::Isolate* isolate = CcTest::isolate();
2015
  LocalContext env;
2016 2017
  v8::HandleScope scope(isolate);
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2018 2019 2020 2021

  CompileRun(
      "function C(x) { this.a = this; this.b = x; }\n"
      "c = new C(2012);\n");
2022
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2023
  CHECK(ValidateSnapshot(snapshot));
2024 2025 2026
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* c =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
2027
  CHECK(c);
2028
  const v8::HeapGraphNode* hidden_props =
2029
      GetProperty(c, v8::HeapGraphEdge::kProperty, "<symbol>");
2030
  CHECK(!hidden_props);
2031

2032 2033
  v8::Local<v8::Value> cHandle =
      env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked();
2034
  CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
2035 2036
  cHandle->ToObject(env.local())
      .ToLocalChecked()
2037 2038 2039 2040
      ->SetPrivate(env.local(),
                   v8::Private::ForApi(env->GetIsolate(), v8_str("key")),
                   v8_str("val"))
      .FromJust();
2041

2042
  snapshot = heap_profiler->TakeHeapSnapshot();
2043
  CHECK(ValidateSnapshot(snapshot));
2044 2045
  global = GetGlobalObject(snapshot);
  c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
2046
  CHECK(c);
2047
  hidden_props = GetProperty(c, v8::HeapGraphEdge::kProperty, "<symbol>");
2048
  CHECK(hidden_props);
2049
}
2050

2051

2052 2053 2054 2055 2056 2057
TEST(AccessorInfo) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  CompileRun("function foo(x) { }\n");
2058
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2059 2060 2061 2062
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* foo =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2063
  CHECK(foo);
2064 2065
  const v8::HeapGraphNode* map =
      GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
2066
  CHECK(map);
2067 2068
  const v8::HeapGraphNode* descriptors =
      GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
2069
  CHECK(descriptors);
2070 2071
  const v8::HeapGraphNode* length_name =
      GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
2072 2073
  CHECK(length_name);
  CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(length_name->GetName())));
2074 2075
  const v8::HeapGraphNode* length_accessor =
      GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
2076
  CHECK(length_accessor);
2077
  CHECK_EQ(0, strcmp("system / AccessorInfo",
2078
                     *v8::String::Utf8Value(length_accessor->GetName())));
2079 2080
  const v8::HeapGraphNode* name =
      GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
2081
  CHECK(name);
2082 2083
  const v8::HeapGraphNode* getter =
      GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
2084
  CHECK(getter);
2085 2086
  const v8::HeapGraphNode* setter =
      GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
2087
  CHECK(setter);
2088 2089 2090
}


2091 2092 2093 2094 2095 2096 2097 2098 2099 2100
bool HasWeakEdge(const v8::HeapGraphNode* node) {
  for (int i = 0; i < node->GetChildrenCount(); ++i) {
    const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
    if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
  }
  return false;
}


bool HasWeakGlobalHandle() {
2101
  v8::Isolate* isolate = CcTest::isolate();
2102
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2103
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2104
  CHECK(ValidateSnapshot(snapshot));
2105
  const v8::HeapGraphNode* gc_roots = GetNode(
2106
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2107
  CHECK(gc_roots);
2108
  const v8::HeapGraphNode* global_handles = GetNode(
2109
      gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
2110
  CHECK(global_handles);
2111 2112 2113 2114
  return HasWeakEdge(global_handles);
}


2115
static void PersistentHandleCallback(
2116
    const v8::WeakCallbackInfo<v8::Persistent<v8::Object> >& data) {
2117
  data.GetParameter()->Reset();
2118 2119 2120 2121 2122
}


TEST(WeakGlobalHandle) {
  LocalContext env;
2123
  v8::HandleScope scope(env->GetIsolate());
2124 2125 2126

  CHECK(!HasWeakGlobalHandle());

2127 2128
  v8::Persistent<v8::Object> handle(env->GetIsolate(),
                                    v8::Object::New(env->GetIsolate()));
2129 2130
  handle.SetWeak(&handle, PersistentHandleCallback,
                 v8::WeakCallbackType::kParameter);
2131 2132 2133 2134 2135 2136 2137

  CHECK(HasWeakGlobalHandle());
}


TEST(SfiAndJsFunctionWeakRefs) {
  LocalContext env;
2138
  v8::HandleScope scope(env->GetIsolate());
2139
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2140 2141 2142

  CompileRun(
      "fun = (function (x) { return function () { return x + 1; } })(1);");
2143
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2144
  CHECK(ValidateSnapshot(snapshot));
2145
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2146
  CHECK(global);
2147
  const v8::HeapGraphNode* fun =
2148
      GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2149
  CHECK(!HasWeakEdge(fun));
2150 2151
  const v8::HeapGraphNode* shared =
      GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
2152
  CHECK(!HasWeakEdge(shared));
2153
}
2154 2155


2156 2157
TEST(NoDebugObjectInSnapshot) {
  LocalContext env;
2158
  v8::HandleScope scope(env->GetIsolate());
2159
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2160

2161
  CHECK(CcTest::i_isolate()->debug()->Load());
2162
  CompileRun("foo = {};");
2163
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2164
  CHECK(ValidateSnapshot(snapshot));
2165 2166 2167 2168 2169 2170 2171 2172 2173
  const v8::HeapGraphNode* root = snapshot->GetRoot();
  int globals_count = 0;
  for (int i = 0; i < root->GetChildrenCount(); ++i) {
    const v8::HeapGraphEdge* edge = root->GetChild(i);
    if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
      ++globals_count;
      const v8::HeapGraphNode* global = edge->GetToNode();
      const v8::HeapGraphNode* foo =
          GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2174
      CHECK(foo);
2175 2176
    }
  }
2177
  CHECK_EQ(1, globals_count);
2178 2179 2180
}


2181 2182
TEST(AllStrongGcRootsHaveNames) {
  LocalContext env;
2183
  v8::HandleScope scope(env->GetIsolate());
2184
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2185 2186

  CompileRun("foo = {};");
2187
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2188
  CHECK(ValidateSnapshot(snapshot));
2189
  const v8::HeapGraphNode* gc_roots = GetNode(
2190
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2191
  CHECK(gc_roots);
2192
  const v8::HeapGraphNode* strong_roots = GetNode(
2193
      gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
2194
  CHECK(strong_roots);
2195 2196 2197
  for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
    const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
    CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
2198
    v8::String::Utf8Value name(edge->GetName());
2199 2200 2201
    CHECK(isalpha(**name));
  }
}
2202 2203 2204 2205


TEST(NoRefsToNonEssentialEntries) {
  LocalContext env;
2206
  v8::HandleScope scope(env->GetIsolate());
2207
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2208
  CompileRun("global_object = {};\n");
2209
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2210
  CHECK(ValidateSnapshot(snapshot));
2211 2212 2213
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* global_object =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
2214
  CHECK(global_object);
2215 2216
  const v8::HeapGraphNode* properties =
      GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
2217
  CHECK(!properties);
2218 2219
  const v8::HeapGraphNode* elements =
      GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
2220
  CHECK(!elements);
2221
}
2222 2223 2224 2225


TEST(MapHasDescriptorsAndTransitions) {
  LocalContext env;
2226
  v8::HandleScope scope(env->GetIsolate());
2227
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2228
  CompileRun("obj = { a: 10 };\n");
2229
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2230
  CHECK(ValidateSnapshot(snapshot));
2231 2232 2233
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* global_object =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2234
  CHECK(global_object);
2235

2236 2237
  const v8::HeapGraphNode* map =
      GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
2238
  CHECK(map);
2239 2240
  const v8::HeapGraphNode* own_descriptors = GetProperty(
      map, v8::HeapGraphEdge::kInternal, "descriptors");
2241
  CHECK(own_descriptors);
2242 2243
  const v8::HeapGraphNode* own_transitions = GetProperty(
      map, v8::HeapGraphEdge::kInternal, "transitions");
2244
  CHECK(!own_transitions);
2245
}
2246 2247 2248 2249


TEST(ManyLocalsInSharedContext) {
  LocalContext env;
2250 2251
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2252
  int num_objects = 6000;
2253
  CompileRun(
2254
      "var n = 6000;"
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266
      "var result = [];"
      "result.push('(function outer() {');"
      "for (var i = 0; i < n; i++) {"
      "    var f = 'function f_' + i + '() { ';"
      "    if (i > 0)"
      "        f += 'f_' + (i - 1) + '();';"
      "    f += ' }';"
      "    result.push(f);"
      "}"
      "result.push('return f_' + (n - 1) + ';');"
      "result.push('})()');"
      "var ok = eval(result.join('\\n'));");
2267
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2268
  CHECK(ValidateSnapshot(snapshot));
2269

2270
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2271
  CHECK(global);
2272 2273
  const v8::HeapGraphNode* ok_object =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
2274
  CHECK(ok_object);
2275 2276
  const v8::HeapGraphNode* context_object =
      GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
2277
  CHECK(context_object);
2278 2279 2280 2281
  // Check the objects are not duplicated in the context.
  CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
           context_object->GetChildrenCount());
  // Check all the objects have got their names.
2282 2283
  // ... well check just every 15th because otherwise it's too slow in debug.
  for (int i = 0; i < num_objects - 1; i += 15) {
alph@chromium.org's avatar
alph@chromium.org committed
2284
    i::EmbeddedVector<char, 100> var_name;
2285
    i::SNPrintF(var_name, "f_%d", i);
2286
    const v8::HeapGraphNode* f_object = GetProperty(
alph@chromium.org's avatar
alph@chromium.org committed
2287
        context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
2288
    CHECK(f_object);
2289 2290
  }
}
2291 2292 2293 2294


TEST(AllocationSitesAreVisible) {
  LocalContext env;
2295 2296 2297
  v8::Isolate* isolate = env->GetIsolate();
  v8::HandleScope scope(isolate);
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2298 2299 2300
  CompileRun(
      "fun = function () { var a = [3, 2, 1]; return a; }\n"
      "fun();");
2301
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2302
  CHECK(ValidateSnapshot(snapshot));
2303 2304

  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2305
  CHECK(global);
2306 2307
  const v8::HeapGraphNode* fun_code =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2308
  CHECK(fun_code);
2309 2310
  const v8::HeapGraphNode* literals =
      GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
2311
  CHECK(literals);
2312
  CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
2313
  CHECK_EQ(1, literals->GetChildrenCount());
2314

2315
  // The first value in the literals array should be the boilerplate,
2316
  // after an AllocationSite.
2317
  const v8::HeapGraphEdge* prop = literals->GetChild(0);
2318 2319
  const v8::HeapGraphNode* allocation_site = prop->GetToNode();
  v8::String::Utf8Value name(allocation_site->GetName());
2320
  CHECK_EQ(0, strcmp("system / AllocationSite", *name));
2321 2322 2323
  const v8::HeapGraphNode* transition_info =
      GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
                  "transition_info");
2324
  CHECK(transition_info);
2325 2326 2327 2328

  const v8::HeapGraphNode* elements =
      GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
                  "elements");
2329
  CHECK(elements);
2330
  CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
2331 2332
  CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
           static_cast<int>(elements->GetShallowSize()));
2333

2334
  v8::Local<v8::Value> array_val =
2335 2336
      heap_profiler->FindObjectById(transition_info->GetId());
  CHECK(array_val->IsArray());
2337
  v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(array_val);
2338
  // Verify the array is "a" in the code above.
2339 2340
  CHECK_EQ(3u, array->Length());
  CHECK(v8::Integer::New(isolate, 3)
2341 2342 2343 2344
            ->Equals(env.local(),
                     array->Get(env.local(), v8::Integer::New(isolate, 0))
                         .ToLocalChecked())
            .FromJust());
2345
  CHECK(v8::Integer::New(isolate, 2)
2346 2347 2348 2349
            ->Equals(env.local(),
                     array->Get(env.local(), v8::Integer::New(isolate, 1))
                         .ToLocalChecked())
            .FromJust());
2350
  CHECK(v8::Integer::New(isolate, 1)
2351 2352 2353 2354
            ->Equals(env.local(),
                     array->Get(env.local(), v8::Integer::New(isolate, 2))
                         .ToLocalChecked())
            .FromJust());
2355
}
2356 2357 2358 2359 2360 2361 2362


TEST(JSFunctionHasCodeLink) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  CompileRun("function foo(x, y) { return x + y; }\n");
2363
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2364 2365 2366 2367
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* foo_func =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2368
  CHECK(foo_func);
2369 2370
  const v8::HeapGraphNode* code =
      GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
2371
  CHECK(code);
2372
}
2373 2374


2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386
static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot,
                                              const char* path[],
                                              int depth) {
  const v8::HeapGraphNode* node = snapshot->GetRoot();
  for (int current_depth = 0; current_depth < depth; ++current_depth) {
    int i, count = node->GetChildrenCount();
    for (i = 0; i < count; ++i) {
      const v8::HeapGraphEdge* edge = node->GetChild(i);
      const v8::HeapGraphNode* to_node = edge->GetToNode();
      v8::String::Utf8Value edge_name(edge->GetName());
      v8::String::Utf8Value node_name(to_node->GetName());
      i::EmbeddedVector<char, 100> name;
2387
      i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403
      if (strstr(name.start(), path[current_depth])) {
        node = to_node;
        break;
      }
    }
    if (i == count) return NULL;
  }
  return node;
}


TEST(CheckCodeNames) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  CompileRun("var a = 1.1;");
2404
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2405 2406 2407 2408 2409 2410 2411 2412 2413
  CHECK(ValidateSnapshot(snapshot));

  const char* stub_path[] = {
    "::(GC roots)",
    "::(Strong roots)",
    "code_stubs::",
    "::(ArraySingleArgumentConstructorStub code)"
  };
  const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
2414
      stub_path, arraysize(stub_path));
2415
  CHECK(node);
2416

2417 2418
  const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
                                 "::(KeyedLoadIC_Megamorphic builtin)"};
2419
  node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
2420
  CHECK(node);
2421

2422 2423
  const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
                                 "::(CompileLazy builtin)"};
2424
  node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
2425
  CHECK(node);
2426
  v8::String::Utf8Value node_name(node->GetName());
2427
  CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
2428
}
2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480


static const char* record_trace_tree_source =
"var topFunctions = [];\n"
"var global = this;\n"
"function generateFunctions(width, depth) {\n"
"  var script = [];\n"
"  for (var i = 0; i < width; i++) {\n"
"    for (var j = 0; j < depth; j++) {\n"
"      script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
"      script.push('  try {\\n');\n"
"      if (j < depth-2) {\n"
"        script.push('    return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
"      } else if (j == depth - 2) {\n"
"        script.push('    return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
"      } else if (j == depth - 1) {\n"
"        script.push('    this.ts = Date.now();\\n');\n"
"      }\n"
"      script.push('  } catch (e) {}\\n');\n"
"      script.push('}\\n');\n"
"      \n"
"    }\n"
"  }\n"
"  var script = script.join('');\n"
"  // throw script;\n"
"  global.eval(script);\n"
"  for (var i = 0; i < width; i++) {\n"
"    topFunctions.push(this['f_' + i + '_0']);\n"
"  }\n"
"}\n"
"\n"
"var width = 3;\n"
"var depth = 3;\n"
"generateFunctions(width, depth);\n"
"var instances = [];\n"
"function start() {\n"
"  for (var i = 0; i < width; i++) {\n"
"    instances.push(topFunctions[i](0));\n"
"  }\n"
"}\n"
"\n"
"for (var i = 0; i < 100; i++) start();\n";


static AllocationTraceNode* FindNode(
    AllocationTracker* tracker, const Vector<const char*>& names) {
  AllocationTraceNode* node = tracker->trace_tree()->root();
  for (int i = 0; node != NULL && i < names.length(); i++) {
    const char* name = names[i];
    Vector<AllocationTraceNode*> children = node->children();
    node = NULL;
    for (int j = 0; j < children.length(); j++) {
2481 2482 2483
      unsigned index = children[j]->function_info_index();
      AllocationTracker::FunctionInfo* info =
          tracker->function_info_list()[index];
2484 2485 2486 2487 2488 2489 2490 2491 2492 2493
      if (info && strcmp(info->name, name) == 0) {
        node = children[j];
        break;
      }
    }
  }
  return node;
}


2494 2495 2496 2497
TEST(ArrayGrowLeftTrim) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2498
  heap_profiler->StartTrackingHeapObjects(true);
2499 2500 2501 2502 2503 2504 2505 2506

  CompileRun(
    "var a = [];\n"
    "for (var i = 0; i < 5; ++i)\n"
    "    a[i] = i;\n"
    "for (var i = 0; i < 3; ++i)\n"
    "    a.shift();\n");

2507
  const char* names[] = {""};
2508 2509
  AllocationTracker* tracker =
      reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2510
  CHECK(tracker);
2511 2512 2513 2514 2515
  // Resolve all function locations.
  tracker->PrepareForSerialization();
  // Print for better diagnostics in case of failure.
  tracker->trace_tree()->Print(tracker);

2516
  AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2517 2518 2519
  CHECK(node);
  CHECK_GE(node->allocation_count(), 2u);
  CHECK_GE(node->allocation_size(), 4u * 5u);
2520
  heap_profiler->StopTrackingHeapObjects();
2521 2522 2523
}


2524 2525 2526 2527 2528
TEST(TrackHeapAllocations) {
  v8::HandleScope scope(v8::Isolate::GetCurrent());
  LocalContext env;

  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2529
  heap_profiler->StartTrackingHeapObjects(true);
2530 2531 2532

  CompileRun(record_trace_tree_source);

2533 2534
  AllocationTracker* tracker =
      reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2535
  CHECK(tracker);
2536 2537 2538 2539 2540
  // Resolve all function locations.
  tracker->PrepareForSerialization();
  // Print for better diagnostics in case of failure.
  tracker->trace_tree()->Print(tracker);

2541
  const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2542
  AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2543 2544
  CHECK(node);
  CHECK_GE(node->allocation_count(), 100u);
2545
  CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2546
  heap_profiler->StopTrackingHeapObjects();
2547
}
2548 2549 2550


static const char* inline_heap_allocation_source =
2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567
    "function f_0(x) {\n"
    "  return f_1(x+1);\n"
    "}\n"
    "%NeverOptimizeFunction(f_0);\n"
    "function f_1(x) {\n"
    "  return new f_2(x+1);\n"
    "}\n"
    "%NeverOptimizeFunction(f_1);\n"
    "function f_2(x) {\n"
    "  this.foo = x;\n"
    "}\n"
    "var instances = [];\n"
    "function start() {\n"
    "  instances.push(f_0(0));\n"
    "}\n"
    "\n"
    "for (var i = 0; i < 100; i++) start();\n";
2568 2569 2570 2571 2572 2573 2574 2575


TEST(TrackBumpPointerAllocations) {
  i::FLAG_allow_natives_syntax = true;
  v8::HandleScope scope(v8::Isolate::GetCurrent());
  LocalContext env;

  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2576
  const char* names[] = {"", "start", "f_0", "f_1"};
2577 2578
  // First check that normally all allocations are recorded.
  {
2579
    heap_profiler->StartTrackingHeapObjects(true);
2580 2581 2582

    CompileRun(inline_heap_allocation_source);

2583 2584
    AllocationTracker* tracker =
        reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2585
    CHECK(tracker);
2586 2587 2588 2589 2590
    // Resolve all function locations.
    tracker->PrepareForSerialization();
    // Print for better diagnostics in case of failure.
    tracker->trace_tree()->Print(tracker);

2591
    AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2592 2593
    CHECK(node);
    CHECK_GE(node->allocation_count(), 100u);
2594
    CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2595
    heap_profiler->StopTrackingHeapObjects();
2596 2597 2598
  }

  {
2599
    heap_profiler->StartTrackingHeapObjects(true);
2600 2601 2602 2603 2604 2605 2606 2607

    // Now check that not all allocations are tracked if we manually reenable
    // inline allocations.
    CHECK(CcTest::heap()->inline_allocation_disabled());
    CcTest::heap()->EnableInlineAllocation();

    CompileRun(inline_heap_allocation_source);

2608 2609
    AllocationTracker* tracker =
        reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2610
    CHECK(tracker);
2611 2612 2613 2614 2615
    // Resolve all function locations.
    tracker->PrepareForSerialization();
    // Print for better diagnostics in case of failure.
    tracker->trace_tree()->Print(tracker);

2616
    AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2617 2618
    CHECK(node);
    CHECK_LT(node->allocation_count(), 100u);
2619 2620

    CcTest::heap()->DisableInlineAllocation();
2621
    heap_profiler->StopTrackingHeapObjects();
2622 2623
  }
}
2624 2625


2626 2627 2628 2629 2630 2631 2632 2633
TEST(TrackV8ApiAllocation) {
  v8::HandleScope scope(v8::Isolate::GetCurrent());
  LocalContext env;

  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  const char* names[] = { "(V8 API)" };
  heap_profiler->StartTrackingHeapObjects(true);

2634
  v8::Local<v8::Object> o1 = v8::Object::New(env->GetIsolate());
2635 2636 2637 2638
  o1->Clone();

  AllocationTracker* tracker =
      reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2639
  CHECK(tracker);
2640 2641 2642 2643 2644
  // Resolve all function locations.
  tracker->PrepareForSerialization();
  // Print for better diagnostics in case of failure.
  tracker->trace_tree()->Print(tracker);

2645
  AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
2646 2647
  CHECK(node);
  CHECK_GE(node->allocation_count(), 2u);
2648 2649 2650 2651 2652
  CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
  heap_profiler->StopTrackingHeapObjects();
}


2653 2654 2655 2656 2657
TEST(ArrayBufferAndArrayBufferView) {
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  CompileRun("arr1 = new Uint32Array(100);\n");
2658
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2659 2660 2661 2662
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* arr1_obj =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1");
2663
  CHECK(arr1_obj);
2664 2665
  const v8::HeapGraphNode* arr1_buffer =
      GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
2666
  CHECK(arr1_buffer);
2667 2668
  const v8::HeapGraphNode* backing_store =
      GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store");
2669
  CHECK(backing_store);
2670
  CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
2671
}
2672 2673


2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690
static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
                             const v8::HeapGraphNode* node) {
  int count = 0;
  for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
    const v8::HeapGraphNode* parent = snapshot->GetNode(i);
    for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
      if (parent->GetChild(j)->GetToNode() == node) {
        ++count;
      }
    }
  }
  return count;
}


TEST(ArrayBufferSharedBackingStore) {
  LocalContext env;
2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702
  v8::Isolate* isolate = env->GetIsolate();
  v8::HandleScope handle_scope(isolate);
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();

  v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
  CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
  CHECK(!ab->IsExternal());
  v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
  CHECK(ab->IsExternal());

  CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
  void* data = ab_contents.Data();
2703
  CHECK(data != NULL);
2704 2705 2706
  v8::Local<v8::ArrayBuffer> ab2 =
      v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
  CHECK(ab2->IsExternal());
2707 2708
  env->Global()->Set(env.local(), v8_str("ab1"), ab).FromJust();
  env->Global()->Set(env.local(), v8_str("ab2"), ab2).FromJust();
2709

2710 2711
  v8::Local<v8::Value> result = CompileRun("ab2.byteLength");
  CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
2712

2713
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2714
  CHECK(ValidateSnapshot(snapshot));
2715 2716 2717
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* ab1_node =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1");
2718
  CHECK(ab1_node);
2719 2720
  const v8::HeapGraphNode* ab1_data =
      GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store");
2721
  CHECK(ab1_data);
2722 2723
  const v8::HeapGraphNode* ab2_node =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2");
2724
  CHECK(ab2_node);
2725 2726
  const v8::HeapGraphNode* ab2_data =
      GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store");
2727
  CHECK(ab2_data);
2728 2729 2730
  CHECK_EQ(ab1_data, ab2_data);
  CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
  free(data);
2731 2732 2733
}


2734 2735 2736 2737
TEST(BoxObject) {
  v8::Isolate* isolate = CcTest::isolate();
  v8::HandleScope scope(isolate);
  LocalContext env;
2738 2739
  v8::Local<v8::Object> global_proxy = env->Global();
  v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
2740 2741

  i::Factory* factory = CcTest::i_isolate()->factory();
2742
  i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
2743
  i::Handle<i::Object> box = factory->NewBox(string);
2744
  global->Set(env.local(), 0, v8::ToApiHandle<v8::Object>(box)).FromJust();
2745 2746

  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2747
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2748 2749 2750 2751
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* box_node =
      GetProperty(global_node, v8::HeapGraphEdge::kElement, "0");
2752
  CHECK(box_node);
2753
  v8::String::Utf8Value box_node_name(box_node->GetName());
2754
  CHECK_EQ(0, strcmp("system / Box", *box_node_name));
2755 2756
  const v8::HeapGraphNode* box_value =
      GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value");
2757
  CHECK(box_value);
2758
}
2759 2760


2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773
TEST(WeakContainers) {
  i::FLAG_allow_natives_syntax = true;
  LocalContext env;
  v8::HandleScope scope(env->GetIsolate());
  if (!CcTest::i_isolate()->use_crankshaft()) return;
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
  CompileRun(
      "function foo(a) { return a.x; }\n"
      "obj = {x : 123};\n"
      "foo(obj);\n"
      "foo(obj);\n"
      "%OptimizeFunctionOnNextCall(foo);\n"
      "foo(obj);\n");
2774
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2775 2776 2777 2778
  CHECK(ValidateSnapshot(snapshot));
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
  const v8::HeapGraphNode* obj =
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2779
  CHECK(obj);
2780 2781
  const v8::HeapGraphNode* map =
      GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
2782
  CHECK(map);
2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794
  const v8::HeapGraphNode* dependent_code =
      GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
  if (!dependent_code) return;
  int count = dependent_code->GetChildrenCount();
  CHECK_NE(0, count);
  for (int i = 0; i < count; ++i) {
    const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
    CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
  }
}


2795 2796 2797 2798 2799 2800 2801 2802
static inline i::Address ToAddress(int n) {
  return reinterpret_cast<i::Address>(n);
}


TEST(AddressToTraceMap) {
  i::AddressToTraceMap map;

2803
  CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
2804 2805 2806

  // [0x100, 0x200) -> 1
  map.AddRange(ToAddress(0x100), 0x100, 1U);
2807 2808 2809 2810 2811
  CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
  CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
  CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
  CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
  CHECK_EQ(1u, map.size());
2812 2813 2814

  // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
  map.AddRange(ToAddress(0x200), 0x100, 2U);
2815 2816
  CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2a0)));
  CHECK_EQ(2u, map.size());
2817 2818 2819

  // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
  map.AddRange(ToAddress(0x180), 0x100, 3U);
2820 2821 2822 2823
  CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
  CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
  CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
  CHECK_EQ(3u, map.size());
2824 2825 2826 2827

  // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
  // [0x400, 0x500) -> 4
  map.AddRange(ToAddress(0x400), 0x100, 4U);
2828 2829 2830 2831 2832 2833 2834
  CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
  CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
  CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
  CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
  CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
  CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
  CHECK_EQ(4u, map.size());
2835 2836 2837

  // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
  map.AddRange(ToAddress(0x200), 0x400, 5U);
2838 2839 2840
  CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
  CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
  CHECK_EQ(3u, map.size());
2841 2842 2843 2844

  // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
  map.AddRange(ToAddress(0x180), 0x80, 6U);
  map.AddRange(ToAddress(0x180), 0x80, 7U);
2845 2846 2847
  CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
  CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
  CHECK_EQ(3u, map.size());
2848 2849

  map.Clear();
2850 2851
  CHECK_EQ(0u, map.size());
  CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));
2852
}
2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872


static const v8::AllocationProfile::Node* FindAllocationProfileNode(
    v8::AllocationProfile& profile, const Vector<const char*>& names) {
  v8::AllocationProfile::Node* node = profile.GetRootNode();
  for (int i = 0; node != nullptr && i < names.length(); ++i) {
    const char* name = names[i];
    auto children = node->children;
    node = nullptr;
    for (v8::AllocationProfile::Node* child : children) {
      v8::String::Utf8Value child_name(child->name);
      if (strcmp(*child_name, name) == 0) {
        node = child;
        break;
      }
    }
  }
  return node;
}

2873 2874 2875 2876 2877 2878 2879 2880 2881
static void CheckNoZeroCountNodes(v8::AllocationProfile::Node* node) {
  for (auto alloc : node->allocations) {
    CHECK_GT(alloc.count, 0u);
  }
  for (auto child : node->children) {
    CheckNoZeroCountNodes(child);
  }
}

2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909
TEST(SamplingHeapProfiler) {
  v8::HandleScope scope(v8::Isolate::GetCurrent());
  LocalContext env;
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  // Turn off always_opt. Inlining can cause stack traces to be shorter than
  // what we expect in this test.
  v8::internal::FLAG_always_opt = false;

  // Suppress randomness to avoid flakiness in tests.
  v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;

  const char* script_source =
      "var A = [];\n"
      "function bar(size) { return new Array(size); }\n"
      "var foo = function() {\n"
      "  for (var i = 0; i < 1024; ++i) {\n"
      "    A[i] = bar(1024);\n"
      "  }\n"
      "}\n"
      "foo();";

  // Sample should be empty if requested before sampling has started.
  {
    v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
    CHECK(profile == nullptr);
  }

2910
  int count_1024 = 0;
2911
  {
2912
    heap_profiler->StartSamplingHeapProfiler(1024);
2913 2914 2915 2916 2917 2918 2919
    CompileRun(script_source);

    v8::base::SmartPointer<v8::AllocationProfile> profile(
        heap_profiler->GetAllocationProfile());
    CHECK(!profile.is_empty());

    const char* names[] = {"", "foo", "bar"};
2920
    auto node_bar = FindAllocationProfileNode(*profile, ArrayVector(names));
2921 2922 2923 2924
    CHECK(node_bar);

    // Count the number of allocations we sampled from bar.
    for (auto allocation : node_bar->allocations) {
2925
      count_1024 += allocation.count;
2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936
    }

    heap_profiler->StopSamplingHeapProfiler();
  }

  // Samples should get cleared once sampling is stopped.
  {
    v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
    CHECK(profile == nullptr);
  }

2937
  // Sampling at a higher rate should give us similar numbers of objects.
2938
  {
2939
    heap_profiler->StartSamplingHeapProfiler(128);
2940 2941 2942 2943 2944 2945 2946
    CompileRun(script_source);

    v8::base::SmartPointer<v8::AllocationProfile> profile(
        heap_profiler->GetAllocationProfile());
    CHECK(!profile.is_empty());

    const char* names[] = {"", "foo", "bar"};
2947
    auto node_bar = FindAllocationProfileNode(*profile, ArrayVector(names));
2948 2949 2950
    CHECK(node_bar);

    // Count the number of allocations we sampled from bar.
2951
    int count_128 = 0;
2952
    for (auto allocation : node_bar->allocations) {
2953
      count_128 += allocation.count;
2954 2955
    }

2956 2957 2958 2959 2960 2961 2962 2963 2964 2965
    // We should have similar unsampled counts of allocations. Though
    // we will sample different numbers of objects at different rates,
    // the unsampling process should produce similar final estimates
    // at the true number of allocations. However, the process to
    // determine these unsampled counts is probabilisitic so we need to
    // account for error.
    double max_count = std::max(count_128, count_1024);
    double min_count = std::min(count_128, count_1024);
    double percent_difference = (max_count - min_count) / min_count;
    CHECK_LT(percent_difference, 0.15);
2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980

    heap_profiler->StopSamplingHeapProfiler();
  }

  // A more complicated test cases with deeper call graph and dynamically
  // generated function names.
  {
    heap_profiler->StartSamplingHeapProfiler(64);
    CompileRun(record_trace_tree_source);

    v8::base::SmartPointer<v8::AllocationProfile> profile(
        heap_profiler->GetAllocationProfile());
    CHECK(!profile.is_empty());

    const char* names1[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2981
    auto node1 = FindAllocationProfileNode(*profile, ArrayVector(names1));
2982 2983 2984
    CHECK(node1);

    const char* names2[] = {"", "generateFunctions"};
2985
    auto node2 = FindAllocationProfileNode(*profile, ArrayVector(names2));
2986 2987 2988 2989
    CHECK(node2);

    heap_profiler->StopSamplingHeapProfiler();
  }
2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004

  // A test case with scripts unloaded before profile gathered
  {
    heap_profiler->StartSamplingHeapProfiler(64);
    CompileRun(
        "for (var i = 0; i < 1024; i++) {\n"
        "  eval(\"new Array(100)\");\n"
        "}\n");

    CcTest::heap()->CollectAllGarbage();

    v8::base::SmartPointer<v8::AllocationProfile> profile(
        heap_profiler->GetAllocationProfile());
    CHECK(!profile.is_empty());

3005 3006
    CheckNoZeroCountNodes(profile->GetRootNode());

3007 3008
    heap_profiler->StopSamplingHeapProfiler();
  }
3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027
}


TEST(SamplingHeapProfilerApiAllocation) {
  v8::HandleScope scope(v8::Isolate::GetCurrent());
  LocalContext env;
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  // Suppress randomness to avoid flakiness in tests.
  v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;

  heap_profiler->StartSamplingHeapProfiler(256);

  for (int i = 0; i < 8 * 1024; ++i) v8::Object::New(env->GetIsolate());

  v8::base::SmartPointer<v8::AllocationProfile> profile(
      heap_profiler->GetAllocationProfile());
  CHECK(!profile.is_empty());
  const char* names[] = {"(V8 API)"};
3028
  auto node = FindAllocationProfileNode(*profile, ArrayVector(names));
3029 3030 3031 3032
  CHECK(node);

  heap_profiler->StopSamplingHeapProfiler();
}
3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057

TEST(SamplingHeapProfilerLeftTrimming) {
  v8::HandleScope scope(v8::Isolate::GetCurrent());
  LocalContext env;
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();

  // Suppress randomness to avoid flakiness in tests.
  v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;

  heap_profiler->StartSamplingHeapProfiler(64);

  CompileRun(
      "for (var j = 0; j < 500; ++j) {\n"
      "  var a = [];\n"
      "  for (var i = 0; i < 5; ++i)\n"
      "      a[i] = i;\n"
      "  for (var i = 0; i < 3; ++i)\n"
      "      a.shift();\n"
      "}\n");

  CcTest::heap()->CollectGarbage(v8::internal::NEW_SPACE);
  // Should not crash.

  heap_profiler->StopSamplingHeapProfiler();
}