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

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / test / cctest / test-heap-profiler.cc @ f230a1cf

History | View | Annotate | Download (77.7 KB)

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

    
30
#include <ctype.h>
31

    
32
#include "v8.h"
33

    
34
#include "allocation-tracker.h"
35
#include "cctest.h"
36
#include "hashmap.h"
37
#include "heap-profiler.h"
38
#include "snapshot.h"
39
#include "debug.h"
40
#include "utils-inl.h"
41
#include "../include/v8-profiler.h"
42

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

    
49
namespace {
50

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

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

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

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

    
91
  bool has_A2;
92
  bool has_B2;
93
  bool has_C2;
94
};
95

    
96
}  // namespace
97

    
98

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

    
110

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

    
123

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

    
136

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

    
141

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

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

    
173

    
174
TEST(HeapSnapshot) {
175
  LocalContext env2;
176
  v8::HandleScope scope(env2->GetIsolate());
177
  v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
178

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

    
191
  // Verify, that JS global object of env2 has '..2' properties.
192
  const v8::HeapGraphNode* a2_node =
193
      GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
194
  CHECK_NE(NULL, a2_node);
195
  CHECK_NE(
196
      NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
197
  CHECK_NE(
198
      NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
199
  CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
200

    
201
  NamedEntriesDetector det;
202
  det.CheckAllReachables(const_cast<i::HeapEntry*>(
203
      reinterpret_cast<const i::HeapEntry*>(global_env2)));
204
  CHECK(det.has_A2);
205
  CHECK(det.has_B2);
206
  CHECK(det.has_C2);
207
}
208

    
209

    
210
TEST(HeapSnapshotObjectSizes) {
211
  LocalContext env;
212
  v8::HandleScope scope(env->GetIsolate());
213
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
214

    
215
  //   -a-> X1 --a
216
  // x -b-> X2 <-|
217
  CompileRun(
218
      "function X(a, b) { this.a = a; this.b = b; }\n"
219
      "x = new X(new X(), new X());\n"
220
      "dummy = new X();\n"
221
      "(function() { x.a.a = x.b; })();");
222
  const v8::HeapSnapshot* snapshot =
223
      heap_profiler->TakeHeapSnapshot(v8_str("sizes"));
224
  CHECK(ValidateSnapshot(snapshot));
225
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
226
  const v8::HeapGraphNode* x =
227
      GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
228
  CHECK_NE(NULL, x);
229
  const v8::HeapGraphNode* x1 =
230
      GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
231
  CHECK_NE(NULL, x1);
232
  const v8::HeapGraphNode* x2 =
233
      GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
234
  CHECK_NE(NULL, x2);
235

    
236
  // Test sizes.
237
  CHECK_NE(0, x->GetSelfSize());
238
  CHECK_NE(0, x1->GetSelfSize());
239
  CHECK_NE(0, x2->GetSelfSize());
240
}
241

    
242

    
243
TEST(BoundFunctionInSnapshot) {
244
  LocalContext env;
245
  v8::HandleScope scope(env->GetIsolate());
246
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
247
  CompileRun(
248
      "function myFunction(a, b) { this.a = a; this.b = b; }\n"
249
      "function AAAAA() {}\n"
250
      "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
251
  const v8::HeapSnapshot* snapshot =
252
      heap_profiler->TakeHeapSnapshot(v8_str("sizes"));
253
  CHECK(ValidateSnapshot(snapshot));
254
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
255
  const v8::HeapGraphNode* f =
256
      GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
257
  CHECK(f);
258
  CHECK_EQ(v8::String::New("native_bind"), f->GetName());
259
  const v8::HeapGraphNode* bindings =
260
      GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
261
  CHECK_NE(NULL, bindings);
262
  CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
263
  CHECK_EQ(4, bindings->GetChildrenCount());
264

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

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

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

    
281

    
282
TEST(HeapSnapshotEntryChildren) {
283
  LocalContext env;
284
  v8::HandleScope scope(env->GetIsolate());
285
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
286

    
287
  CompileRun(
288
      "function A() { }\n"
289
      "a = new A;");
290
  const v8::HeapSnapshot* snapshot =
291
      heap_profiler->TakeHeapSnapshot(v8_str("children"));
292
  CHECK(ValidateSnapshot(snapshot));
293
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
294
  for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
295
    const v8::HeapGraphEdge* prop = global->GetChild(i);
296
    CHECK_EQ(global, prop->GetFromNode());
297
  }
298
  const v8::HeapGraphNode* a =
299
      GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
300
  CHECK_NE(NULL, a);
301
  for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
302
    const v8::HeapGraphEdge* prop = a->GetChild(i);
303
    CHECK_EQ(a, prop->GetFromNode());
304
  }
305
}
306

    
307

    
308
TEST(HeapSnapshotCodeObjects) {
309
  LocalContext env;
310
  v8::HandleScope scope(env->GetIsolate());
311
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
312

    
313
  CompileRun(
314
      "function lazy(x) { return x - 1; }\n"
315
      "function compiled(x) { return x + 1; }\n"
316
      "var anonymous = (function() { return function() { return 0; } })();\n"
317
      "compiled(1)");
318
  const v8::HeapSnapshot* snapshot =
319
      heap_profiler->TakeHeapSnapshot(v8_str("code"));
320
  CHECK(ValidateSnapshot(snapshot));
321

    
322
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
323
  const v8::HeapGraphNode* compiled =
324
      GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
325
  CHECK_NE(NULL, compiled);
326
  CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
327
  const v8::HeapGraphNode* lazy =
328
      GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
329
  CHECK_NE(NULL, lazy);
330
  CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
331
  const v8::HeapGraphNode* anonymous =
332
      GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
333
  CHECK_NE(NULL, anonymous);
334
  CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
335
  v8::String::Utf8Value anonymous_name(anonymous->GetName());
336
  CHECK_EQ("", *anonymous_name);
337

    
338
  // Find references to code.
339
  const v8::HeapGraphNode* compiled_code =
340
      GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
341
  CHECK_NE(NULL, compiled_code);
342
  const v8::HeapGraphNode* lazy_code =
343
      GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
344
  CHECK_NE(NULL, lazy_code);
345

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

    
374

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

    
393

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

    
421

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

    
432
  i::Factory* factory = CcTest::i_isolate()->factory();
433
  i::Handle<i::String> first =
434
      factory->NewStringFromAscii(i::CStrVector("0123456789"));
435
  i::Handle<i::String> second =
436
      factory->NewStringFromAscii(i::CStrVector("0123456789"));
437
  i::Handle<i::String> cons_string = factory->NewConsString(first, second);
438

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

    
441
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
442
  const v8::HeapSnapshot* snapshot =
443
      heap_profiler->TakeHeapSnapshot(v8_str("cons_strings"));
444
  CHECK(ValidateSnapshot(snapshot));
445
  const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
446

    
447
  const v8::HeapGraphNode* string_node =
448
      GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0");
449
  CHECK_NE(NULL, string_node);
450
  CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
451

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

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

    
460
  heap_profiler->DeleteAllHeapSnapshots();
461
}
462

    
463

    
464

    
465
TEST(HeapSnapshotInternalReferences) {
466
  v8::Isolate* isolate = CcTest::isolate();
467
  v8::HandleScope scope(isolate);
468
  v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
469
  global_template->SetInternalFieldCount(2);
470
  LocalContext env(NULL, global_template);
471
  v8::Handle<v8::Object> global_proxy = env->Global();
472
  v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
473
  CHECK_EQ(2, global->InternalFieldCount());
474
  v8::Local<v8::Object> obj = v8::Object::New();
475
  global->SetInternalField(0, v8_num(17));
476
  global->SetInternalField(1, obj);
477
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
478
  const v8::HeapSnapshot* snapshot =
479
      heap_profiler->TakeHeapSnapshot(v8_str("internals"));
480
  CHECK(ValidateSnapshot(snapshot));
481
  const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
482
  // The first reference will not present, because it's a Smi.
483
  CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
484
  // The second reference is to an object.
485
  CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
486
}
487

    
488

    
489
// Trying to introduce a check helper for uint32_t causes many
490
// overloading ambiguities, so it seems easier just to cast
491
// them to a signed type.
492
#define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \
493
  CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b))
494
#define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \
495
  CHECK((a) != (b))  // NOLINT
496

    
497
TEST(HeapSnapshotAddressReuse) {
498
  LocalContext env;
499
  v8::HandleScope scope(env->GetIsolate());
500
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
501

    
502
  CompileRun(
503
      "function A() {}\n"
504
      "var a = [];\n"
505
      "for (var i = 0; i < 10000; ++i)\n"
506
      "  a[i] = new A();\n");
507
  const v8::HeapSnapshot* snapshot1 =
508
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot1"));
509
  CHECK(ValidateSnapshot(snapshot1));
510
  v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
511

    
512
  CompileRun(
513
      "for (var i = 0; i < 10000; ++i)\n"
514
      "  a[i] = new A();\n");
515
  CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
516

    
517
  const v8::HeapSnapshot* snapshot2 =
518
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot2"));
519
  CHECK(ValidateSnapshot(snapshot2));
520
  const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
521

    
522
  const v8::HeapGraphNode* array_node =
523
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
524
  CHECK_NE(NULL, array_node);
525
  int wrong_count = 0;
526
  for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
527
    const v8::HeapGraphEdge* prop = array_node->GetChild(i);
528
    if (prop->GetType() != v8::HeapGraphEdge::kElement)
529
      continue;
530
    v8::SnapshotObjectId id = prop->GetToNode()->GetId();
531
    if (id < maxId1)
532
      ++wrong_count;
533
  }
534
  CHECK_EQ(0, wrong_count);
535
}
536

    
537

    
538
TEST(HeapEntryIdsAndArrayShift) {
539
  LocalContext env;
540
  v8::HandleScope scope(env->GetIsolate());
541
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
542

    
543
  CompileRun(
544
      "function AnObject() {\n"
545
      "    this.first = 'first';\n"
546
      "    this.second = 'second';\n"
547
      "}\n"
548
      "var a = new Array();\n"
549
      "for (var i = 0; i < 10; ++i)\n"
550
      "  a.push(new AnObject());\n");
551
  const v8::HeapSnapshot* snapshot1 =
552
      heap_profiler->TakeHeapSnapshot(v8_str("s1"));
553
  CHECK(ValidateSnapshot(snapshot1));
554

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

    
559
  CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
560

    
561
  const v8::HeapSnapshot* snapshot2 =
562
      heap_profiler->TakeHeapSnapshot(v8_str("s2"));
563
  CHECK(ValidateSnapshot(snapshot2));
564

    
565
  const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
566
  const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
567
  CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
568
  CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
569

    
570
  const v8::HeapGraphNode* a1 =
571
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
572
  CHECK_NE(NULL, a1);
573
  const v8::HeapGraphNode* k1 =
574
      GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
575
  CHECK_NE(NULL, k1);
576
  const v8::HeapGraphNode* a2 =
577
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
578
  CHECK_NE(NULL, a2);
579
  const v8::HeapGraphNode* k2 =
580
      GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
581
  CHECK_NE(NULL, k2);
582

    
583
  CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
584
  CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId());
585
}
586

    
587

    
588
TEST(HeapEntryIdsAndGC) {
589
  LocalContext env;
590
  v8::HandleScope scope(env->GetIsolate());
591
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
592

    
593
  CompileRun(
594
      "function A() {}\n"
595
      "function B(x) { this.x = x; }\n"
596
      "var a = new A();\n"
597
      "var b = new B(a);");
598
  v8::Local<v8::String> s1_str = v8_str("s1");
599
  v8::Local<v8::String> s2_str = v8_str("s2");
600
  const v8::HeapSnapshot* snapshot1 =
601
      heap_profiler->TakeHeapSnapshot(s1_str);
602
  CHECK(ValidateSnapshot(snapshot1));
603

    
604
  CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
605

    
606
  const v8::HeapSnapshot* snapshot2 =
607
      heap_profiler->TakeHeapSnapshot(s2_str);
608
  CHECK(ValidateSnapshot(snapshot2));
609

    
610
  CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000);
611
  CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
612
        snapshot2->GetMaxSnapshotJSObjectId());
613

    
614
  const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
615
  const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
616
  CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
617
  CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
618
  const v8::HeapGraphNode* A1 =
619
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
620
  CHECK_NE(NULL, A1);
621
  const v8::HeapGraphNode* A2 =
622
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
623
  CHECK_NE(NULL, A2);
624
  CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId());
625
  CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId());
626
  const v8::HeapGraphNode* B1 =
627
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
628
  CHECK_NE(NULL, B1);
629
  const v8::HeapGraphNode* B2 =
630
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
631
  CHECK_NE(NULL, B2);
632
  CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId());
633
  CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId());
634
  const v8::HeapGraphNode* a1 =
635
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
636
  CHECK_NE(NULL, a1);
637
  const v8::HeapGraphNode* a2 =
638
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
639
  CHECK_NE(NULL, a2);
640
  CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId());
641
  CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
642
  const v8::HeapGraphNode* b1 =
643
      GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
644
  CHECK_NE(NULL, b1);
645
  const v8::HeapGraphNode* b2 =
646
      GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
647
  CHECK_NE(NULL, b2);
648
  CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId());
649
  CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId());
650
}
651

    
652

    
653
TEST(HeapSnapshotRootPreservedAfterSorting) {
654
  LocalContext env;
655
  v8::HandleScope scope(env->GetIsolate());
656
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
657
  const v8::HeapSnapshot* snapshot =
658
      heap_profiler->TakeHeapSnapshot(v8_str("s"));
659
  CHECK(ValidateSnapshot(snapshot));
660
  const v8::HeapGraphNode* root1 = snapshot->GetRoot();
661
  const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
662
      snapshot))->GetSortedEntriesList();
663
  const v8::HeapGraphNode* root2 = snapshot->GetRoot();
664
  CHECK_EQ(root1, root2);
665
}
666

    
667

    
668
namespace {
669

    
670
class TestJSONStream : public v8::OutputStream {
671
 public:
672
  TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
673
  explicit TestJSONStream(int abort_countdown)
674
      : eos_signaled_(0), abort_countdown_(abort_countdown) {}
675
  virtual ~TestJSONStream() {}
676
  virtual void EndOfStream() { ++eos_signaled_; }
677
  virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
678
    if (abort_countdown_ > 0) --abort_countdown_;
679
    if (abort_countdown_ == 0) return kAbort;
680
    CHECK_GT(chars_written, 0);
681
    i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
682
    i::OS::MemCopy(chunk.start(), buffer, chars_written);
683
    return kContinue;
684
  }
685
  virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
686
    ASSERT(false);
687
    return kAbort;
688
  }
689
  void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
690
  int eos_signaled() { return eos_signaled_; }
691
  int size() { return buffer_.size(); }
692

    
693
 private:
694
  i::Collector<char> buffer_;
695
  int eos_signaled_;
696
  int abort_countdown_;
697
};
698

    
699
class AsciiResource: public v8::String::ExternalAsciiStringResource {
700
 public:
701
  explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
702
    length_ = string.length();
703
  }
704
  virtual const char* data() const { return data_; }
705
  virtual size_t length() const { return length_; }
706
 private:
707
  const char* data_;
708
  size_t length_;
709
};
710

    
711
}  // namespace
712

    
713
TEST(HeapSnapshotJSONSerialization) {
714
  LocalContext env;
715
  v8::HandleScope scope(env->GetIsolate());
716
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
717

    
718
#define STRING_LITERAL_FOR_TEST \
719
  "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
720
  CompileRun(
721
      "function A(s) { this.s = s; }\n"
722
      "function B(x) { this.x = x; }\n"
723
      "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
724
      "var b = new B(a);");
725
  const v8::HeapSnapshot* snapshot =
726
      heap_profiler->TakeHeapSnapshot(v8_str("json"));
727
  CHECK(ValidateSnapshot(snapshot));
728

    
729
  TestJSONStream stream;
730
  snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
731
  CHECK_GT(stream.size(), 0);
732
  CHECK_EQ(1, stream.eos_signaled());
733
  i::ScopedVector<char> json(stream.size());
734
  stream.WriteTo(json);
735

    
736
  // Verify that snapshot string is valid JSON.
737
  AsciiResource json_res(json);
738
  v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
739
  env->Global()->Set(v8_str("json_snapshot"), json_string);
740
  v8::Local<v8::Value> snapshot_parse_result = CompileRun(
741
      "var parsed = JSON.parse(json_snapshot); true;");
742
  CHECK(!snapshot_parse_result.IsEmpty());
743

    
744
  // Verify that snapshot object has required fields.
745
  v8::Local<v8::Object> parsed_snapshot =
746
      env->Global()->Get(v8_str("parsed"))->ToObject();
747
  CHECK(parsed_snapshot->Has(v8_str("snapshot")));
748
  CHECK(parsed_snapshot->Has(v8_str("nodes")));
749
  CHECK(parsed_snapshot->Has(v8_str("edges")));
750
  CHECK(parsed_snapshot->Has(v8_str("strings")));
751

    
752
  // Get node and edge "member" offsets.
753
  v8::Local<v8::Value> meta_analysis_result = CompileRun(
754
      "var meta = parsed.snapshot.meta;\n"
755
      "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
756
      "var node_fields_count = meta.node_fields.length;\n"
757
      "var edge_fields_count = meta.edge_fields.length;\n"
758
      "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
759
      "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
760
      "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
761
      "var property_type ="
762
      "    meta.edge_types[edge_type_offset].indexOf('property');\n"
763
      "var shortcut_type ="
764
      "    meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
765
      "var node_count = parsed.nodes.length / node_fields_count;\n"
766
      "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
767
      "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
768
      "  first_edge_indexes[i] = first_edge_index;\n"
769
      "  first_edge_index += edge_fields_count *\n"
770
      "      parsed.nodes[i * node_fields_count + edge_count_offset];\n"
771
      "}\n"
772
      "first_edge_indexes[node_count] = first_edge_index;\n");
773
  CHECK(!meta_analysis_result.IsEmpty());
774

    
775
  // A helper function for processing encoded nodes.
776
  CompileRun(
777
      "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
778
      "  var nodes = parsed.nodes;\n"
779
      "  var edges = parsed.edges;\n"
780
      "  var strings = parsed.strings;\n"
781
      "  var node_ordinal = pos / node_fields_count;\n"
782
      "  for (var i = parsed.first_edge_indexes[node_ordinal],\n"
783
      "      count = parsed.first_edge_indexes[node_ordinal + 1];\n"
784
      "      i < count; i += edge_fields_count) {\n"
785
      "    if (edges[i + edge_type_offset] === prop_type\n"
786
      "        && strings[edges[i + edge_name_offset]] === prop_name)\n"
787
      "      return edges[i + edge_to_node_offset];\n"
788
      "  }\n"
789
      "  return null;\n"
790
      "}\n");
791
  // Get the string index using the path: <root> -> <global>.b.x.s
792
  v8::Local<v8::Value> string_obj_pos_val = CompileRun(
793
      "GetChildPosByProperty(\n"
794
      "  GetChildPosByProperty(\n"
795
      "    GetChildPosByProperty("
796
      "      parsed.edges[edge_fields_count + edge_to_node_offset],"
797
      "      \"b\", property_type),\n"
798
      "    \"x\", property_type),"
799
      "  \"s\", property_type)");
800
  CHECK(!string_obj_pos_val.IsEmpty());
801
  int string_obj_pos =
802
      static_cast<int>(string_obj_pos_val->ToNumber()->Value());
803
  v8::Local<v8::Object> nodes_array =
804
      parsed_snapshot->Get(v8_str("nodes"))->ToObject();
805
  int string_index = static_cast<int>(
806
      nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
807
  CHECK_GT(string_index, 0);
808
  v8::Local<v8::Object> strings_array =
809
      parsed_snapshot->Get(v8_str("strings"))->ToObject();
810
  v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
811
  v8::Local<v8::String> ref_string =
812
      CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
813
#undef STRING_LITERAL_FOR_TEST
814
  CHECK_EQ(*v8::String::Utf8Value(ref_string),
815
           *v8::String::Utf8Value(string));
816
}
817

    
818

    
819
TEST(HeapSnapshotJSONSerializationAborting) {
820
  LocalContext env;
821
  v8::HandleScope scope(env->GetIsolate());
822
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
823
  const v8::HeapSnapshot* snapshot =
824
      heap_profiler->TakeHeapSnapshot(v8_str("abort"));
825
  CHECK(ValidateSnapshot(snapshot));
826
  TestJSONStream stream(5);
827
  snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
828
  CHECK_GT(stream.size(), 0);
829
  CHECK_EQ(0, stream.eos_signaled());
830
}
831

    
832
namespace {
833

    
834
class TestStatsStream : public v8::OutputStream {
835
 public:
836
  TestStatsStream()
837
    : eos_signaled_(0),
838
      updates_written_(0),
839
      entries_count_(0),
840
      entries_size_(0),
841
      intervals_count_(0),
842
      first_interval_index_(-1) { }
843
  TestStatsStream(const TestStatsStream& stream)
844
    : v8::OutputStream(stream),
845
      eos_signaled_(stream.eos_signaled_),
846
      updates_written_(stream.updates_written_),
847
      entries_count_(stream.entries_count_),
848
      entries_size_(stream.entries_size_),
849
      intervals_count_(stream.intervals_count_),
850
      first_interval_index_(stream.first_interval_index_) { }
851
  virtual ~TestStatsStream() {}
852
  virtual void EndOfStream() { ++eos_signaled_; }
853
  virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
854
    ASSERT(false);
855
    return kAbort;
856
  }
857
  virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
858
                                          int updates_written) {
859
    ++intervals_count_;
860
    ASSERT(updates_written);
861
    updates_written_ += updates_written;
862
    entries_count_ = 0;
863
    if (first_interval_index_ == -1 && updates_written != 0)
864
      first_interval_index_ = buffer[0].index;
865
    for (int i = 0; i < updates_written; ++i) {
866
      entries_count_ += buffer[i].count;
867
      entries_size_ += buffer[i].size;
868
    }
869

    
870
    return kContinue;
871
  }
872
  int eos_signaled() { return eos_signaled_; }
873
  int updates_written() { return updates_written_; }
874
  uint32_t entries_count() const { return entries_count_; }
875
  uint32_t entries_size() const { return entries_size_; }
876
  int intervals_count() const { return intervals_count_; }
877
  int first_interval_index() const { return first_interval_index_; }
878

    
879
 private:
880
  int eos_signaled_;
881
  int updates_written_;
882
  uint32_t entries_count_;
883
  uint32_t entries_size_;
884
  int intervals_count_;
885
  int first_interval_index_;
886
};
887

    
888
}  // namespace
889

    
890
static TestStatsStream GetHeapStatsUpdate(
891
    v8::HeapProfiler* heap_profiler,
892
    v8::SnapshotObjectId* object_id = NULL) {
893
  TestStatsStream stream;
894
  v8::SnapshotObjectId last_seen_id = heap_profiler->GetHeapStats(&stream);
895
  if (object_id)
896
    *object_id = last_seen_id;
897
  CHECK_EQ(1, stream.eos_signaled());
898
  return stream;
899
}
900

    
901

    
902
TEST(HeapSnapshotObjectsStats) {
903
  LocalContext env;
904
  v8::HandleScope scope(env->GetIsolate());
905
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
906

    
907
  heap_profiler->StartTrackingHeapObjects();
908
  // We have to call GC 6 times. In other case the garbage will be
909
  // the reason of flakiness.
910
  for (int i = 0; i < 6; ++i) {
911
    CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
912
  }
913

    
914
  v8::SnapshotObjectId initial_id;
915
  {
916
    // Single chunk of data expected in update. Initial data.
917
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
918
                                                      &initial_id);
919
    CHECK_EQ(1, stats_update.intervals_count());
920
    CHECK_EQ(1, stats_update.updates_written());
921
    CHECK_LT(0, stats_update.entries_size());
922
    CHECK_EQ(0, stats_update.first_interval_index());
923
  }
924

    
925
  // No data expected in update because nothing has happened.
926
  v8::SnapshotObjectId same_id;
927
  CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
928
  CHECK_EQ_SNAPSHOT_OBJECT_ID(initial_id, same_id);
929

    
930
  {
931
    v8::SnapshotObjectId additional_string_id;
932
    v8::HandleScope inner_scope_1(env->GetIsolate());
933
    v8_str("string1");
934
    {
935
      // Single chunk of data with one new entry expected in update.
936
      TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
937
                                                        &additional_string_id);
938
      CHECK_LT(same_id, additional_string_id);
939
      CHECK_EQ(1, stats_update.intervals_count());
940
      CHECK_EQ(1, stats_update.updates_written());
941
      CHECK_LT(0, stats_update.entries_size());
942
      CHECK_EQ(1, stats_update.entries_count());
943
      CHECK_EQ(2, stats_update.first_interval_index());
944
    }
945

    
946
    // No data expected in update because nothing happened.
947
    v8::SnapshotObjectId last_id;
948
    CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
949
    CHECK_EQ_SNAPSHOT_OBJECT_ID(additional_string_id, last_id);
950

    
951
    {
952
      v8::HandleScope inner_scope_2(env->GetIsolate());
953
      v8_str("string2");
954

    
955
      uint32_t entries_size;
956
      {
957
        v8::HandleScope inner_scope_3(env->GetIsolate());
958
        v8_str("string3");
959
        v8_str("string4");
960

    
961
        {
962
          // Single chunk of data with three new entries expected in update.
963
          TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
964
          CHECK_EQ(1, stats_update.intervals_count());
965
          CHECK_EQ(1, stats_update.updates_written());
966
          CHECK_LT(0, entries_size = stats_update.entries_size());
967
          CHECK_EQ(3, stats_update.entries_count());
968
          CHECK_EQ(4, stats_update.first_interval_index());
969
        }
970
      }
971

    
972
      {
973
        // Single chunk of data with two left entries expected in update.
974
        TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
975
        CHECK_EQ(1, stats_update.intervals_count());
976
        CHECK_EQ(1, stats_update.updates_written());
977
        CHECK_GT(entries_size, stats_update.entries_size());
978
        CHECK_EQ(1, stats_update.entries_count());
979
        // Two strings from forth interval were released.
980
        CHECK_EQ(4, stats_update.first_interval_index());
981
      }
982
    }
983

    
984
    {
985
      // Single chunk of data with 0 left entries expected in update.
986
      TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
987
      CHECK_EQ(1, stats_update.intervals_count());
988
      CHECK_EQ(1, stats_update.updates_written());
989
      CHECK_EQ(0, stats_update.entries_size());
990
      CHECK_EQ(0, stats_update.entries_count());
991
      // The last string from forth interval was released.
992
      CHECK_EQ(4, stats_update.first_interval_index());
993
    }
994
  }
995
  {
996
    // Single chunk of data with 0 left entries expected in update.
997
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
998
    CHECK_EQ(1, stats_update.intervals_count());
999
    CHECK_EQ(1, stats_update.updates_written());
1000
    CHECK_EQ(0, stats_update.entries_size());
1001
    CHECK_EQ(0, stats_update.entries_count());
1002
    // The only string from the second interval was released.
1003
    CHECK_EQ(2, stats_update.first_interval_index());
1004
  }
1005

    
1006
  v8::Local<v8::Array> array = v8::Array::New();
1007
  CHECK_EQ(0, array->Length());
1008
  // Force array's buffer allocation.
1009
  array->Set(2, v8_num(7));
1010

    
1011
  uint32_t entries_size;
1012
  {
1013
    // Single chunk of data with 2 entries expected in update.
1014
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1015
    CHECK_EQ(1, stats_update.intervals_count());
1016
    CHECK_EQ(1, stats_update.updates_written());
1017
    CHECK_LT(0, entries_size = stats_update.entries_size());
1018
    // They are the array and its buffer.
1019
    CHECK_EQ(2, stats_update.entries_count());
1020
    CHECK_EQ(8, stats_update.first_interval_index());
1021
  }
1022

    
1023
  for (int i = 0; i < 100; ++i)
1024
    array->Set(i, v8_num(i));
1025

    
1026
  {
1027
    // Single chunk of data with 1 entry expected in update.
1028
    TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1029
    CHECK_EQ(1, stats_update.intervals_count());
1030
    // The first interval was changed because old buffer was collected.
1031
    // The second interval was changed because new buffer was allocated.
1032
    CHECK_EQ(2, stats_update.updates_written());
1033
    CHECK_LT(entries_size, stats_update.entries_size());
1034
    CHECK_EQ(2, stats_update.entries_count());
1035
    CHECK_EQ(8, stats_update.first_interval_index());
1036
  }
1037

    
1038
  heap_profiler->StopTrackingHeapObjects();
1039
}
1040

    
1041

    
1042
static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
1043
                             const v8::HeapGraphNode* node,
1044
                             int level, int max_level) {
1045
  if (level > max_level) return;
1046
  CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
1047
  for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
1048
    const v8::HeapGraphEdge* prop = node->GetChild(i);
1049
    const v8::HeapGraphNode* child =
1050
        snapshot->GetNodeById(prop->GetToNode()->GetId());
1051
    CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId());
1052
    CHECK_EQ(prop->GetToNode(), child);
1053
    CheckChildrenIds(snapshot, child, level + 1, max_level);
1054
  }
1055
}
1056

    
1057

    
1058
TEST(HeapSnapshotGetNodeById) {
1059
  LocalContext env;
1060
  v8::HandleScope scope(env->GetIsolate());
1061
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1062

    
1063
  const v8::HeapSnapshot* snapshot =
1064
      heap_profiler->TakeHeapSnapshot(v8_str("id"));
1065
  CHECK(ValidateSnapshot(snapshot));
1066
  const v8::HeapGraphNode* root = snapshot->GetRoot();
1067
  CheckChildrenIds(snapshot, root, 0, 3);
1068
  // Check a big id, which should not exist yet.
1069
  CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
1070
}
1071

    
1072

    
1073
TEST(HeapSnapshotGetSnapshotObjectId) {
1074
  LocalContext env;
1075
  v8::HandleScope scope(env->GetIsolate());
1076
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1077
  CompileRun("globalObject = {};\n");
1078
  const v8::HeapSnapshot* snapshot =
1079
      heap_profiler->TakeHeapSnapshot(v8_str("get_snapshot_object_id"));
1080
  CHECK(ValidateSnapshot(snapshot));
1081
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1082
  const v8::HeapGraphNode* global_object =
1083
      GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
1084
  CHECK(global_object);
1085

    
1086
  v8::Local<v8::Value> globalObjectHandle =
1087
      env->Global()->Get(v8::String::New("globalObject"));
1088
  CHECK(!globalObjectHandle.IsEmpty());
1089
  CHECK(globalObjectHandle->IsObject());
1090

    
1091
  v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
1092
  CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId),
1093
           id);
1094
  CHECK_EQ(static_cast<int>(id), global_object->GetId());
1095
}
1096

    
1097

    
1098
TEST(HeapSnapshotUnknownSnapshotObjectId) {
1099
  LocalContext env;
1100
  v8::HandleScope scope(env->GetIsolate());
1101
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1102
  CompileRun("globalObject = {};\n");
1103
  const v8::HeapSnapshot* snapshot =
1104
      heap_profiler->TakeHeapSnapshot(v8_str("unknown_object_id"));
1105
  CHECK(ValidateSnapshot(snapshot));
1106
  const v8::HeapGraphNode* node =
1107
      snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
1108
  CHECK_EQ(NULL, node);
1109
}
1110

    
1111

    
1112
namespace {
1113

    
1114
class TestActivityControl : public v8::ActivityControl {
1115
 public:
1116
  explicit TestActivityControl(int abort_count)
1117
      : done_(0), total_(0), abort_count_(abort_count) {}
1118
  ControlOption ReportProgressValue(int done, int total) {
1119
    done_ = done;
1120
    total_ = total;
1121
    return --abort_count_ != 0 ? kContinue : kAbort;
1122
  }
1123
  int done() { return done_; }
1124
  int total() { return total_; }
1125

    
1126
 private:
1127
  int done_;
1128
  int total_;
1129
  int abort_count_;
1130
};
1131
}
1132

    
1133

    
1134
TEST(TakeHeapSnapshotAborting) {
1135
  LocalContext env;
1136
  v8::HandleScope scope(env->GetIsolate());
1137

    
1138
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1139
  const int snapshots_count = heap_profiler->GetSnapshotCount();
1140
  TestActivityControl aborting_control(1);
1141
  const v8::HeapSnapshot* no_snapshot =
1142
      heap_profiler->TakeHeapSnapshot(v8_str("abort"),
1143
                                     &aborting_control);
1144
  CHECK_EQ(NULL, no_snapshot);
1145
  CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1146
  CHECK_GT(aborting_control.total(), aborting_control.done());
1147

    
1148
  TestActivityControl control(-1);  // Don't abort.
1149
  const v8::HeapSnapshot* snapshot =
1150
      heap_profiler->TakeHeapSnapshot(v8_str("full"),
1151
                                     &control);
1152
  CHECK(ValidateSnapshot(snapshot));
1153

    
1154
  CHECK_NE(NULL, snapshot);
1155
  CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1156
  CHECK_EQ(control.total(), control.done());
1157
  CHECK_GT(control.total(), 0);
1158
}
1159

    
1160

    
1161
namespace {
1162

    
1163
class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1164
 public:
1165
  TestRetainedObjectInfo(int hash,
1166
                         const char* group_label,
1167
                         const char* label,
1168
                         intptr_t element_count = -1,
1169
                         intptr_t size = -1)
1170
      : disposed_(false),
1171
        hash_(hash),
1172
        group_label_(group_label),
1173
        label_(label),
1174
        element_count_(element_count),
1175
        size_(size) {
1176
    instances.Add(this);
1177
  }
1178
  virtual ~TestRetainedObjectInfo() {}
1179
  virtual void Dispose() {
1180
    CHECK(!disposed_);
1181
    disposed_ = true;
1182
  }
1183
  virtual bool IsEquivalent(RetainedObjectInfo* other) {
1184
    return GetHash() == other->GetHash();
1185
  }
1186
  virtual intptr_t GetHash() { return hash_; }
1187
  virtual const char* GetGroupLabel() { return group_label_; }
1188
  virtual const char* GetLabel() { return label_; }
1189
  virtual intptr_t GetElementCount() { return element_count_; }
1190
  virtual intptr_t GetSizeInBytes() { return size_; }
1191
  bool disposed() { return disposed_; }
1192

    
1193
  static v8::RetainedObjectInfo* WrapperInfoCallback(
1194
      uint16_t class_id, v8::Handle<v8::Value> wrapper) {
1195
    if (class_id == 1) {
1196
      if (wrapper->IsString()) {
1197
        v8::String::Utf8Value utf8(wrapper);
1198
        if (strcmp(*utf8, "AAA") == 0)
1199
          return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1200
        else if (strcmp(*utf8, "BBB") == 0)
1201
          return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1202
      }
1203
    } else if (class_id == 2) {
1204
      if (wrapper->IsString()) {
1205
        v8::String::Utf8Value utf8(wrapper);
1206
        if (strcmp(*utf8, "CCC") == 0)
1207
          return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1208
      }
1209
    }
1210
    CHECK(false);
1211
    return NULL;
1212
  }
1213

    
1214
  static i::List<TestRetainedObjectInfo*> instances;
1215

    
1216
 private:
1217
  bool disposed_;
1218
  int hash_;
1219
  const char* group_label_;
1220
  const char* label_;
1221
  intptr_t element_count_;
1222
  intptr_t size_;
1223
};
1224

    
1225

    
1226
i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1227
}
1228

    
1229

    
1230
static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1231
                                        v8::HeapGraphNode::Type type,
1232
                                        const char* name) {
1233
  for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1234
    const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1235
    if (node->GetType() == type && strcmp(name,
1236
               const_cast<i::HeapEntry*>(
1237
                   reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1238
      return node;
1239
    }
1240
  }
1241
  return NULL;
1242
}
1243

    
1244

    
1245
TEST(HeapSnapshotRetainedObjectInfo) {
1246
  LocalContext env;
1247
  v8::Isolate* isolate = env->GetIsolate();
1248
  v8::HandleScope scope(isolate);
1249
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1250

    
1251
  heap_profiler->SetWrapperClassInfoProvider(
1252
      1, TestRetainedObjectInfo::WrapperInfoCallback);
1253
  heap_profiler->SetWrapperClassInfoProvider(
1254
      2, TestRetainedObjectInfo::WrapperInfoCallback);
1255
  v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1256
  p_AAA.SetWrapperClassId(1);
1257
  v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1258
  p_BBB.SetWrapperClassId(1);
1259
  v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1260
  p_CCC.SetWrapperClassId(2);
1261
  CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1262
  const v8::HeapSnapshot* snapshot =
1263
      heap_profiler->TakeHeapSnapshot(v8_str("retained"));
1264
  CHECK(ValidateSnapshot(snapshot));
1265

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

    
1272
  const v8::HeapGraphNode* native_group_aaa = GetNode(
1273
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1274
  CHECK_NE(NULL, native_group_aaa);
1275
  CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1276
  const v8::HeapGraphNode* aaa = GetNode(
1277
      native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1278
  CHECK_NE(NULL, aaa);
1279
  CHECK_EQ(2, aaa->GetChildrenCount());
1280

    
1281
  const v8::HeapGraphNode* native_group_ccc = GetNode(
1282
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1283
  const v8::HeapGraphNode* ccc = GetNode(
1284
      native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1285
  CHECK_NE(NULL, ccc);
1286

    
1287
  const v8::HeapGraphNode* n_AAA = GetNode(
1288
      aaa, v8::HeapGraphNode::kString, "AAA");
1289
  CHECK_NE(NULL, n_AAA);
1290
  const v8::HeapGraphNode* n_BBB = GetNode(
1291
      aaa, v8::HeapGraphNode::kString, "BBB");
1292
  CHECK_NE(NULL, n_BBB);
1293
  CHECK_EQ(1, ccc->GetChildrenCount());
1294
  const v8::HeapGraphNode* n_CCC = GetNode(
1295
      ccc, v8::HeapGraphNode::kString, "CCC");
1296
  CHECK_NE(NULL, n_CCC);
1297

    
1298
  CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1299
  CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1300
  CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1301
}
1302

    
1303

    
1304
class GraphWithImplicitRefs {
1305
 public:
1306
  static const int kObjectsCount = 4;
1307
  explicit GraphWithImplicitRefs(LocalContext* env) {
1308
    CHECK_EQ(NULL, instance_);
1309
    instance_ = this;
1310
    isolate_ = (*env)->GetIsolate();
1311
    for (int i = 0; i < kObjectsCount; i++) {
1312
      objects_[i].Reset(isolate_, v8::Object::New());
1313
    }
1314
    (*env)->Global()->Set(v8_str("root_object"),
1315
                          v8::Local<v8::Value>::New(isolate_, objects_[0]));
1316
  }
1317
  ~GraphWithImplicitRefs() {
1318
    instance_ = NULL;
1319
  }
1320

    
1321
  static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) {
1322
    instance_->AddImplicitReferences();
1323
  }
1324

    
1325
 private:
1326
  void AddImplicitReferences() {
1327
    // 0 -> 1
1328
    isolate_->SetObjectGroupId(objects_[0],
1329
                               v8::UniqueId(1));
1330
    isolate_->SetReferenceFromGroup(
1331
        v8::UniqueId(1), objects_[1]);
1332
    // Adding two more references: 1 -> 2, 1 -> 3
1333
    isolate_->SetReference(objects_[1].As<v8::Object>(),
1334
                           objects_[2]);
1335
    isolate_->SetReference(objects_[1].As<v8::Object>(),
1336
                           objects_[3]);
1337
  }
1338

    
1339
  v8::Persistent<v8::Value> objects_[kObjectsCount];
1340
  static GraphWithImplicitRefs* instance_;
1341
  v8::Isolate* isolate_;
1342
};
1343

    
1344
GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1345

    
1346

    
1347
TEST(HeapSnapshotImplicitReferences) {
1348
  LocalContext env;
1349
  v8::HandleScope scope(env->GetIsolate());
1350
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1351

    
1352
  GraphWithImplicitRefs graph(&env);
1353
  v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1354

    
1355
  const v8::HeapSnapshot* snapshot =
1356
      heap_profiler->TakeHeapSnapshot(v8_str("implicit_refs"));
1357
  CHECK(ValidateSnapshot(snapshot));
1358

    
1359
  const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1360
  const v8::HeapGraphNode* obj0 = GetProperty(
1361
      global_object, v8::HeapGraphEdge::kProperty, "root_object");
1362
  CHECK(obj0);
1363
  CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1364
  const v8::HeapGraphNode* obj1 = GetProperty(
1365
      obj0, v8::HeapGraphEdge::kInternal, "native");
1366
  CHECK(obj1);
1367
  int implicit_targets_count = 0;
1368
  for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1369
    const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1370
    v8::String::Utf8Value prop_name(prop->GetName());
1371
    if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1372
        strcmp("native", *prop_name) == 0) {
1373
      ++implicit_targets_count;
1374
    }
1375
  }
1376
  CHECK_EQ(2, implicit_targets_count);
1377
  v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1378
}
1379

    
1380

    
1381
TEST(DeleteAllHeapSnapshots) {
1382
  LocalContext env;
1383
  v8::HandleScope scope(env->GetIsolate());
1384
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1385

    
1386
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1387
  heap_profiler->DeleteAllHeapSnapshots();
1388
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1389
  CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1")));
1390
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1391
  heap_profiler->DeleteAllHeapSnapshots();
1392
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1393
  CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1")));
1394
  CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("2")));
1395
  CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1396
  heap_profiler->DeleteAllHeapSnapshots();
1397
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1398
}
1399

    
1400

    
1401
static const v8::HeapSnapshot* FindHeapSnapshot(v8::HeapProfiler* profiler,
1402
                                                unsigned uid) {
1403
  int length = profiler->GetSnapshotCount();
1404
  for (int i = 0; i < length; i++) {
1405
    const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i);
1406
    if (snapshot->GetUid() == uid) {
1407
      return snapshot;
1408
    }
1409
  }
1410
  return NULL;
1411
}
1412

    
1413

    
1414
TEST(DeleteHeapSnapshot) {
1415
  LocalContext env;
1416
  v8::HandleScope scope(env->GetIsolate());
1417
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1418

    
1419
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1420
  const v8::HeapSnapshot* s1 =
1421
      heap_profiler->TakeHeapSnapshot(v8_str("1"));
1422

    
1423
  CHECK_NE(NULL, s1);
1424
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1425
  unsigned uid1 = s1->GetUid();
1426
  CHECK_EQ(s1, FindHeapSnapshot(heap_profiler, uid1));
1427
  const_cast<v8::HeapSnapshot*>(s1)->Delete();
1428
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1429
  CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid1));
1430

    
1431
  const v8::HeapSnapshot* s2 =
1432
      heap_profiler->TakeHeapSnapshot(v8_str("2"));
1433
  CHECK_NE(NULL, s2);
1434
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1435
  unsigned uid2 = s2->GetUid();
1436
  CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
1437
  CHECK_EQ(s2, FindHeapSnapshot(heap_profiler, uid2));
1438
  const v8::HeapSnapshot* s3 =
1439
      heap_profiler->TakeHeapSnapshot(v8_str("3"));
1440
  CHECK_NE(NULL, s3);
1441
  CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1442
  unsigned uid3 = s3->GetUid();
1443
  CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
1444
  CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3));
1445
  const_cast<v8::HeapSnapshot*>(s2)->Delete();
1446
  CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1447
  CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid2));
1448
  CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3));
1449
  const_cast<v8::HeapSnapshot*>(s3)->Delete();
1450
  CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1451
  CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid3));
1452
}
1453

    
1454

    
1455
class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
1456
 public:
1457
  virtual const char* GetName(v8::Handle<v8::Object> object) {
1458
    return "Global object name";
1459
  }
1460
};
1461

    
1462

    
1463
TEST(GlobalObjectName) {
1464
  LocalContext env;
1465
  v8::HandleScope scope(env->GetIsolate());
1466
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1467

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

    
1470
  NameResolver name_resolver;
1471
  const v8::HeapSnapshot* snapshot =
1472
      heap_profiler->TakeHeapSnapshot(v8_str("document"),
1473
      NULL,
1474
      &name_resolver);
1475
  CHECK(ValidateSnapshot(snapshot));
1476
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1477
  CHECK_NE(NULL, global);
1478
  CHECK_EQ("Object / Global object name" ,
1479
           const_cast<i::HeapEntry*>(
1480
               reinterpret_cast<const i::HeapEntry*>(global))->name());
1481
}
1482

    
1483

    
1484
TEST(NoHandleLeaks) {
1485
  LocalContext env;
1486
  v8::HandleScope scope(env->GetIsolate());
1487
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1488

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

    
1491
  v8::Handle<v8::String> name(v8_str("leakz"));
1492
  i::Isolate* isolate = CcTest::i_isolate();
1493
  int count_before = i::HandleScope::NumberOfHandles(isolate);
1494
  heap_profiler->TakeHeapSnapshot(name);
1495
  int count_after = i::HandleScope::NumberOfHandles(isolate);
1496
  CHECK_EQ(count_before, count_after);
1497
}
1498

    
1499

    
1500
TEST(NodesIteration) {
1501
  LocalContext env;
1502
  v8::HandleScope scope(env->GetIsolate());
1503
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1504
  const v8::HeapSnapshot* snapshot =
1505
      heap_profiler->TakeHeapSnapshot(v8_str("iteration"));
1506
  CHECK(ValidateSnapshot(snapshot));
1507
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1508
  CHECK_NE(NULL, global);
1509
  // Verify that we can find this object by iteration.
1510
  const int nodes_count = snapshot->GetNodesCount();
1511
  int count = 0;
1512
  for (int i = 0; i < nodes_count; ++i) {
1513
    if (snapshot->GetNode(i) == global)
1514
      ++count;
1515
  }
1516
  CHECK_EQ(1, count);
1517
}
1518

    
1519

    
1520
TEST(GetHeapValue) {
1521
  LocalContext env;
1522
  v8::HandleScope scope(env->GetIsolate());
1523
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1524

    
1525
  CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
1526
  const v8::HeapSnapshot* snapshot =
1527
      heap_profiler->TakeHeapSnapshot(v8_str("value"));
1528
  CHECK(ValidateSnapshot(snapshot));
1529
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1530
  CHECK(global->GetHeapValue()->IsObject());
1531
  v8::Local<v8::Object> js_global =
1532
      env->Global()->GetPrototype().As<v8::Object>();
1533
  CHECK(js_global == global->GetHeapValue());
1534
  const v8::HeapGraphNode* obj = GetProperty(
1535
      global, v8::HeapGraphEdge::kProperty, "a");
1536
  CHECK(obj->GetHeapValue()->IsObject());
1537
  v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1538
  CHECK(js_obj == obj->GetHeapValue());
1539
  const v8::HeapGraphNode* s_prop =
1540
      GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1541
  v8::Local<v8::String> js_s_prop =
1542
      js_obj->Get(v8_str("s_prop")).As<v8::String>();
1543
  CHECK(js_s_prop == s_prop->GetHeapValue());
1544
  const v8::HeapGraphNode* n_prop =
1545
      GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1546
  v8::Local<v8::Number> js_n_prop =
1547
      js_obj->Get(v8_str("n_prop")).As<v8::Number>();
1548
  CHECK(js_n_prop->NumberValue() == n_prop->GetHeapValue()->NumberValue());
1549
}
1550

    
1551

    
1552
TEST(GetHeapValueForDeletedObject) {
1553
  LocalContext env;
1554
  v8::HandleScope scope(env->GetIsolate());
1555
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1556

    
1557
  // It is impossible to delete a global property, so we are about to delete a
1558
  // property of the "a" object. Also, the "p" object can't be an empty one
1559
  // because the empty object is static and isn't actually deleted.
1560
  CompileRun("a = { p: { r: {} } };");
1561
  const v8::HeapSnapshot* snapshot =
1562
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1563
  CHECK(ValidateSnapshot(snapshot));
1564
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1565
  const v8::HeapGraphNode* obj = GetProperty(
1566
      global, v8::HeapGraphEdge::kProperty, "a");
1567
  const v8::HeapGraphNode* prop = GetProperty(
1568
      obj, v8::HeapGraphEdge::kProperty, "p");
1569
  {
1570
    // Perform the check inside a nested local scope to avoid creating a
1571
    // reference to the object we are deleting.
1572
    v8::HandleScope scope(env->GetIsolate());
1573
    CHECK(prop->GetHeapValue()->IsObject());
1574
  }
1575
  CompileRun("delete a.p;");
1576
  CHECK(prop->GetHeapValue()->IsUndefined());
1577
}
1578

    
1579

    
1580
static int StringCmp(const char* ref, i::String* act) {
1581
  i::SmartArrayPointer<char> s_act = act->ToCString();
1582
  int result = strcmp(ref, *s_act);
1583
  if (result != 0)
1584
    fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act);
1585
  return result;
1586
}
1587

    
1588

    
1589
TEST(GetConstructorName) {
1590
  LocalContext env;
1591
  v8::HandleScope scope(env->GetIsolate());
1592

    
1593
  CompileRun(
1594
      "function Constructor1() {};\n"
1595
      "var obj1 = new Constructor1();\n"
1596
      "var Constructor2 = function() {};\n"
1597
      "var obj2 = new Constructor2();\n"
1598
      "var obj3 = {};\n"
1599
      "obj3.constructor = function Constructor3() {};\n"
1600
      "var obj4 = {};\n"
1601
      "// Slow properties\n"
1602
      "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1603
      "obj4.constructor = function Constructor4() {};\n"
1604
      "var obj5 = {};\n"
1605
      "var obj6 = {};\n"
1606
      "obj6.constructor = 6;");
1607
  v8::Local<v8::Object> js_global =
1608
      env->Global()->GetPrototype().As<v8::Object>();
1609
  v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1610
  i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1611
  CHECK_EQ(0, StringCmp(
1612
      "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1613
  v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1614
  i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1615
  CHECK_EQ(0, StringCmp(
1616
      "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1617
  v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1618
  i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1619
  CHECK_EQ(0, StringCmp(
1620
      "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1621
  v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1622
  i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1623
  CHECK_EQ(0, StringCmp(
1624
      "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1625
  v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1626
  i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1627
  CHECK_EQ(0, StringCmp(
1628
      "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1629
  v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1630
  i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1631
  CHECK_EQ(0, StringCmp(
1632
      "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1633
}
1634

    
1635

    
1636
TEST(FastCaseAccessors) {
1637
  LocalContext env;
1638
  v8::HandleScope scope(env->GetIsolate());
1639
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1640

    
1641
  CompileRun("var obj1 = {};\n"
1642
             "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1643
             "  return 42;\n"
1644
             "});\n"
1645
             "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1646
             "  return this.value_ = value;\n"
1647
             "});\n");
1648
  const v8::HeapSnapshot* snapshot =
1649
      heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors"));
1650
  CHECK(ValidateSnapshot(snapshot));
1651

    
1652
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1653
  CHECK_NE(NULL, global);
1654
  const v8::HeapGraphNode* obj1 =
1655
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1656
  CHECK_NE(NULL, obj1);
1657
  const v8::HeapGraphNode* func;
1658
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1659
  CHECK_NE(NULL, func);
1660
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1661
  CHECK_EQ(NULL, func);
1662
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1663
  CHECK_NE(NULL, func);
1664
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1665
  CHECK_EQ(NULL, func);
1666
}
1667

    
1668

    
1669
TEST(SlowCaseAccessors) {
1670
  LocalContext env;
1671
  v8::HandleScope scope(env->GetIsolate());
1672
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1673

    
1674
  CompileRun("var obj1 = {};\n"
1675
             "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
1676
             "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1677
             "  return 42;\n"
1678
             "});\n"
1679
             "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1680
             "  return this.value_ = value;\n"
1681
             "});\n");
1682
  const v8::HeapSnapshot* snapshot =
1683
      heap_profiler->TakeHeapSnapshot(v8_str("slowCaseAccessors"));
1684
  CHECK(ValidateSnapshot(snapshot));
1685

    
1686
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1687
  CHECK_NE(NULL, global);
1688
  const v8::HeapGraphNode* obj1 =
1689
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1690
  CHECK_NE(NULL, obj1);
1691
  const v8::HeapGraphNode* func;
1692
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1693
  CHECK_NE(NULL, func);
1694
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1695
  CHECK_EQ(NULL, func);
1696
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1697
  CHECK_NE(NULL, func);
1698
  func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1699
  CHECK_EQ(NULL, func);
1700
}
1701

    
1702

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

    
1708
  CompileRun(
1709
      "function C(x) { this.a = this; this.b = x; }\n"
1710
      "c = new C(2012);\n");
1711
  const v8::HeapSnapshot* snapshot =
1712
      heap_profiler->TakeHeapSnapshot(v8_str("HiddenPropertiesFastCase1"));
1713
  CHECK(ValidateSnapshot(snapshot));
1714
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1715
  const v8::HeapGraphNode* c =
1716
      GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1717
  CHECK_NE(NULL, c);
1718
  const v8::HeapGraphNode* hidden_props =
1719
      GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties");
1720
  CHECK_EQ(NULL, hidden_props);
1721

    
1722
  v8::Handle<v8::Value> cHandle = env->Global()->Get(v8::String::New("c"));
1723
  CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
1724
  cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val"));
1725

    
1726
  snapshot = heap_profiler->TakeHeapSnapshot(
1727
      v8_str("HiddenPropertiesFastCase2"));
1728
  CHECK(ValidateSnapshot(snapshot));
1729
  global = GetGlobalObject(snapshot);
1730
  c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1731
  CHECK_NE(NULL, c);
1732
  hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal,
1733
      "hidden_properties");
1734
  CHECK_NE(NULL, hidden_props);
1735
}
1736

    
1737

    
1738
bool HasWeakEdge(const v8::HeapGraphNode* node) {
1739
  for (int i = 0; i < node->GetChildrenCount(); ++i) {
1740
    const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
1741
    if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
1742
  }
1743
  return false;
1744
}
1745

    
1746

    
1747
bool HasWeakGlobalHandle() {
1748
  v8::Isolate* isolate = CcTest::isolate();
1749
  v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1750
  const v8::HeapSnapshot* snapshot =
1751
      heap_profiler->TakeHeapSnapshot(v8_str("weaks"));
1752
  CHECK(ValidateSnapshot(snapshot));
1753
  const v8::HeapGraphNode* gc_roots = GetNode(
1754
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
1755
  CHECK_NE(NULL, gc_roots);
1756
  const v8::HeapGraphNode* global_handles = GetNode(
1757
      gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
1758
  CHECK_NE(NULL, global_handles);
1759
  return HasWeakEdge(global_handles);
1760
}
1761

    
1762

    
1763
static void PersistentHandleCallback(v8::Isolate* isolate,
1764
                                     v8::Persistent<v8::Value>* handle,
1765
                                     void*) {
1766
  handle->Dispose();
1767
}
1768

    
1769

    
1770
TEST(WeakGlobalHandle) {
1771
  LocalContext env;
1772
  v8::HandleScope scope(env->GetIsolate());
1773

    
1774
  CHECK(!HasWeakGlobalHandle());
1775

    
1776
  v8::Persistent<v8::Object> handle(env->GetIsolate(), v8::Object::New());
1777
  handle.MakeWeak<v8::Value, void>(NULL, PersistentHandleCallback);
1778

    
1779
  CHECK(HasWeakGlobalHandle());
1780
}
1781

    
1782

    
1783
TEST(SfiAndJsFunctionWeakRefs) {
1784
  LocalContext env;
1785
  v8::HandleScope scope(env->GetIsolate());
1786
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1787

    
1788
  CompileRun(
1789
      "fun = (function (x) { return function () { return x + 1; } })(1);");
1790
  const v8::HeapSnapshot* snapshot =
1791
      heap_profiler->TakeHeapSnapshot(v8_str("fun"));
1792
  CHECK(ValidateSnapshot(snapshot));
1793
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1794
  CHECK_NE(NULL, global);
1795
  const v8::HeapGraphNode* fun =
1796
      GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
1797
  CHECK(!HasWeakEdge(fun));
1798
  const v8::HeapGraphNode* shared =
1799
      GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
1800
  CHECK(!HasWeakEdge(shared));
1801
}
1802

    
1803

    
1804
#ifdef ENABLE_DEBUGGER_SUPPORT
1805
TEST(NoDebugObjectInSnapshot) {
1806
  LocalContext env;
1807
  v8::HandleScope scope(env->GetIsolate());
1808
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1809

    
1810
  CcTest::i_isolate()->debug()->Load();
1811
  CompileRun("foo = {};");
1812
  const v8::HeapSnapshot* snapshot =
1813
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1814
  CHECK(ValidateSnapshot(snapshot));
1815
  const v8::HeapGraphNode* root = snapshot->GetRoot();
1816
  int globals_count = 0;
1817
  for (int i = 0; i < root->GetChildrenCount(); ++i) {
1818
    const v8::HeapGraphEdge* edge = root->GetChild(i);
1819
    if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
1820
      ++globals_count;
1821
      const v8::HeapGraphNode* global = edge->GetToNode();
1822
      const v8::HeapGraphNode* foo =
1823
          GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
1824
      CHECK_NE(NULL, foo);
1825
    }
1826
  }
1827
  CHECK_EQ(1, globals_count);
1828
}
1829
#endif  // ENABLE_DEBUGGER_SUPPORT
1830

    
1831

    
1832
TEST(AllStrongGcRootsHaveNames) {
1833
  LocalContext env;
1834
  v8::HandleScope scope(env->GetIsolate());
1835
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1836

    
1837
  CompileRun("foo = {};");
1838
  const v8::HeapSnapshot* snapshot =
1839
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1840
  CHECK(ValidateSnapshot(snapshot));
1841
  const v8::HeapGraphNode* gc_roots = GetNode(
1842
      snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
1843
  CHECK_NE(NULL, gc_roots);
1844
  const v8::HeapGraphNode* strong_roots = GetNode(
1845
      gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
1846
  CHECK_NE(NULL, strong_roots);
1847
  for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
1848
    const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
1849
    CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
1850
    v8::String::Utf8Value name(edge->GetName());
1851
    CHECK(isalpha(**name));
1852
  }
1853
}
1854

    
1855

    
1856
TEST(NoRefsToNonEssentialEntries) {
1857
  LocalContext env;
1858
  v8::HandleScope scope(env->GetIsolate());
1859
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1860
  CompileRun("global_object = {};\n");
1861
  const v8::HeapSnapshot* snapshot =
1862
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1863
  CHECK(ValidateSnapshot(snapshot));
1864
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1865
  const v8::HeapGraphNode* global_object =
1866
      GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
1867
  CHECK_NE(NULL, global_object);
1868
  const v8::HeapGraphNode* properties =
1869
      GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
1870
  CHECK_EQ(NULL, properties);
1871
  const v8::HeapGraphNode* elements =
1872
      GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
1873
  CHECK_EQ(NULL, elements);
1874
}
1875

    
1876

    
1877
TEST(MapHasDescriptorsAndTransitions) {
1878
  LocalContext env;
1879
  v8::HandleScope scope(env->GetIsolate());
1880
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1881
  CompileRun("obj = { a: 10 };\n");
1882
  const v8::HeapSnapshot* snapshot =
1883
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1884
  CHECK(ValidateSnapshot(snapshot));
1885
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1886
  const v8::HeapGraphNode* global_object =
1887
      GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
1888
  CHECK_NE(NULL, global_object);
1889

    
1890
  const v8::HeapGraphNode* map =
1891
      GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
1892
  CHECK_NE(NULL, map);
1893
  const v8::HeapGraphNode* own_descriptors = GetProperty(
1894
      map, v8::HeapGraphEdge::kInternal, "descriptors");
1895
  CHECK_NE(NULL, own_descriptors);
1896
  const v8::HeapGraphNode* own_transitions = GetProperty(
1897
      map, v8::HeapGraphEdge::kInternal, "transitions");
1898
  CHECK_EQ(NULL, own_transitions);
1899
}
1900

    
1901

    
1902
TEST(ManyLocalsInSharedContext) {
1903
  LocalContext env;
1904
  v8::HandleScope scope(env->GetIsolate());
1905
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1906
  int num_objects = 6000;
1907
  CompileRun(
1908
      "var n = 6000;"
1909
      "var result = [];"
1910
      "result.push('(function outer() {');"
1911
      "for (var i = 0; i < n; i++) {"
1912
      "    var f = 'function f_' + i + '() { ';"
1913
      "    if (i > 0)"
1914
      "        f += 'f_' + (i - 1) + '();';"
1915
      "    f += ' }';"
1916
      "    result.push(f);"
1917
      "}"
1918
      "result.push('return f_' + (n - 1) + ';');"
1919
      "result.push('})()');"
1920
      "var ok = eval(result.join('\\n'));");
1921
  const v8::HeapSnapshot* snapshot =
1922
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1923
  CHECK(ValidateSnapshot(snapshot));
1924

    
1925
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1926
  CHECK_NE(NULL, global);
1927
  const v8::HeapGraphNode* ok_object =
1928
      GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
1929
  CHECK_NE(NULL, ok_object);
1930
  const v8::HeapGraphNode* context_object =
1931
      GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
1932
  CHECK_NE(NULL, context_object);
1933
  // Check the objects are not duplicated in the context.
1934
  CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
1935
           context_object->GetChildrenCount());
1936
  // Check all the objects have got their names.
1937
  // ... well check just every 8th because otherwise it's too slow in debug.
1938
  for (int i = 0; i < num_objects - 1; i += 8) {
1939
    i::EmbeddedVector<char, 100> var_name;
1940
    i::OS::SNPrintF(var_name, "f_%d", i);
1941
    const v8::HeapGraphNode* f_object = GetProperty(
1942
        context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
1943
    CHECK_NE(NULL, f_object);
1944
  }
1945
}
1946

    
1947

    
1948
TEST(AllocationSitesAreVisible) {
1949
  LocalContext env;
1950
  v8::HandleScope scope(env->GetIsolate());
1951
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1952
  CompileRun(
1953
      "fun = function () { var a = [3, 2, 1]; return a; }\n"
1954
      "fun();");
1955
  const v8::HeapSnapshot* snapshot =
1956
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1957
  CHECK(ValidateSnapshot(snapshot));
1958

    
1959
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1960
  CHECK_NE(NULL, global);
1961
  const v8::HeapGraphNode* fun_code =
1962
      GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
1963
  CHECK_NE(NULL, fun_code);
1964
  const v8::HeapGraphNode* literals =
1965
      GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
1966
  CHECK_NE(NULL, literals);
1967
  CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
1968
  CHECK_EQ(2, literals->GetChildrenCount());
1969

    
1970
  // The second value in the literals array should be the boilerplate,
1971
  // after an AllocationSite.
1972
  const v8::HeapGraphEdge* prop = literals->GetChild(1);
1973
  const v8::HeapGraphNode* allocation_site = prop->GetToNode();
1974
  v8::String::Utf8Value name(allocation_site->GetName());
1975
  CHECK_EQ("system / AllocationSite", *name);
1976
  const v8::HeapGraphNode* transition_info =
1977
      GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
1978
                  "transition_info");
1979
  CHECK_NE(NULL, transition_info);
1980

    
1981
  const v8::HeapGraphNode* elements =
1982
      GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
1983
                  "elements");
1984
  CHECK_NE(NULL, elements);
1985
  CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
1986
  CHECK_EQ(v8::internal::FixedArray::SizeFor(3), elements->GetSelfSize());
1987

    
1988
  CHECK(transition_info->GetHeapValue()->IsArray());
1989
  v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(
1990
      transition_info->GetHeapValue());
1991
  // Verify the array is "a" in the code above.
1992
  CHECK_EQ(3, array->Length());
1993
  CHECK_EQ(v8::Integer::New(3), array->Get(v8::Integer::New(0)));
1994
  CHECK_EQ(v8::Integer::New(2), array->Get(v8::Integer::New(1)));
1995
  CHECK_EQ(v8::Integer::New(1), array->Get(v8::Integer::New(2)));
1996
}
1997

    
1998

    
1999
TEST(JSFunctionHasCodeLink) {
2000
  LocalContext env;
2001
  v8::HandleScope scope(env->GetIsolate());
2002
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2003
  CompileRun("function foo(x, y) { return x + y; }\n");
2004
  const v8::HeapSnapshot* snapshot =
2005
      heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2006
  CHECK(ValidateSnapshot(snapshot));
2007
  const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2008
  const v8::HeapGraphNode* foo_func =
2009
      GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2010
  CHECK_NE(NULL, foo_func);
2011
  const v8::HeapGraphNode* code =
2012
      GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
2013
  CHECK_NE(NULL, code);
2014
}
2015

    
2016

    
2017

    
2018
class HeapProfilerExtension : public v8::Extension {
2019
 public:
2020
  static const char* kName;
2021
  HeapProfilerExtension() : v8::Extension(kName, kSource) { }
2022
  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
2023
      v8::Handle<v8::String> name);
2024
  static void FindUntrackedObjects(
2025
      const v8::FunctionCallbackInfo<v8::Value>& args);
2026
 private:
2027
  static const char* kSource;
2028
};
2029

    
2030
const char* HeapProfilerExtension::kName = "v8/heap-profiler";
2031

    
2032

    
2033
const char* HeapProfilerExtension::kSource =
2034
    "native function findUntrackedObjects();";
2035

    
2036

    
2037
v8::Handle<v8::FunctionTemplate> HeapProfilerExtension::GetNativeFunction(
2038
    v8::Handle<v8::String> name) {
2039
  if (name->Equals(v8::String::New("findUntrackedObjects"))) {
2040
    return v8::FunctionTemplate::New(
2041
        HeapProfilerExtension::FindUntrackedObjects);
2042
  } else {
2043
    CHECK(false);
2044
    return v8::Handle<v8::FunctionTemplate>();
2045
  }
2046
}
2047

    
2048

    
2049
void HeapProfilerExtension::FindUntrackedObjects(
2050
    const v8::FunctionCallbackInfo<v8::Value>& args) {
2051
  i::HeapProfiler* heap_profiler =
2052
      reinterpret_cast<i::HeapProfiler*>(args.GetIsolate()->GetHeapProfiler());
2053
  int untracked_objects = heap_profiler->FindUntrackedObjects();
2054
  args.GetReturnValue().Set(untracked_objects);
2055
  CHECK_EQ(0, untracked_objects);
2056
}
2057

    
2058

    
2059
static HeapProfilerExtension kHeapProfilerExtension;
2060
v8::DeclareExtension kHeapProfilerExtensionDeclaration(
2061
    &kHeapProfilerExtension);
2062

    
2063

    
2064
// This is an example of using checking of JS allocations tracking in a test.
2065
TEST(HeapObjectsTracker) {
2066
  const char* extensions[] = { HeapProfilerExtension::kName };
2067
  v8::ExtensionConfiguration config(1, extensions);
2068
  LocalContext env(&config);
2069
  v8::HandleScope scope(env->GetIsolate());
2070
  HeapObjectsTracker tracker;
2071
  CompileRun("var a = 1.2");
2072
  CompileRun("var a = 1.2; var b = 1.0; var c = 1.0;");
2073
  CompileRun(
2074
    "var a = [];\n"
2075
    "for (var i = 0; i < 5; ++i)\n"
2076
    "    a[i] = i;\n"
2077
    "findUntrackedObjects();\n"
2078
    "for (var i = 0; i < 3; ++i)\n"
2079
    "    a.shift();\n"
2080
    "findUntrackedObjects();\n");
2081
}
2082

    
2083

    
2084
static const char* record_trace_tree_source =
2085
"var topFunctions = [];\n"
2086
"var global = this;\n"
2087
"function generateFunctions(width, depth) {\n"
2088
"  var script = [];\n"
2089
"  for (var i = 0; i < width; i++) {\n"
2090
"    for (var j = 0; j < depth; j++) {\n"
2091
"      script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
2092
"      script.push('  try {\\n');\n"
2093
"      if (j < depth-2) {\n"
2094
"        script.push('    return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
2095
"      } else if (j == depth - 2) {\n"
2096
"        script.push('    return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
2097
"      } else if (j == depth - 1) {\n"
2098
"        script.push('    this.ts = Date.now();\\n');\n"
2099
"      }\n"
2100
"      script.push('  } catch (e) {}\\n');\n"
2101
"      script.push('}\\n');\n"
2102
"      \n"
2103
"    }\n"
2104
"  }\n"
2105
"  var script = script.join('');\n"
2106
"  // throw script;\n"
2107
"  global.eval(script);\n"
2108
"  for (var i = 0; i < width; i++) {\n"
2109
"    topFunctions.push(this['f_' + i + '_0']);\n"
2110
"  }\n"
2111
"}\n"
2112
"\n"
2113
"var width = 3;\n"
2114
"var depth = 3;\n"
2115
"generateFunctions(width, depth);\n"
2116
"var instances = [];\n"
2117
"function start() {\n"
2118
"  for (var i = 0; i < width; i++) {\n"
2119
"    instances.push(topFunctions[i](0));\n"
2120
"  }\n"
2121
"}\n"
2122
"\n"
2123
"for (var i = 0; i < 100; i++) start();\n";
2124

    
2125

    
2126
static i::HeapSnapshot* ToInternal(const v8::HeapSnapshot* snapshot) {
2127
  return const_cast<i::HeapSnapshot*>(
2128
      reinterpret_cast<const i::HeapSnapshot*>(snapshot));
2129
}
2130

    
2131

    
2132
static AllocationTraceNode* FindNode(
2133
    AllocationTracker* tracker, const Vector<const char*>& names) {
2134
  AllocationTraceNode* node = tracker->trace_tree()->root();
2135
  for (int i = 0; node != NULL && i < names.length(); i++) {
2136
    const char* name = names[i];
2137
    Vector<AllocationTraceNode*> children = node->children();
2138
    node = NULL;
2139
    for (int j = 0; j < children.length(); j++) {
2140
      v8::SnapshotObjectId id = children[j]->function_id();
2141
      AllocationTracker::FunctionInfo* info = tracker->GetFunctionInfo(id);
2142
      if (info && strcmp(info->name, name) == 0) {
2143
        node = children[j];
2144
        break;
2145
      }
2146
    }
2147
  }
2148
  return node;
2149
}
2150

    
2151

    
2152
TEST(TrackHeapAllocations) {
2153
  v8::HandleScope scope(v8::Isolate::GetCurrent());
2154
  LocalContext env;
2155

    
2156
  v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2157
  heap_profiler->StartRecordingHeapAllocations();
2158

    
2159
  CompileRun(record_trace_tree_source);
2160

    
2161
  const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(
2162
      v8::String::New("Test"));
2163
  i::HeapSnapshotsCollection* collection = ToInternal(snapshot)->collection();
2164
  AllocationTracker* tracker = collection->allocation_tracker();
2165
  CHECK_NE(NULL, tracker);
2166
  // Resolve all function locations.
2167
  tracker->PrepareForSerialization();
2168
  // Print for better diagnostics in case of failure.
2169
  tracker->trace_tree()->Print(tracker);
2170

    
2171
  const char* names[] =
2172
      { "(anonymous function)", "start", "f_0_0", "f_0_1", "f_0_2" };
2173
  AllocationTraceNode* node =
2174
      FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names)));
2175
  CHECK_NE(NULL, node);
2176
  CHECK_GE(node->allocation_count(), 100);
2177
  CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2178
  heap_profiler->StopRecordingHeapAllocations();
2179
}