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
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.
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.
28 // Tests for heap profiler
34 #include "include/v8-profiler.h"
35 #include "src/allocation-tracker.h"
36 #include "src/debug.h"
37 #include "src/hashmap.h"
38 #include "src/heap-profiler.h"
39 #include "test/cctest/cctest.h"
41 using i::AllocationTraceNode;
42 using i::AllocationTraceTree;
43 using i::AllocationTracker;
49 class NamedEntriesDetector {
51 NamedEntriesDetector()
52 : has_A2(false), has_B2(false), has_C2(false) {
55 void CheckEntry(i::HeapEntry* entry) {
56 if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
57 if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
58 if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
61 static bool AddressesMatch(void* key1, void* key2) {
65 void CheckAllReachables(i::HeapEntry* root) {
66 i::HashMap visited(AddressesMatch);
67 i::List<i::HeapEntry*> list(10);
70 while (!list.is_empty()) {
71 i::HeapEntry* entry = list.RemoveLast();
72 i::Vector<i::HeapGraphEdge*> children = entry->children();
73 for (int i = 0; i < children.length(); ++i) {
74 if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
75 i::HeapEntry* child = children[i]->to();
76 i::HashMap::Entry* entry = visited.LookupOrInsert(
77 reinterpret_cast<void*>(child),
78 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)));
81 entry->value = reinterpret_cast<void*>(1);
96 static const v8::HeapGraphNode* GetGlobalObject(
97 const v8::HeapSnapshot* snapshot) {
98 CHECK_EQ(3, snapshot->GetRoot()->GetChildrenCount());
99 // The 0th-child is (GC Roots), 1st is code stubs context, 2nd is the user
101 const v8::HeapGraphNode* global_obj =
102 snapshot->GetRoot()->GetChild(2)->GetToNode();
103 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
104 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
109 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
110 v8::HeapGraphEdge::Type type,
112 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
113 const v8::HeapGraphEdge* prop = node->GetChild(i);
114 v8::String::Utf8Value prop_name(prop->GetName());
115 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
116 return prop->GetToNode();
122 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
123 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
124 const v8::HeapGraphEdge* prop = node->GetChild(i);
125 const v8::HeapGraphNode* node = prop->GetToNode();
126 if (node->GetType() == v8::HeapGraphNode::kString) {
127 v8::String::Utf8Value node_name(node->GetName());
128 if (strcmp(contents, *node_name) == 0) return true;
135 static bool AddressesMatch(void* key1, void* key2) {
140 // Check that snapshot has no unretained entries except root.
141 static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
142 i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
143 reinterpret_cast<const i::HeapSnapshot*>(snapshot));
145 i::HashMap visited(AddressesMatch);
146 i::List<i::HeapGraphEdge>& edges = heap_snapshot->edges();
147 for (int i = 0; i < edges.length(); ++i) {
148 i::HashMap::Entry* entry = visited.LookupOrInsert(
149 reinterpret_cast<void*>(edges[i].to()),
150 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())));
151 uint32_t ref_count = static_cast<uint32_t>(
152 reinterpret_cast<uintptr_t>(entry->value));
153 entry->value = reinterpret_cast<void*>(ref_count + 1);
155 uint32_t unretained_entries_count = 0;
156 i::List<i::HeapEntry>& entries = heap_snapshot->entries();
157 for (int i = 0; i < entries.length(); ++i) {
158 i::HashMap::Entry* entry = visited.Lookup(
159 reinterpret_cast<void*>(&entries[i]),
160 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entries[i])));
161 if (!entry && entries[i].id() != 1) {
162 entries[i].Print("entry with no retainer", "", depth, 0);
163 ++unretained_entries_count;
166 return unretained_entries_count == 0;
172 v8::HandleScope scope(env2->GetIsolate());
173 v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
177 "function B2(x) { return function() { return typeof x; }; }\n"
178 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
179 "var a2 = new A2();\n"
180 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
181 "var c2 = new C2(a2);");
182 const v8::HeapSnapshot* snapshot_env2 = heap_profiler->TakeHeapSnapshot();
183 CHECK(ValidateSnapshot(snapshot_env2));
184 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
186 // Verify, that JS global object of env2 has '..2' properties.
187 const v8::HeapGraphNode* a2_node =
188 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
190 CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
191 CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
192 CHECK(GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
194 NamedEntriesDetector det;
195 det.CheckAllReachables(const_cast<i::HeapEntry*>(
196 reinterpret_cast<const i::HeapEntry*>(global_env2)));
203 TEST(HeapSnapshotObjectSizes) {
205 v8::HandleScope scope(env->GetIsolate());
206 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
211 "function X(a, b) { this.a = a; this.b = b; }\n"
212 "x = new X(new X(), new X());\n"
214 "(function() { x.a.a = x.b; })();");
215 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
216 CHECK(ValidateSnapshot(snapshot));
217 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
218 const v8::HeapGraphNode* x =
219 GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
221 const v8::HeapGraphNode* x1 =
222 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
224 const v8::HeapGraphNode* x2 =
225 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
229 CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
230 CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
231 CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
235 TEST(BoundFunctionInSnapshot) {
237 v8::HandleScope scope(env->GetIsolate());
238 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
240 "function myFunction(a, b) { this.a = a; this.b = b; }\n"
241 "function AAAAA() {}\n"
242 "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
243 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
244 CHECK(ValidateSnapshot(snapshot));
245 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
246 const v8::HeapGraphNode* f =
247 GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
249 CHECK(v8::String::NewFromUtf8(env->GetIsolate(), "native_bind")
250 ->Equals(f->GetName()));
251 const v8::HeapGraphNode* bindings =
252 GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
254 CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
255 CHECK_EQ(3, bindings->GetChildrenCount());
257 const v8::HeapGraphNode* bound_this = GetProperty(
258 f, v8::HeapGraphEdge::kShortcut, "bound_this");
260 CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
262 const v8::HeapGraphNode* bound_function = GetProperty(
263 f, v8::HeapGraphEdge::kShortcut, "bound_function");
264 CHECK(bound_function);
265 CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
267 const v8::HeapGraphNode* bound_argument = GetProperty(
268 f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
269 CHECK(bound_argument);
270 CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
274 TEST(HeapSnapshotEntryChildren) {
276 v8::HandleScope scope(env->GetIsolate());
277 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
282 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
283 CHECK(ValidateSnapshot(snapshot));
284 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
285 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
286 const v8::HeapGraphEdge* prop = global->GetChild(i);
287 CHECK_EQ(global, prop->GetFromNode());
289 const v8::HeapGraphNode* a =
290 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
292 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
293 const v8::HeapGraphEdge* prop = a->GetChild(i);
294 CHECK_EQ(a, prop->GetFromNode());
299 TEST(HeapSnapshotCodeObjects) {
301 v8::HandleScope scope(env->GetIsolate());
302 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
305 "function lazy(x) { return x - 1; }\n"
306 "function compiled(x) { return x + 1; }\n"
307 "var anonymous = (function() { return function() { return 0; } })();\n"
309 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
310 CHECK(ValidateSnapshot(snapshot));
312 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
313 const v8::HeapGraphNode* compiled =
314 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
316 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
317 const v8::HeapGraphNode* lazy =
318 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
320 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
321 const v8::HeapGraphNode* anonymous =
322 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
324 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
325 v8::String::Utf8Value anonymous_name(anonymous->GetName());
326 CHECK_EQ(0, strcmp("", *anonymous_name));
328 // Find references to code.
329 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");
336 // Check that there's no strong next_code_link. There might be a weak one
337 // but might be not, so we can't check that fact.
338 const v8::HeapGraphNode* code =
339 GetProperty(compiled_code, v8::HeapGraphEdge::kInternal, "code");
341 const v8::HeapGraphNode* next_code_link =
342 GetProperty(code, v8::HeapGraphEdge::kInternal, "code");
343 CHECK(!next_code_link);
345 // Verify that non-compiled code doesn't contain references to "x"
346 // literal, while compiled code does. The scope info is stored in FixedArray
347 // objects attached to the SharedFunctionInfo.
348 bool compiled_references_x = false, lazy_references_x = false;
349 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
350 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
351 const v8::HeapGraphNode* node = prop->GetToNode();
352 if (node->GetType() == v8::HeapGraphNode::kArray) {
353 if (HasString(node, "x")) {
354 compiled_references_x = true;
359 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
360 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
361 const v8::HeapGraphNode* node = prop->GetToNode();
362 if (node->GetType() == v8::HeapGraphNode::kArray) {
363 if (HasString(node, "x")) {
364 lazy_references_x = true;
369 CHECK(compiled_references_x);
370 CHECK(!lazy_references_x);
374 TEST(HeapSnapshotHeapNumbers) {
376 v8::HandleScope scope(env->GetIsolate());
377 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
379 "a = 1; // a is Smi\n"
380 "b = 2.5; // b is HeapNumber");
381 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
382 CHECK(ValidateSnapshot(snapshot));
383 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
384 CHECK(!GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
385 const v8::HeapGraphNode* b =
386 GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
388 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
392 TEST(HeapSnapshotSlicedString) {
394 v8::HandleScope scope(env->GetIsolate());
395 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
397 "parent_string = \"123456789.123456789.123456789.123456789.123456789."
398 "123456789.123456789.123456789.123456789.123456789."
399 "123456789.123456789.123456789.123456789.123456789."
400 "123456789.123456789.123456789.123456789.123456789."
401 "123456789.123456789.123456789.123456789.123456789."
402 "123456789.123456789.123456789.123456789.123456789."
403 "123456789.123456789.123456789.123456789.123456789."
404 "123456789.123456789.123456789.123456789.123456789.\";"
405 "child_string = parent_string.slice(100);");
406 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
407 CHECK(ValidateSnapshot(snapshot));
408 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
409 const v8::HeapGraphNode* parent_string =
410 GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
411 CHECK(parent_string);
412 const v8::HeapGraphNode* child_string =
413 GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
415 CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
416 const v8::HeapGraphNode* parent =
417 GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
418 CHECK_EQ(parent_string, parent);
419 heap_profiler->DeleteAllHeapSnapshots();
423 TEST(HeapSnapshotConsString) {
424 v8::Isolate* isolate = CcTest::isolate();
425 v8::HandleScope scope(isolate);
426 v8::Local<v8::ObjectTemplate> global_template =
427 v8::ObjectTemplate::New(isolate);
428 global_template->SetInternalFieldCount(1);
429 LocalContext env(NULL, global_template);
430 v8::Handle<v8::Object> global_proxy = env->Global();
431 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
432 CHECK_EQ(1, global->InternalFieldCount());
434 i::Factory* factory = CcTest::i_isolate()->factory();
435 i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
436 i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
437 i::Handle<i::String> cons_string =
438 factory->NewConsString(first, second).ToHandleChecked();
440 global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
442 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
443 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
444 CHECK(ValidateSnapshot(snapshot));
445 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
447 const v8::HeapGraphNode* string_node =
448 GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0");
450 CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
452 const v8::HeapGraphNode* first_node =
453 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "first");
454 CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
456 const v8::HeapGraphNode* second_node =
457 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "second");
458 CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
460 heap_profiler->DeleteAllHeapSnapshots();
464 TEST(HeapSnapshotSymbol) {
466 v8::HandleScope scope(env->GetIsolate());
467 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
469 CompileRun("a = Symbol('mySymbol');\n");
470 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
471 CHECK(ValidateSnapshot(snapshot));
472 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
473 const v8::HeapGraphNode* a =
474 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
476 CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
477 CHECK(v8_str("symbol")->Equals(a->GetName()));
478 const v8::HeapGraphNode* name =
479 GetProperty(a, v8::HeapGraphEdge::kInternal, "name");
481 CHECK(v8_str("mySymbol")->Equals(name->GetName()));
485 TEST(HeapSnapshotFloat32x4) {
486 i::FLAG_harmony_simd = true;
488 v8::HandleScope scope(env->GetIsolate());
489 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
491 CompileRun("a = SIMD.float32x4(1, 2, 3, 4);\n");
492 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
493 CHECK(ValidateSnapshot(snapshot));
494 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
495 const v8::HeapGraphNode* a =
496 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
498 CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSimdValue);
502 TEST(HeapSnapshotWeakCollection) {
504 v8::HandleScope scope(env->GetIsolate());
505 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
508 "k = {}; v = {}; s = 'str';\n"
509 "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
510 "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
511 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
512 CHECK(ValidateSnapshot(snapshot));
513 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
514 const v8::HeapGraphNode* k =
515 GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
517 const v8::HeapGraphNode* v =
518 GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
520 const v8::HeapGraphNode* s =
521 GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
524 const v8::HeapGraphNode* ws =
525 GetProperty(global, v8::HeapGraphEdge::kProperty, "ws");
527 CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
528 CHECK(v8_str("WeakSet")->Equals(ws->GetName()));
530 const v8::HeapGraphNode* ws_table =
531 GetProperty(ws, v8::HeapGraphEdge::kInternal, "table");
532 CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
533 CHECK_GT(ws_table->GetChildrenCount(), 0);
534 int weak_entries = 0;
535 for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
536 const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
537 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
538 if (k->GetId() == prop->GetToNode()->GetId()) {
542 CHECK_EQ(1, weak_entries);
543 const v8::HeapGraphNode* ws_s =
544 GetProperty(ws, v8::HeapGraphEdge::kProperty, "str");
546 CHECK_EQ(s->GetId(), ws_s->GetId());
548 const v8::HeapGraphNode* wm =
549 GetProperty(global, v8::HeapGraphEdge::kProperty, "wm");
551 CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
552 CHECK(v8_str("WeakMap")->Equals(wm->GetName()));
554 const v8::HeapGraphNode* wm_table =
555 GetProperty(wm, v8::HeapGraphEdge::kInternal, "table");
556 CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
557 CHECK_GT(wm_table->GetChildrenCount(), 0);
559 for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
560 const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
561 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
562 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
563 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
567 CHECK_EQ(2, weak_entries);
568 const v8::HeapGraphNode* wm_s =
569 GetProperty(wm, v8::HeapGraphEdge::kProperty, "str");
571 CHECK_EQ(s->GetId(), wm_s->GetId());
575 TEST(HeapSnapshotCollection) {
577 v8::HandleScope scope(env->GetIsolate());
578 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
581 "k = {}; v = {}; s = 'str';\n"
582 "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
583 "map = new Map(); map.set(k, v); map[s] = s;\n");
584 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
585 CHECK(ValidateSnapshot(snapshot));
586 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
587 const v8::HeapGraphNode* k =
588 GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
590 const v8::HeapGraphNode* v =
591 GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
593 const v8::HeapGraphNode* s =
594 GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
597 const v8::HeapGraphNode* set =
598 GetProperty(global, v8::HeapGraphEdge::kProperty, "set");
600 CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
601 CHECK(v8_str("Set")->Equals(set->GetName()));
603 const v8::HeapGraphNode* set_table =
604 GetProperty(set, v8::HeapGraphEdge::kInternal, "table");
605 CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
606 CHECK_GT(set_table->GetChildrenCount(), 0);
608 for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
609 const v8::HeapGraphEdge* prop = set_table->GetChild(i);
610 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
611 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
615 CHECK_EQ(2, entries);
616 const v8::HeapGraphNode* set_s =
617 GetProperty(set, v8::HeapGraphEdge::kProperty, "str");
619 CHECK_EQ(s->GetId(), set_s->GetId());
621 const v8::HeapGraphNode* map =
622 GetProperty(global, v8::HeapGraphEdge::kProperty, "map");
624 CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
625 CHECK(v8_str("Map")->Equals(map->GetName()));
627 const v8::HeapGraphNode* map_table =
628 GetProperty(map, v8::HeapGraphEdge::kInternal, "table");
629 CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
630 CHECK_GT(map_table->GetChildrenCount(), 0);
632 for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
633 const v8::HeapGraphEdge* prop = map_table->GetChild(i);
634 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
635 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
639 CHECK_EQ(2, entries);
640 const v8::HeapGraphNode* map_s =
641 GetProperty(map, v8::HeapGraphEdge::kProperty, "str");
643 CHECK_EQ(s->GetId(), map_s->GetId());
647 TEST(HeapSnapshotInternalReferences) {
648 v8::Isolate* isolate = CcTest::isolate();
649 v8::HandleScope scope(isolate);
650 v8::Local<v8::ObjectTemplate> global_template =
651 v8::ObjectTemplate::New(isolate);
652 global_template->SetInternalFieldCount(2);
653 LocalContext env(NULL, global_template);
654 v8::Handle<v8::Object> global_proxy = env->Global();
655 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
656 CHECK_EQ(2, global->InternalFieldCount());
657 v8::Local<v8::Object> obj = v8::Object::New(isolate);
658 global->SetInternalField(0, v8_num(17));
659 global->SetInternalField(1, obj);
660 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
661 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
662 CHECK(ValidateSnapshot(snapshot));
663 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
664 // The first reference will not present, because it's a Smi.
665 CHECK(!GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
666 // The second reference is to an object.
667 CHECK(GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
671 TEST(HeapSnapshotAddressReuse) {
673 v8::HandleScope scope(env->GetIsolate());
674 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
679 "for (var i = 0; i < 10000; ++i)\n"
680 " a[i] = new A();\n");
681 const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
682 CHECK(ValidateSnapshot(snapshot1));
683 v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
686 "for (var i = 0; i < 10000; ++i)\n"
687 " a[i] = new A();\n");
688 CcTest::heap()->CollectAllGarbage();
690 const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
691 CHECK(ValidateSnapshot(snapshot2));
692 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
694 const v8::HeapGraphNode* array_node =
695 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
698 for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
699 const v8::HeapGraphEdge* prop = array_node->GetChild(i);
700 if (prop->GetType() != v8::HeapGraphEdge::kElement)
702 v8::SnapshotObjectId id = prop->GetToNode()->GetId();
706 CHECK_EQ(0, wrong_count);
710 TEST(HeapEntryIdsAndArrayShift) {
712 v8::HandleScope scope(env->GetIsolate());
713 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
716 "function AnObject() {\n"
717 " this.first = 'first';\n"
718 " this.second = 'second';\n"
720 "var a = new Array();\n"
721 "for (var i = 0; i < 10; ++i)\n"
722 " a.push(new AnObject());\n");
723 const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
724 CHECK(ValidateSnapshot(snapshot1));
727 "for (var i = 0; i < 1; ++i)\n"
730 CcTest::heap()->CollectAllGarbage();
732 const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
733 CHECK(ValidateSnapshot(snapshot2));
735 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
736 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
737 CHECK_NE(0u, global1->GetId());
738 CHECK_EQ(global1->GetId(), global2->GetId());
740 const v8::HeapGraphNode* a1 =
741 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
743 const v8::HeapGraphNode* k1 =
744 GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
746 const v8::HeapGraphNode* a2 =
747 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
749 const v8::HeapGraphNode* k2 =
750 GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
753 CHECK_EQ(a1->GetId(), a2->GetId());
754 CHECK_EQ(k1->GetId(), k2->GetId());
758 TEST(HeapEntryIdsAndGC) {
760 v8::HandleScope scope(env->GetIsolate());
761 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
765 "function B(x) { this.x = x; }\n"
767 "var b = new B(a);");
768 const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
769 CHECK(ValidateSnapshot(snapshot1));
771 CcTest::heap()->CollectAllGarbage();
773 const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
774 CHECK(ValidateSnapshot(snapshot2));
776 CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000u);
777 CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
778 snapshot2->GetMaxSnapshotJSObjectId());
780 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
781 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
782 CHECK_NE(0u, global1->GetId());
783 CHECK_EQ(global1->GetId(), global2->GetId());
784 const v8::HeapGraphNode* A1 =
785 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
787 const v8::HeapGraphNode* A2 =
788 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
790 CHECK_NE(0u, A1->GetId());
791 CHECK_EQ(A1->GetId(), A2->GetId());
792 const v8::HeapGraphNode* B1 =
793 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
795 const v8::HeapGraphNode* B2 =
796 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
798 CHECK_NE(0u, B1->GetId());
799 CHECK_EQ(B1->GetId(), B2->GetId());
800 const v8::HeapGraphNode* a1 =
801 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
803 const v8::HeapGraphNode* a2 =
804 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
806 CHECK_NE(0u, a1->GetId());
807 CHECK_EQ(a1->GetId(), a2->GetId());
808 const v8::HeapGraphNode* b1 =
809 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
811 const v8::HeapGraphNode* b2 =
812 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
814 CHECK_NE(0u, b1->GetId());
815 CHECK_EQ(b1->GetId(), b2->GetId());
819 TEST(HeapSnapshotRootPreservedAfterSorting) {
821 v8::HandleScope scope(env->GetIsolate());
822 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
823 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
824 CHECK(ValidateSnapshot(snapshot));
825 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
826 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
827 snapshot))->GetSortedEntriesList();
828 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
829 CHECK_EQ(root1, root2);
835 class TestJSONStream : public v8::OutputStream {
837 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
838 explicit TestJSONStream(int abort_countdown)
839 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
840 virtual ~TestJSONStream() {}
841 virtual void EndOfStream() { ++eos_signaled_; }
842 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
843 if (abort_countdown_ > 0) --abort_countdown_;
844 if (abort_countdown_ == 0) return kAbort;
845 CHECK_GT(chars_written, 0);
846 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
847 i::MemCopy(chunk.start(), buffer, chars_written);
850 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
854 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
855 int eos_signaled() { return eos_signaled_; }
856 int size() { return buffer_.size(); }
859 i::Collector<char> buffer_;
861 int abort_countdown_;
864 class OneByteResource : public v8::String::ExternalOneByteStringResource {
866 explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
867 length_ = string.length();
869 virtual const char* data() const { return data_; }
870 virtual size_t length() const { return length_; }
878 TEST(HeapSnapshotJSONSerialization) {
879 v8::Isolate* isolate = CcTest::isolate();
881 v8::HandleScope scope(isolate);
882 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
884 #define STRING_LITERAL_FOR_TEST \
885 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
887 "function A(s) { this.s = s; }\n"
888 "function B(x) { this.x = x; }\n"
889 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
890 "var b = new B(a);");
891 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
892 CHECK(ValidateSnapshot(snapshot));
894 TestJSONStream stream;
895 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
896 CHECK_GT(stream.size(), 0);
897 CHECK_EQ(1, stream.eos_signaled());
898 i::ScopedVector<char> json(stream.size());
899 stream.WriteTo(json);
901 // Verify that snapshot string is valid JSON.
902 OneByteResource* json_res = new OneByteResource(json);
903 v8::Local<v8::String> json_string =
904 v8::String::NewExternal(env->GetIsolate(), json_res);
905 env->Global()->Set(v8_str("json_snapshot"), json_string);
906 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
907 "var parsed = JSON.parse(json_snapshot); true;");
908 CHECK(!snapshot_parse_result.IsEmpty());
910 // Verify that snapshot object has required fields.
911 v8::Local<v8::Object> parsed_snapshot =
912 env->Global()->Get(v8_str("parsed"))->ToObject(isolate);
913 CHECK(parsed_snapshot->Has(v8_str("snapshot")));
914 CHECK(parsed_snapshot->Has(v8_str("nodes")));
915 CHECK(parsed_snapshot->Has(v8_str("edges")));
916 CHECK(parsed_snapshot->Has(v8_str("strings")));
918 // Get node and edge "member" offsets.
919 v8::Local<v8::Value> meta_analysis_result = CompileRun(
920 "var meta = parsed.snapshot.meta;\n"
921 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
922 "var node_fields_count = meta.node_fields.length;\n"
923 "var edge_fields_count = meta.edge_fields.length;\n"
924 "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
925 "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
926 "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
927 "var property_type ="
928 " meta.edge_types[edge_type_offset].indexOf('property');\n"
929 "var shortcut_type ="
930 " meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
931 "var node_count = parsed.nodes.length / node_fields_count;\n"
932 "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
933 "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
934 " first_edge_indexes[i] = first_edge_index;\n"
935 " first_edge_index += edge_fields_count *\n"
936 " parsed.nodes[i * node_fields_count + edge_count_offset];\n"
938 "first_edge_indexes[node_count] = first_edge_index;\n");
939 CHECK(!meta_analysis_result.IsEmpty());
941 // A helper function for processing encoded nodes.
943 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
944 " var nodes = parsed.nodes;\n"
945 " var edges = parsed.edges;\n"
946 " var strings = parsed.strings;\n"
947 " var node_ordinal = pos / node_fields_count;\n"
948 " for (var i = parsed.first_edge_indexes[node_ordinal],\n"
949 " count = parsed.first_edge_indexes[node_ordinal + 1];\n"
950 " i < count; i += edge_fields_count) {\n"
951 " if (edges[i + edge_type_offset] === prop_type\n"
952 " && strings[edges[i + edge_name_offset]] === prop_name)\n"
953 " return edges[i + edge_to_node_offset];\n"
957 // Get the string index using the path: <root> -> <global>.b.x.s
958 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
959 "GetChildPosByProperty(\n"
960 " GetChildPosByProperty(\n"
961 " GetChildPosByProperty("
962 " parsed.edges[edge_fields_count + edge_to_node_offset],"
963 " \"b\", property_type),\n"
964 " \"x\", property_type),"
965 " \"s\", property_type)");
966 CHECK(!string_obj_pos_val.IsEmpty());
968 static_cast<int>(string_obj_pos_val->ToNumber(isolate)->Value());
969 v8::Local<v8::Object> nodes_array =
970 parsed_snapshot->Get(v8_str("nodes"))->ToObject(isolate);
971 int string_index = static_cast<int>(
972 nodes_array->Get(string_obj_pos + 1)->ToNumber(isolate)->Value());
973 CHECK_GT(string_index, 0);
974 v8::Local<v8::Object> strings_array =
975 parsed_snapshot->Get(v8_str("strings"))->ToObject(isolate);
976 v8::Local<v8::String> string =
977 strings_array->Get(string_index)->ToString(isolate);
978 v8::Local<v8::String> ref_string =
979 CompileRun(STRING_LITERAL_FOR_TEST)->ToString(isolate);
980 #undef STRING_LITERAL_FOR_TEST
981 CHECK_LT(0, strcmp(*v8::String::Utf8Value(ref_string),
982 *v8::String::Utf8Value(string)));
986 TEST(HeapSnapshotJSONSerializationAborting) {
988 v8::HandleScope scope(env->GetIsolate());
989 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
990 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
991 CHECK(ValidateSnapshot(snapshot));
992 TestJSONStream stream(5);
993 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
994 CHECK_GT(stream.size(), 0);
995 CHECK_EQ(0, stream.eos_signaled());
1000 class TestStatsStream : public v8::OutputStream {
1004 updates_written_(0),
1007 intervals_count_(0),
1008 first_interval_index_(-1) { }
1009 TestStatsStream(const TestStatsStream& stream)
1010 : v8::OutputStream(stream),
1011 eos_signaled_(stream.eos_signaled_),
1012 updates_written_(stream.updates_written_),
1013 entries_count_(stream.entries_count_),
1014 entries_size_(stream.entries_size_),
1015 intervals_count_(stream.intervals_count_),
1016 first_interval_index_(stream.first_interval_index_) { }
1017 virtual ~TestStatsStream() {}
1018 virtual void EndOfStream() { ++eos_signaled_; }
1019 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1023 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
1024 int updates_written) {
1026 DCHECK(updates_written);
1027 updates_written_ += updates_written;
1029 if (first_interval_index_ == -1 && updates_written != 0)
1030 first_interval_index_ = buffer[0].index;
1031 for (int i = 0; i < updates_written; ++i) {
1032 entries_count_ += buffer[i].count;
1033 entries_size_ += buffer[i].size;
1038 int eos_signaled() { return eos_signaled_; }
1039 int updates_written() { return updates_written_; }
1040 uint32_t entries_count() const { return entries_count_; }
1041 uint32_t entries_size() const { return entries_size_; }
1042 int intervals_count() const { return intervals_count_; }
1043 int first_interval_index() const { return first_interval_index_; }
1047 int updates_written_;
1048 uint32_t entries_count_;
1049 uint32_t entries_size_;
1050 int intervals_count_;
1051 int first_interval_index_;
1056 static TestStatsStream GetHeapStatsUpdate(
1057 v8::HeapProfiler* heap_profiler,
1058 v8::SnapshotObjectId* object_id = NULL) {
1059 TestStatsStream stream;
1060 int64_t timestamp = -1;
1061 v8::SnapshotObjectId last_seen_id =
1062 heap_profiler->GetHeapStats(&stream, ×tamp);
1064 *object_id = last_seen_id;
1065 CHECK_NE(-1, timestamp);
1066 CHECK_EQ(1, stream.eos_signaled());
1071 TEST(HeapSnapshotObjectsStats) {
1073 v8::HandleScope scope(env->GetIsolate());
1074 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1076 heap_profiler->StartTrackingHeapObjects();
1077 // We have to call GC 6 times. In other case the garbage will be
1078 // the reason of flakiness.
1079 for (int i = 0; i < 6; ++i) {
1080 CcTest::heap()->CollectAllGarbage();
1083 v8::SnapshotObjectId initial_id;
1085 // Single chunk of data expected in update. Initial data.
1086 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1088 CHECK_EQ(1, stats_update.intervals_count());
1089 CHECK_EQ(1, stats_update.updates_written());
1090 CHECK_LT(0u, stats_update.entries_size());
1091 CHECK_EQ(0, stats_update.first_interval_index());
1094 // No data expected in update because nothing has happened.
1095 v8::SnapshotObjectId same_id;
1096 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
1097 CHECK_EQ(initial_id, same_id);
1100 v8::SnapshotObjectId additional_string_id;
1101 v8::HandleScope inner_scope_1(env->GetIsolate());
1104 // Single chunk of data with one new entry expected in update.
1105 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1106 &additional_string_id);
1107 CHECK_LT(same_id, additional_string_id);
1108 CHECK_EQ(1, stats_update.intervals_count());
1109 CHECK_EQ(1, stats_update.updates_written());
1110 CHECK_LT(0u, stats_update.entries_size());
1111 CHECK_EQ(1u, stats_update.entries_count());
1112 CHECK_EQ(2, stats_update.first_interval_index());
1115 // No data expected in update because nothing happened.
1116 v8::SnapshotObjectId last_id;
1117 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
1118 CHECK_EQ(additional_string_id, last_id);
1121 v8::HandleScope inner_scope_2(env->GetIsolate());
1124 uint32_t entries_size;
1126 v8::HandleScope inner_scope_3(env->GetIsolate());
1131 // Single chunk of data with three new entries expected in update.
1132 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1133 CHECK_EQ(1, stats_update.intervals_count());
1134 CHECK_EQ(1, stats_update.updates_written());
1135 CHECK_LT(0u, entries_size = stats_update.entries_size());
1136 CHECK_EQ(3u, stats_update.entries_count());
1137 CHECK_EQ(4, stats_update.first_interval_index());
1142 // Single chunk of data with two left entries expected in update.
1143 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1144 CHECK_EQ(1, stats_update.intervals_count());
1145 CHECK_EQ(1, stats_update.updates_written());
1146 CHECK_GT(entries_size, stats_update.entries_size());
1147 CHECK_EQ(1u, stats_update.entries_count());
1148 // Two strings from forth interval were released.
1149 CHECK_EQ(4, stats_update.first_interval_index());
1154 // Single chunk of data with 0 left entries expected in update.
1155 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1156 CHECK_EQ(1, stats_update.intervals_count());
1157 CHECK_EQ(1, stats_update.updates_written());
1158 CHECK_EQ(0u, stats_update.entries_size());
1159 CHECK_EQ(0u, stats_update.entries_count());
1160 // The last string from forth interval was released.
1161 CHECK_EQ(4, stats_update.first_interval_index());
1165 // Single chunk of data with 0 left entries expected in update.
1166 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1167 CHECK_EQ(1, stats_update.intervals_count());
1168 CHECK_EQ(1, stats_update.updates_written());
1169 CHECK_EQ(0u, stats_update.entries_size());
1170 CHECK_EQ(0u, stats_update.entries_count());
1171 // The only string from the second interval was released.
1172 CHECK_EQ(2, stats_update.first_interval_index());
1175 v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
1176 CHECK_EQ(0u, array->Length());
1177 // Force array's buffer allocation.
1178 array->Set(2, v8_num(7));
1180 uint32_t entries_size;
1182 // Single chunk of data with 2 entries expected in update.
1183 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1184 CHECK_EQ(1, stats_update.intervals_count());
1185 CHECK_EQ(1, stats_update.updates_written());
1186 CHECK_LT(0u, entries_size = stats_update.entries_size());
1187 // They are the array and its buffer.
1188 CHECK_EQ(2u, stats_update.entries_count());
1189 CHECK_EQ(8, stats_update.first_interval_index());
1192 for (int i = 0; i < 100; ++i)
1193 array->Set(i, v8_num(i));
1196 // Single chunk of data with 1 entry expected in update.
1197 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1198 CHECK_EQ(1, stats_update.intervals_count());
1199 // The first interval was changed because old buffer was collected.
1200 // The second interval was changed because new buffer was allocated.
1201 CHECK_EQ(2, stats_update.updates_written());
1202 CHECK_LT(entries_size, stats_update.entries_size());
1203 CHECK_EQ(2u, stats_update.entries_count());
1204 CHECK_EQ(8, stats_update.first_interval_index());
1207 heap_profiler->StopTrackingHeapObjects();
1211 TEST(HeapObjectIds) {
1213 v8::Isolate* isolate = env->GetIsolate();
1214 v8::HandleScope scope(isolate);
1215 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1217 const int kLength = 10;
1218 v8::Handle<v8::Object> objects[kLength];
1219 v8::SnapshotObjectId ids[kLength];
1221 heap_profiler->StartTrackingHeapObjects(false);
1223 for (int i = 0; i < kLength; i++) {
1224 objects[i] = v8::Object::New(isolate);
1226 GetHeapStatsUpdate(heap_profiler);
1228 for (int i = 0; i < kLength; i++) {
1229 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1230 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1234 heap_profiler->StopTrackingHeapObjects();
1235 CcTest::heap()->CollectAllAvailableGarbage();
1237 for (int i = 0; i < kLength; i++) {
1238 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1239 CHECK_EQ(ids[i], id);
1240 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1241 CHECK(objects[i]->Equals(obj));
1244 heap_profiler->ClearObjectIds();
1245 for (int i = 0; i < kLength; i++) {
1246 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1247 CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, id);
1248 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1249 CHECK(obj.IsEmpty());
1254 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
1255 const v8::HeapGraphNode* node,
1256 int level, int max_level) {
1257 if (level > max_level) return;
1258 CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
1259 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
1260 const v8::HeapGraphEdge* prop = node->GetChild(i);
1261 const v8::HeapGraphNode* child =
1262 snapshot->GetNodeById(prop->GetToNode()->GetId());
1263 CHECK_EQ(prop->GetToNode()->GetId(), child->GetId());
1264 CHECK_EQ(prop->GetToNode(), child);
1265 CheckChildrenIds(snapshot, child, level + 1, max_level);
1270 TEST(HeapSnapshotGetNodeById) {
1272 v8::HandleScope scope(env->GetIsolate());
1273 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1275 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1276 CHECK(ValidateSnapshot(snapshot));
1277 const v8::HeapGraphNode* root = snapshot->GetRoot();
1278 CheckChildrenIds(snapshot, root, 0, 3);
1279 // Check a big id, which should not exist yet.
1280 CHECK(!snapshot->GetNodeById(0x1000000UL));
1284 TEST(HeapSnapshotGetSnapshotObjectId) {
1286 v8::HandleScope scope(env->GetIsolate());
1287 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1288 CompileRun("globalObject = {};\n");
1289 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1290 CHECK(ValidateSnapshot(snapshot));
1291 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1292 const v8::HeapGraphNode* global_object =
1293 GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
1294 CHECK(global_object);
1296 v8::Local<v8::Value> globalObjectHandle = env->Global()->Get(
1297 v8::String::NewFromUtf8(env->GetIsolate(), "globalObject"));
1298 CHECK(!globalObjectHandle.IsEmpty());
1299 CHECK(globalObjectHandle->IsObject());
1301 v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
1302 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
1303 CHECK_EQ(id, global_object->GetId());
1307 TEST(HeapSnapshotUnknownSnapshotObjectId) {
1309 v8::HandleScope scope(env->GetIsolate());
1310 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1311 CompileRun("globalObject = {};\n");
1312 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1313 CHECK(ValidateSnapshot(snapshot));
1314 const v8::HeapGraphNode* node =
1315 snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
1322 class TestActivityControl : public v8::ActivityControl {
1324 explicit TestActivityControl(int abort_count)
1325 : done_(0), total_(0), abort_count_(abort_count) {}
1326 ControlOption ReportProgressValue(int done, int total) {
1329 return --abort_count_ != 0 ? kContinue : kAbort;
1331 int done() { return done_; }
1332 int total() { return total_; }
1342 TEST(TakeHeapSnapshotAborting) {
1344 v8::HandleScope scope(env->GetIsolate());
1346 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1347 const int snapshots_count = heap_profiler->GetSnapshotCount();
1348 TestActivityControl aborting_control(1);
1349 const v8::HeapSnapshot* no_snapshot =
1350 heap_profiler->TakeHeapSnapshot(&aborting_control);
1351 CHECK(!no_snapshot);
1352 CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1353 CHECK_GT(aborting_control.total(), aborting_control.done());
1355 TestActivityControl control(-1); // Don't abort.
1356 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
1357 CHECK(ValidateSnapshot(snapshot));
1360 CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1361 CHECK_EQ(control.total(), control.done());
1362 CHECK_GT(control.total(), 0);
1368 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1370 TestRetainedObjectInfo(int hash,
1371 const char* group_label,
1373 intptr_t element_count = -1,
1377 group_label_(group_label),
1379 element_count_(element_count),
1381 instances.Add(this);
1383 virtual ~TestRetainedObjectInfo() {}
1384 virtual void Dispose() {
1388 virtual bool IsEquivalent(RetainedObjectInfo* other) {
1389 return GetHash() == other->GetHash();
1391 virtual intptr_t GetHash() { return hash_; }
1392 virtual const char* GetGroupLabel() { return group_label_; }
1393 virtual const char* GetLabel() { return label_; }
1394 virtual intptr_t GetElementCount() { return element_count_; }
1395 virtual intptr_t GetSizeInBytes() { return size_; }
1396 bool disposed() { return disposed_; }
1398 static v8::RetainedObjectInfo* WrapperInfoCallback(
1399 uint16_t class_id, v8::Handle<v8::Value> wrapper) {
1400 if (class_id == 1) {
1401 if (wrapper->IsString()) {
1402 v8::String::Utf8Value utf8(wrapper);
1403 if (strcmp(*utf8, "AAA") == 0)
1404 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1405 else if (strcmp(*utf8, "BBB") == 0)
1406 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1408 } else if (class_id == 2) {
1409 if (wrapper->IsString()) {
1410 v8::String::Utf8Value utf8(wrapper);
1411 if (strcmp(*utf8, "CCC") == 0)
1412 return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1419 static i::List<TestRetainedObjectInfo*> instances;
1424 const char* group_label_;
1426 intptr_t element_count_;
1431 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1435 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1436 v8::HeapGraphNode::Type type,
1438 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1439 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1440 if (node->GetType() == type && strcmp(name,
1441 const_cast<i::HeapEntry*>(
1442 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1450 TEST(HeapSnapshotRetainedObjectInfo) {
1452 v8::Isolate* isolate = env->GetIsolate();
1453 v8::HandleScope scope(isolate);
1454 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1456 heap_profiler->SetWrapperClassInfoProvider(
1457 1, TestRetainedObjectInfo::WrapperInfoCallback);
1458 heap_profiler->SetWrapperClassInfoProvider(
1459 2, TestRetainedObjectInfo::WrapperInfoCallback);
1460 v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1461 p_AAA.SetWrapperClassId(1);
1462 v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1463 p_BBB.SetWrapperClassId(1);
1464 v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1465 p_CCC.SetWrapperClassId(2);
1466 CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1467 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1468 CHECK(ValidateSnapshot(snapshot));
1470 CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
1471 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
1472 CHECK(TestRetainedObjectInfo::instances[i]->disposed());
1473 delete TestRetainedObjectInfo::instances[i];
1476 const v8::HeapGraphNode* native_group_aaa = GetNode(
1477 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1478 CHECK(native_group_aaa);
1479 CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1480 const v8::HeapGraphNode* aaa = GetNode(
1481 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1483 CHECK_EQ(2, aaa->GetChildrenCount());
1485 const v8::HeapGraphNode* native_group_ccc = GetNode(
1486 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1487 const v8::HeapGraphNode* ccc = GetNode(
1488 native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1491 const v8::HeapGraphNode* n_AAA = GetNode(
1492 aaa, v8::HeapGraphNode::kString, "AAA");
1494 const v8::HeapGraphNode* n_BBB = GetNode(
1495 aaa, v8::HeapGraphNode::kString, "BBB");
1497 CHECK_EQ(1, ccc->GetChildrenCount());
1498 const v8::HeapGraphNode* n_CCC = GetNode(
1499 ccc, v8::HeapGraphNode::kString, "CCC");
1502 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1503 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1504 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1508 class GraphWithImplicitRefs {
1510 static const int kObjectsCount = 4;
1511 explicit GraphWithImplicitRefs(LocalContext* env) {
1514 isolate_ = (*env)->GetIsolate();
1515 for (int i = 0; i < kObjectsCount; i++) {
1516 objects_[i].Reset(isolate_, v8::Object::New(isolate_));
1518 (*env)->Global()->Set(v8_str("root_object"),
1519 v8::Local<v8::Value>::New(isolate_, objects_[0]));
1521 ~GraphWithImplicitRefs() {
1525 static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) {
1526 instance_->AddImplicitReferences();
1530 void AddImplicitReferences() {
1532 isolate_->SetObjectGroupId(objects_[0],
1534 isolate_->SetReferenceFromGroup(
1535 v8::UniqueId(1), objects_[1]);
1536 // Adding two more references: 1 -> 2, 1 -> 3
1537 isolate_->SetReference(objects_[1].As<v8::Object>(),
1539 isolate_->SetReference(objects_[1].As<v8::Object>(),
1543 v8::Persistent<v8::Value> objects_[kObjectsCount];
1544 static GraphWithImplicitRefs* instance_;
1545 v8::Isolate* isolate_;
1548 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1551 TEST(HeapSnapshotImplicitReferences) {
1553 v8::HandleScope scope(env->GetIsolate());
1554 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1556 GraphWithImplicitRefs graph(&env);
1557 v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1559 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1560 CHECK(ValidateSnapshot(snapshot));
1562 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1563 const v8::HeapGraphNode* obj0 = GetProperty(
1564 global_object, v8::HeapGraphEdge::kProperty, "root_object");
1566 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1567 const v8::HeapGraphNode* obj1 = GetProperty(
1568 obj0, v8::HeapGraphEdge::kInternal, "native");
1570 int implicit_targets_count = 0;
1571 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1572 const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1573 v8::String::Utf8Value prop_name(prop->GetName());
1574 if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1575 strcmp("native", *prop_name) == 0) {
1576 ++implicit_targets_count;
1579 CHECK_EQ(2, implicit_targets_count);
1580 v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1584 TEST(DeleteAllHeapSnapshots) {
1586 v8::HandleScope scope(env->GetIsolate());
1587 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1589 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1590 heap_profiler->DeleteAllHeapSnapshots();
1591 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1592 CHECK(heap_profiler->TakeHeapSnapshot());
1593 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1594 heap_profiler->DeleteAllHeapSnapshots();
1595 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1596 CHECK(heap_profiler->TakeHeapSnapshot());
1597 CHECK(heap_profiler->TakeHeapSnapshot());
1598 CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1599 heap_profiler->DeleteAllHeapSnapshots();
1600 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1604 static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
1605 const v8::HeapSnapshot* snapshot) {
1606 int length = profiler->GetSnapshotCount();
1607 for (int i = 0; i < length; i++) {
1608 if (snapshot == profiler->GetHeapSnapshot(i)) return true;
1614 TEST(DeleteHeapSnapshot) {
1616 v8::HandleScope scope(env->GetIsolate());
1617 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1619 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1620 const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
1623 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1624 CHECK(FindHeapSnapshot(heap_profiler, s1));
1625 const_cast<v8::HeapSnapshot*>(s1)->Delete();
1626 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1627 CHECK(!FindHeapSnapshot(heap_profiler, s1));
1629 const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
1631 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1632 CHECK(FindHeapSnapshot(heap_profiler, s2));
1633 const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
1635 CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1637 CHECK(FindHeapSnapshot(heap_profiler, s3));
1638 const_cast<v8::HeapSnapshot*>(s2)->Delete();
1639 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1640 CHECK(!FindHeapSnapshot(heap_profiler, s2));
1641 CHECK(FindHeapSnapshot(heap_profiler, s3));
1642 const_cast<v8::HeapSnapshot*>(s3)->Delete();
1643 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1644 CHECK(!FindHeapSnapshot(heap_profiler, s3));
1648 class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
1650 virtual const char* GetName(v8::Handle<v8::Object> object) {
1651 return "Global object name";
1656 TEST(GlobalObjectName) {
1658 v8::HandleScope scope(env->GetIsolate());
1659 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1661 CompileRun("document = { URL:\"abcdefgh\" };");
1663 NameResolver name_resolver;
1664 const v8::HeapSnapshot* snapshot =
1665 heap_profiler->TakeHeapSnapshot(NULL, &name_resolver);
1666 CHECK(ValidateSnapshot(snapshot));
1667 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1670 strcmp("Object / Global object name",
1671 const_cast<i::HeapEntry*>(
1672 reinterpret_cast<const i::HeapEntry*>(global))->name()));
1676 TEST(GlobalObjectFields) {
1678 v8::HandleScope scope(env->GetIsolate());
1679 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1680 CompileRun("obj = {};");
1681 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1682 CHECK(ValidateSnapshot(snapshot));
1683 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1684 const v8::HeapGraphNode* builtins =
1685 GetProperty(global, v8::HeapGraphEdge::kInternal, "builtins");
1687 const v8::HeapGraphNode* native_context =
1688 GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
1689 CHECK(native_context);
1690 const v8::HeapGraphNode* global_proxy =
1691 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
1692 CHECK(global_proxy);
1696 TEST(NoHandleLeaks) {
1698 v8::HandleScope scope(env->GetIsolate());
1699 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1701 CompileRun("document = { URL:\"abcdefgh\" };");
1703 i::Isolate* isolate = CcTest::i_isolate();
1704 int count_before = i::HandleScope::NumberOfHandles(isolate);
1705 heap_profiler->TakeHeapSnapshot();
1706 int count_after = i::HandleScope::NumberOfHandles(isolate);
1707 CHECK_EQ(count_before, count_after);
1711 TEST(NodesIteration) {
1713 v8::HandleScope scope(env->GetIsolate());
1714 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1715 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1716 CHECK(ValidateSnapshot(snapshot));
1717 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1719 // Verify that we can find this object by iteration.
1720 const int nodes_count = snapshot->GetNodesCount();
1722 for (int i = 0; i < nodes_count; ++i) {
1723 if (snapshot->GetNode(i) == global)
1730 TEST(GetHeapValueForNode) {
1732 v8::HandleScope scope(env->GetIsolate());
1733 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1735 CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
1736 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1737 CHECK(ValidateSnapshot(snapshot));
1738 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1739 CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
1740 v8::Local<v8::Object> js_global =
1741 env->Global()->GetPrototype().As<v8::Object>();
1742 CHECK(js_global == heap_profiler->FindObjectById(global->GetId()));
1743 const v8::HeapGraphNode* obj = GetProperty(
1744 global, v8::HeapGraphEdge::kProperty, "a");
1745 CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
1746 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1747 CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId()));
1748 const v8::HeapGraphNode* s_prop =
1749 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1750 v8::Local<v8::String> js_s_prop =
1751 js_obj->Get(v8_str("s_prop")).As<v8::String>();
1752 CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
1753 const v8::HeapGraphNode* n_prop =
1754 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1755 v8::Local<v8::String> js_n_prop =
1756 js_obj->Get(v8_str("n_prop")).As<v8::String>();
1757 CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
1761 TEST(GetHeapValueForDeletedObject) {
1763 v8::HandleScope scope(env->GetIsolate());
1764 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1766 // It is impossible to delete a global property, so we are about to delete a
1767 // property of the "a" object. Also, the "p" object can't be an empty one
1768 // because the empty object is static and isn't actually deleted.
1769 CompileRun("a = { p: { r: {} } };");
1770 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1771 CHECK(ValidateSnapshot(snapshot));
1772 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1773 const v8::HeapGraphNode* obj = GetProperty(
1774 global, v8::HeapGraphEdge::kProperty, "a");
1775 const v8::HeapGraphNode* prop = GetProperty(
1776 obj, v8::HeapGraphEdge::kProperty, "p");
1778 // Perform the check inside a nested local scope to avoid creating a
1779 // reference to the object we are deleting.
1780 v8::HandleScope scope(env->GetIsolate());
1781 CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
1783 CompileRun("delete a.p;");
1784 CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
1788 static int StringCmp(const char* ref, i::String* act) {
1789 v8::base::SmartArrayPointer<char> s_act = act->ToCString();
1790 int result = strcmp(ref, s_act.get());
1792 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
1797 TEST(GetConstructorName) {
1799 v8::HandleScope scope(env->GetIsolate());
1802 "function Constructor1() {};\n"
1803 "var obj1 = new Constructor1();\n"
1804 "var Constructor2 = function() {};\n"
1805 "var obj2 = new Constructor2();\n"
1807 "obj3.constructor = function Constructor3() {};\n"
1809 "// Slow properties\n"
1810 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1811 "obj4.constructor = function Constructor4() {};\n"
1814 "obj6.constructor = 6;");
1815 v8::Local<v8::Object> js_global =
1816 env->Global()->GetPrototype().As<v8::Object>();
1817 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1818 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1819 CHECK_EQ(0, StringCmp(
1820 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1821 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1822 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1823 CHECK_EQ(0, StringCmp(
1824 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1825 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1826 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1827 // TODO(verwaest): Restore to Constructor3 once supported by the
1828 // heap-snapshot-generator.
1830 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1831 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1832 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1833 // TODO(verwaest): Restore to Constructor4 once supported by the
1834 // heap-snapshot-generator.
1836 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1837 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1838 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1839 CHECK_EQ(0, StringCmp(
1840 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1841 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1842 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1843 CHECK_EQ(0, StringCmp(
1844 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1848 TEST(FastCaseAccessors) {
1850 v8::HandleScope scope(env->GetIsolate());
1851 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1853 CompileRun("var obj1 = {};\n"
1854 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1857 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1858 " return this.value_ = value;\n"
1860 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1861 CHECK(ValidateSnapshot(snapshot));
1863 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1865 const v8::HeapGraphNode* obj1 =
1866 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1868 const v8::HeapGraphNode* func;
1869 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1871 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1873 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1875 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1880 TEST(FastCaseRedefinedAccessors) {
1882 v8::HandleScope scope(env->GetIsolate());
1883 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1887 "Object.defineProperty(obj1, 'prop', { "
1888 " get: function() { return 42; },\n"
1889 " set: function(value) { return this.prop_ = value; },\n"
1890 " configurable: true,\n"
1891 " enumerable: true,\n"
1893 "Object.defineProperty(obj1, 'prop', { "
1894 " get: function() { return 153; },\n"
1895 " set: function(value) { return this.prop_ = value; },\n"
1896 " configurable: true,\n"
1897 " enumerable: true,\n"
1899 v8::Local<v8::Object> js_global =
1900 env->Global()->GetPrototype().As<v8::Object>();
1901 i::Handle<i::JSObject> js_obj1 =
1902 v8::Utils::OpenHandle(*js_global->Get(v8_str("obj1")).As<v8::Object>());
1905 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1906 CHECK(ValidateSnapshot(snapshot));
1907 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1909 const v8::HeapGraphNode* obj1 =
1910 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1912 const v8::HeapGraphNode* func;
1913 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get prop");
1915 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set prop");
1920 TEST(SlowCaseAccessors) {
1922 v8::HandleScope scope(env->GetIsolate());
1923 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1925 CompileRun("var obj1 = {};\n"
1926 "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
1927 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1930 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1931 " return this.value_ = value;\n"
1933 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1934 CHECK(ValidateSnapshot(snapshot));
1936 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1938 const v8::HeapGraphNode* obj1 =
1939 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1941 const v8::HeapGraphNode* func;
1942 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1944 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1946 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1948 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1953 TEST(HiddenPropertiesFastCase) {
1954 v8::Isolate* isolate = CcTest::isolate();
1956 v8::HandleScope scope(isolate);
1957 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1960 "function C(x) { this.a = this; this.b = x; }\n"
1961 "c = new C(2012);\n");
1962 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1963 CHECK(ValidateSnapshot(snapshot));
1964 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1965 const v8::HeapGraphNode* c =
1966 GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1968 const v8::HeapGraphNode* hidden_props =
1969 GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties");
1970 CHECK(!hidden_props);
1972 v8::Handle<v8::Value> cHandle =
1973 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c"));
1974 CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
1975 cHandle->ToObject(isolate)->SetHiddenValue(v8_str("key"), v8_str("val"));
1977 snapshot = heap_profiler->TakeHeapSnapshot();
1978 CHECK(ValidateSnapshot(snapshot));
1979 global = GetGlobalObject(snapshot);
1980 c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1982 hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal,
1983 "hidden_properties");
1984 CHECK(hidden_props);
1988 TEST(AccessorInfo) {
1990 v8::HandleScope scope(env->GetIsolate());
1991 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1993 CompileRun("function foo(x) { }\n");
1994 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
1995 CHECK(ValidateSnapshot(snapshot));
1996 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1997 const v8::HeapGraphNode* foo =
1998 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2000 const v8::HeapGraphNode* map =
2001 GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
2003 const v8::HeapGraphNode* descriptors =
2004 GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
2006 const v8::HeapGraphNode* length_name =
2007 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
2009 CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(length_name->GetName())));
2010 const v8::HeapGraphNode* length_accessor =
2011 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
2012 CHECK(length_accessor);
2013 CHECK_EQ(0, strcmp("system / ExecutableAccessorInfo",
2014 *v8::String::Utf8Value(length_accessor->GetName())));
2015 const v8::HeapGraphNode* name =
2016 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
2018 const v8::HeapGraphNode* getter =
2019 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
2021 const v8::HeapGraphNode* setter =
2022 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
2027 bool HasWeakEdge(const v8::HeapGraphNode* node) {
2028 for (int i = 0; i < node->GetChildrenCount(); ++i) {
2029 const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
2030 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
2036 bool HasWeakGlobalHandle() {
2037 v8::Isolate* isolate = CcTest::isolate();
2038 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2039 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2040 CHECK(ValidateSnapshot(snapshot));
2041 const v8::HeapGraphNode* gc_roots = GetNode(
2042 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2044 const v8::HeapGraphNode* global_handles = GetNode(
2045 gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
2046 CHECK(global_handles);
2047 return HasWeakEdge(global_handles);
2051 static void PersistentHandleCallback(
2052 const v8::WeakCallbackInfo<v8::Persistent<v8::Object> >& data) {
2053 data.GetParameter()->Reset();
2057 TEST(WeakGlobalHandle) {
2059 v8::HandleScope scope(env->GetIsolate());
2061 CHECK(!HasWeakGlobalHandle());
2063 v8::Persistent<v8::Object> handle(env->GetIsolate(),
2064 v8::Object::New(env->GetIsolate()));
2065 handle.SetWeak(&handle, PersistentHandleCallback,
2066 v8::WeakCallbackType::kParameter);
2068 CHECK(HasWeakGlobalHandle());
2072 TEST(SfiAndJsFunctionWeakRefs) {
2074 v8::HandleScope scope(env->GetIsolate());
2075 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2078 "fun = (function (x) { return function () { return x + 1; } })(1);");
2079 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2080 CHECK(ValidateSnapshot(snapshot));
2081 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2083 const v8::HeapGraphNode* fun =
2084 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2085 CHECK(!HasWeakEdge(fun));
2086 const v8::HeapGraphNode* shared =
2087 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
2088 CHECK(!HasWeakEdge(shared));
2092 TEST(NoDebugObjectInSnapshot) {
2094 v8::HandleScope scope(env->GetIsolate());
2095 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2097 CHECK(CcTest::i_isolate()->debug()->Load());
2098 CompileRun("foo = {};");
2099 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2100 CHECK(ValidateSnapshot(snapshot));
2101 const v8::HeapGraphNode* root = snapshot->GetRoot();
2102 int globals_count = 0;
2104 for (int i = 0; i < root->GetChildrenCount(); ++i) {
2105 const v8::HeapGraphEdge* edge = root->GetChild(i);
2106 if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
2108 const v8::HeapGraphNode* global = edge->GetToNode();
2109 const v8::HeapGraphNode* foo =
2110 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2111 if (foo != nullptr) {
2116 CHECK_EQ(2, globals_count);
2121 TEST(AllStrongGcRootsHaveNames) {
2123 v8::HandleScope scope(env->GetIsolate());
2124 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2126 CompileRun("foo = {};");
2127 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2128 CHECK(ValidateSnapshot(snapshot));
2129 const v8::HeapGraphNode* gc_roots = GetNode(
2130 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2132 const v8::HeapGraphNode* strong_roots = GetNode(
2133 gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
2134 CHECK(strong_roots);
2135 for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
2136 const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
2137 CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
2138 v8::String::Utf8Value name(edge->GetName());
2139 CHECK(isalpha(**name));
2144 TEST(NoRefsToNonEssentialEntries) {
2146 v8::HandleScope scope(env->GetIsolate());
2147 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2148 CompileRun("global_object = {};\n");
2149 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2150 CHECK(ValidateSnapshot(snapshot));
2151 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2152 const v8::HeapGraphNode* global_object =
2153 GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
2154 CHECK(global_object);
2155 const v8::HeapGraphNode* properties =
2156 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
2158 const v8::HeapGraphNode* elements =
2159 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
2164 TEST(MapHasDescriptorsAndTransitions) {
2166 v8::HandleScope scope(env->GetIsolate());
2167 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2168 CompileRun("obj = { a: 10 };\n");
2169 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2170 CHECK(ValidateSnapshot(snapshot));
2171 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2172 const v8::HeapGraphNode* global_object =
2173 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2174 CHECK(global_object);
2176 const v8::HeapGraphNode* map =
2177 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
2179 const v8::HeapGraphNode* own_descriptors = GetProperty(
2180 map, v8::HeapGraphEdge::kInternal, "descriptors");
2181 CHECK(own_descriptors);
2182 const v8::HeapGraphNode* own_transitions = GetProperty(
2183 map, v8::HeapGraphEdge::kInternal, "transitions");
2184 CHECK(!own_transitions);
2188 TEST(ManyLocalsInSharedContext) {
2190 v8::HandleScope scope(env->GetIsolate());
2191 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2192 int num_objects = 6000;
2196 "result.push('(function outer() {');"
2197 "for (var i = 0; i < n; i++) {"
2198 " var f = 'function f_' + i + '() { ';"
2200 " f += 'f_' + (i - 1) + '();';"
2204 "result.push('return f_' + (n - 1) + ';');"
2205 "result.push('})()');"
2206 "var ok = eval(result.join('\\n'));");
2207 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2208 CHECK(ValidateSnapshot(snapshot));
2210 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2212 const v8::HeapGraphNode* ok_object =
2213 GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
2215 const v8::HeapGraphNode* context_object =
2216 GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
2217 CHECK(context_object);
2218 // Check the objects are not duplicated in the context.
2219 CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
2220 context_object->GetChildrenCount());
2221 // Check all the objects have got their names.
2222 // ... well check just every 15th because otherwise it's too slow in debug.
2223 for (int i = 0; i < num_objects - 1; i += 15) {
2224 i::EmbeddedVector<char, 100> var_name;
2225 i::SNPrintF(var_name, "f_%d", i);
2226 const v8::HeapGraphNode* f_object = GetProperty(
2227 context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
2233 TEST(AllocationSitesAreVisible) {
2235 v8::Isolate* isolate = env->GetIsolate();
2236 v8::HandleScope scope(isolate);
2237 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2239 "fun = function () { var a = [3, 2, 1]; return a; }\n"
2241 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2242 CHECK(ValidateSnapshot(snapshot));
2244 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2246 const v8::HeapGraphNode* fun_code =
2247 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2249 const v8::HeapGraphNode* literals =
2250 GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
2252 CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
2253 CHECK_EQ(1, literals->GetChildrenCount());
2255 // The first value in the literals array should be the boilerplate,
2256 // after an AllocationSite.
2257 const v8::HeapGraphEdge* prop = literals->GetChild(0);
2258 const v8::HeapGraphNode* allocation_site = prop->GetToNode();
2259 v8::String::Utf8Value name(allocation_site->GetName());
2260 CHECK_EQ(0, strcmp("system / AllocationSite", *name));
2261 const v8::HeapGraphNode* transition_info =
2262 GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
2264 CHECK(transition_info);
2266 const v8::HeapGraphNode* elements =
2267 GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
2270 CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
2271 CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
2272 static_cast<int>(elements->GetShallowSize()));
2274 v8::Handle<v8::Value> array_val =
2275 heap_profiler->FindObjectById(transition_info->GetId());
2276 CHECK(array_val->IsArray());
2277 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(array_val);
2278 // Verify the array is "a" in the code above.
2279 CHECK_EQ(3u, array->Length());
2280 CHECK(v8::Integer::New(isolate, 3)
2281 ->Equals(array->Get(v8::Integer::New(isolate, 0))));
2282 CHECK(v8::Integer::New(isolate, 2)
2283 ->Equals(array->Get(v8::Integer::New(isolate, 1))));
2284 CHECK(v8::Integer::New(isolate, 1)
2285 ->Equals(array->Get(v8::Integer::New(isolate, 2))));
2289 TEST(JSFunctionHasCodeLink) {
2291 v8::HandleScope scope(env->GetIsolate());
2292 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2293 CompileRun("function foo(x, y) { return x + y; }\n");
2294 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2295 CHECK(ValidateSnapshot(snapshot));
2296 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2297 const v8::HeapGraphNode* foo_func =
2298 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2300 const v8::HeapGraphNode* code =
2301 GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
2306 static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot,
2309 const v8::HeapGraphNode* node = snapshot->GetRoot();
2310 for (int current_depth = 0; current_depth < depth; ++current_depth) {
2311 int i, count = node->GetChildrenCount();
2312 for (i = 0; i < count; ++i) {
2313 const v8::HeapGraphEdge* edge = node->GetChild(i);
2314 const v8::HeapGraphNode* to_node = edge->GetToNode();
2315 v8::String::Utf8Value edge_name(edge->GetName());
2316 v8::String::Utf8Value node_name(to_node->GetName());
2317 i::EmbeddedVector<char, 100> name;
2318 i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
2319 if (strstr(name.start(), path[current_depth])) {
2324 if (i == count) return NULL;
2330 TEST(CheckCodeNames) {
2332 v8::HandleScope scope(env->GetIsolate());
2333 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2334 CompileRun("var a = 1.1;");
2335 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2336 CHECK(ValidateSnapshot(snapshot));
2338 const char* stub_path[] = {
2342 "::(ArraySingleArgumentConstructorStub code)"
2344 const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
2345 stub_path, arraysize(stub_path));
2348 const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
2349 "::(KeyedLoadIC_Megamorphic builtin)"};
2350 node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
2353 const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
2354 "::(CompileLazy builtin)"};
2355 node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
2357 v8::String::Utf8Value node_name(node->GetName());
2358 CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
2362 static const char* record_trace_tree_source =
2363 "var topFunctions = [];\n"
2364 "var global = this;\n"
2365 "function generateFunctions(width, depth) {\n"
2366 " var script = [];\n"
2367 " for (var i = 0; i < width; i++) {\n"
2368 " for (var j = 0; j < depth; j++) {\n"
2369 " script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
2370 " script.push(' try {\\n');\n"
2371 " if (j < depth-2) {\n"
2372 " script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
2373 " } else if (j == depth - 2) {\n"
2374 " script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
2375 " } else if (j == depth - 1) {\n"
2376 " script.push(' this.ts = Date.now();\\n');\n"
2378 " script.push(' } catch (e) {}\\n');\n"
2379 " script.push('}\\n');\n"
2383 " var script = script.join('');\n"
2384 " // throw script;\n"
2385 " global.eval(script);\n"
2386 " for (var i = 0; i < width; i++) {\n"
2387 " topFunctions.push(this['f_' + i + '_0']);\n"
2393 "generateFunctions(width, depth);\n"
2394 "var instances = [];\n"
2395 "function start() {\n"
2396 " for (var i = 0; i < width; i++) {\n"
2397 " instances.push(topFunctions[i](0));\n"
2401 "for (var i = 0; i < 100; i++) start();\n";
2404 static AllocationTraceNode* FindNode(
2405 AllocationTracker* tracker, const Vector<const char*>& names) {
2406 AllocationTraceNode* node = tracker->trace_tree()->root();
2407 for (int i = 0; node != NULL && i < names.length(); i++) {
2408 const char* name = names[i];
2409 Vector<AllocationTraceNode*> children = node->children();
2411 for (int j = 0; j < children.length(); j++) {
2412 unsigned index = children[j]->function_info_index();
2413 AllocationTracker::FunctionInfo* info =
2414 tracker->function_info_list()[index];
2415 if (info && strcmp(info->name, name) == 0) {
2425 TEST(ArrayGrowLeftTrim) {
2427 v8::HandleScope scope(env->GetIsolate());
2428 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2429 heap_profiler->StartTrackingHeapObjects(true);
2433 "for (var i = 0; i < 5; ++i)\n"
2435 "for (var i = 0; i < 3; ++i)\n"
2438 const char* names[] = {""};
2439 AllocationTracker* tracker =
2440 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2442 // Resolve all function locations.
2443 tracker->PrepareForSerialization();
2444 // Print for better diagnostics in case of failure.
2445 tracker->trace_tree()->Print(tracker);
2447 AllocationTraceNode* node =
2448 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2450 CHECK_GE(node->allocation_count(), 2u);
2451 CHECK_GE(node->allocation_size(), 4u * 5u);
2452 heap_profiler->StopTrackingHeapObjects();
2456 TEST(TrackHeapAllocations) {
2457 v8::HandleScope scope(v8::Isolate::GetCurrent());
2460 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2461 heap_profiler->StartTrackingHeapObjects(true);
2463 CompileRun(record_trace_tree_source);
2465 AllocationTracker* tracker =
2466 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2468 // Resolve all function locations.
2469 tracker->PrepareForSerialization();
2470 // Print for better diagnostics in case of failure.
2471 tracker->trace_tree()->Print(tracker);
2473 const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2474 AllocationTraceNode* node =
2475 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2477 CHECK_GE(node->allocation_count(), 100u);
2478 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2479 heap_profiler->StopTrackingHeapObjects();
2483 static const char* inline_heap_allocation_source =
2484 "function f_0(x) {\n"
2485 " return f_1(x+1);\n"
2487 "%NeverOptimizeFunction(f_0);\n"
2488 "function f_1(x) {\n"
2489 " return new f_2(x+1);\n"
2491 "function f_2(x) {\n"
2494 "var instances = [];\n"
2495 "function start() {\n"
2496 " instances.push(f_0(0));\n"
2499 "for (var i = 0; i < 100; i++) start();\n";
2502 TEST(TrackBumpPointerAllocations) {
2503 i::FLAG_allow_natives_syntax = true;
2504 v8::HandleScope scope(v8::Isolate::GetCurrent());
2507 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2508 const char* names[] = {"", "start", "f_0", "f_1"};
2509 // First check that normally all allocations are recorded.
2511 heap_profiler->StartTrackingHeapObjects(true);
2513 CompileRun(inline_heap_allocation_source);
2515 AllocationTracker* tracker =
2516 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2518 // Resolve all function locations.
2519 tracker->PrepareForSerialization();
2520 // Print for better diagnostics in case of failure.
2521 tracker->trace_tree()->Print(tracker);
2523 AllocationTraceNode* node =
2524 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2526 CHECK_GE(node->allocation_count(), 100u);
2527 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2528 heap_profiler->StopTrackingHeapObjects();
2532 heap_profiler->StartTrackingHeapObjects(true);
2534 // Now check that not all allocations are tracked if we manually reenable
2535 // inline allocations.
2536 CHECK(CcTest::heap()->inline_allocation_disabled());
2537 CcTest::heap()->EnableInlineAllocation();
2539 CompileRun(inline_heap_allocation_source);
2541 AllocationTracker* tracker =
2542 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2544 // Resolve all function locations.
2545 tracker->PrepareForSerialization();
2546 // Print for better diagnostics in case of failure.
2547 tracker->trace_tree()->Print(tracker);
2549 AllocationTraceNode* node =
2550 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2552 CHECK_LT(node->allocation_count(), 100u);
2554 CcTest::heap()->DisableInlineAllocation();
2555 heap_profiler->StopTrackingHeapObjects();
2560 TEST(TrackV8ApiAllocation) {
2561 v8::HandleScope scope(v8::Isolate::GetCurrent());
2564 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2565 const char* names[] = { "(V8 API)" };
2566 heap_profiler->StartTrackingHeapObjects(true);
2568 v8::Handle<v8::Object> o1 = v8::Object::New(env->GetIsolate());
2571 AllocationTracker* tracker =
2572 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2574 // Resolve all function locations.
2575 tracker->PrepareForSerialization();
2576 // Print for better diagnostics in case of failure.
2577 tracker->trace_tree()->Print(tracker);
2579 AllocationTraceNode* node =
2580 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2582 CHECK_GE(node->allocation_count(), 2u);
2583 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2584 heap_profiler->StopTrackingHeapObjects();
2588 TEST(ArrayBufferAndArrayBufferView) {
2590 v8::HandleScope scope(env->GetIsolate());
2591 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2592 CompileRun("arr1 = new Uint32Array(100);\n");
2593 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2594 CHECK(ValidateSnapshot(snapshot));
2595 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2596 const v8::HeapGraphNode* arr1_obj =
2597 GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1");
2599 const v8::HeapGraphNode* arr1_buffer =
2600 GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
2602 const v8::HeapGraphNode* backing_store =
2603 GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store");
2604 CHECK(backing_store);
2605 CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
2609 static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
2610 const v8::HeapGraphNode* node) {
2612 for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
2613 const v8::HeapGraphNode* parent = snapshot->GetNode(i);
2614 for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
2615 if (parent->GetChild(j)->GetToNode() == node) {
2624 TEST(ArrayBufferSharedBackingStore) {
2626 v8::Isolate* isolate = env->GetIsolate();
2627 v8::HandleScope handle_scope(isolate);
2628 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2630 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
2631 CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
2632 CHECK(!ab->IsExternal());
2633 v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
2634 CHECK(ab->IsExternal());
2636 CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
2637 void* data = ab_contents.Data();
2638 DCHECK(data != NULL);
2639 v8::Local<v8::ArrayBuffer> ab2 =
2640 v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
2641 CHECK(ab2->IsExternal());
2642 env->Global()->Set(v8_str("ab1"), ab);
2643 env->Global()->Set(v8_str("ab2"), ab2);
2645 v8::Handle<v8::Value> result = CompileRun("ab2.byteLength");
2646 CHECK_EQ(1024, result->Int32Value());
2648 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2649 CHECK(ValidateSnapshot(snapshot));
2650 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2651 const v8::HeapGraphNode* ab1_node =
2652 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1");
2654 const v8::HeapGraphNode* ab1_data =
2655 GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store");
2657 const v8::HeapGraphNode* ab2_node =
2658 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2");
2660 const v8::HeapGraphNode* ab2_data =
2661 GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store");
2663 CHECK_EQ(ab1_data, ab2_data);
2664 CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
2670 v8::Isolate* isolate = CcTest::isolate();
2671 v8::HandleScope scope(isolate);
2673 v8::Handle<v8::Object> global_proxy = env->Global();
2674 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
2676 i::Factory* factory = CcTest::i_isolate()->factory();
2677 i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
2678 i::Handle<i::Object> box = factory->NewBox(string);
2679 global->Set(0, v8::ToApiHandle<v8::Object>(box));
2681 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2682 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2683 CHECK(ValidateSnapshot(snapshot));
2684 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
2685 const v8::HeapGraphNode* box_node =
2686 GetProperty(global_node, v8::HeapGraphEdge::kElement, "0");
2688 v8::String::Utf8Value box_node_name(box_node->GetName());
2689 CHECK_EQ(0, strcmp("system / Box", *box_node_name));
2690 const v8::HeapGraphNode* box_value =
2691 GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value");
2696 TEST(WeakContainers) {
2697 i::FLAG_allow_natives_syntax = true;
2699 v8::HandleScope scope(env->GetIsolate());
2700 if (!CcTest::i_isolate()->use_crankshaft()) return;
2701 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2703 "function foo(a) { return a.x; }\n"
2704 "obj = {x : 123};\n"
2707 "%OptimizeFunctionOnNextCall(foo);\n"
2709 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
2710 CHECK(ValidateSnapshot(snapshot));
2711 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2712 const v8::HeapGraphNode* obj =
2713 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2715 const v8::HeapGraphNode* map =
2716 GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
2718 const v8::HeapGraphNode* dependent_code =
2719 GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
2720 if (!dependent_code) return;
2721 int count = dependent_code->GetChildrenCount();
2723 for (int i = 0; i < count; ++i) {
2724 const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
2725 CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
2730 static inline i::Address ToAddress(int n) {
2731 return reinterpret_cast<i::Address>(n);
2735 TEST(AddressToTraceMap) {
2736 i::AddressToTraceMap map;
2738 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
2740 // [0x100, 0x200) -> 1
2741 map.AddRange(ToAddress(0x100), 0x100, 1U);
2742 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
2743 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
2744 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
2745 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
2746 CHECK_EQ(1u, map.size());
2748 // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
2749 map.AddRange(ToAddress(0x200), 0x100, 2U);
2750 CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2a0)));
2751 CHECK_EQ(2u, map.size());
2753 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
2754 map.AddRange(ToAddress(0x180), 0x100, 3U);
2755 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
2756 CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
2757 CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
2758 CHECK_EQ(3u, map.size());
2760 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
2761 // [0x400, 0x500) -> 4
2762 map.AddRange(ToAddress(0x400), 0x100, 4U);
2763 CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
2764 CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
2765 CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
2766 CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
2767 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
2768 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
2769 CHECK_EQ(4u, map.size());
2771 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
2772 map.AddRange(ToAddress(0x200), 0x400, 5U);
2773 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
2774 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
2775 CHECK_EQ(3u, map.size());
2777 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
2778 map.AddRange(ToAddress(0x180), 0x80, 6U);
2779 map.AddRange(ToAddress(0x180), 0x80, 7U);
2780 CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
2781 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
2782 CHECK_EQ(3u, map.size());
2785 CHECK_EQ(0u, map.size());
2786 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));