1 // Copyright 2011 the V8 project authors. All rights reserved.
3 // Tests for heap profiler
10 #include "heap-profiler.h"
13 #include "utils-inl.h"
14 #include "../include/v8-profiler.h"
18 class NamedEntriesDetector {
20 NamedEntriesDetector()
21 : has_A2(false), has_B2(false), has_C2(false) {
24 void CheckEntry(i::HeapEntry* entry) {
25 if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
26 if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
27 if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
30 void CheckAllReachables(i::HeapEntry* root) {
31 i::List<i::HeapEntry*> list(10);
35 while (!list.is_empty()) {
36 i::HeapEntry* entry = list.RemoveLast();
37 i::Vector<i::HeapGraphEdge*> children = entry->children();
38 for (int i = 0; i < children.length(); ++i) {
39 if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
40 i::HeapEntry* child = children[i]->to();
41 if (!child->painted()) {
58 static const v8::HeapGraphNode* GetGlobalObject(
59 const v8::HeapSnapshot* snapshot) {
60 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
61 const v8::HeapGraphNode* global_obj =
62 snapshot->GetRoot()->GetChild(0)->GetToNode();
63 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
64 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
69 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
70 v8::HeapGraphEdge::Type type,
72 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
73 const v8::HeapGraphEdge* prop = node->GetChild(i);
74 v8::String::AsciiValue prop_name(prop->GetName());
75 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
76 return prop->GetToNode();
82 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
83 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
84 const v8::HeapGraphEdge* prop = node->GetChild(i);
85 const v8::HeapGraphNode* node = prop->GetToNode();
86 if (node->GetType() == v8::HeapGraphNode::kString) {
87 v8::String::AsciiValue node_name(node->GetName());
88 if (strcmp(contents, *node_name) == 0) return true;
96 v8::HandleScope scope;
101 "function B2(x) { return function() { return typeof x; }; }\n"
102 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
103 "var a2 = new A2();\n"
104 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
105 "var c2 = new C2(a2);");
106 const v8::HeapSnapshot* snapshot_env2 =
107 v8::HeapProfiler::TakeSnapshot(v8_str("env2"));
108 i::HeapSnapshot* i_snapshot_env2 =
109 const_cast<i::HeapSnapshot*>(
110 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
111 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
113 // Verify, that JS global object of env2 has '..2' properties.
114 const v8::HeapGraphNode* a2_node =
115 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
116 CHECK_NE(NULL, a2_node);
118 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
120 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
121 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
123 // Paint all nodes reachable from global object.
124 NamedEntriesDetector det;
125 i_snapshot_env2->ClearPaint();
126 det.CheckAllReachables(const_cast<i::HeapEntry*>(
127 reinterpret_cast<const i::HeapEntry*>(global_env2)));
134 TEST(HeapSnapshotObjectSizes) {
135 v8::HandleScope scope;
141 "function X(a, b) { this.a = a; this.b = b; }\n"
142 "x = new X(new X(), new X());\n"
144 "(function() { x.a.a = x.b; })();");
145 const v8::HeapSnapshot* snapshot =
146 v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
147 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
148 const v8::HeapGraphNode* x =
149 GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
151 const v8::HeapGraphNode* x1 =
152 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
154 const v8::HeapGraphNode* x2 =
155 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
159 CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize());
160 CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize());
161 CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize());
165 TEST(BoundFunctionInSnapshot) {
166 v8::HandleScope scope;
169 "function myFunction(a, b) { this.a = a; this.b = b; }\n"
170 "function AAAAA() {}\n"
171 "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
172 const v8::HeapSnapshot* snapshot =
173 v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
174 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
175 const v8::HeapGraphNode* f =
176 GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
178 CHECK_EQ(v8::String::New("native_bind"), f->GetName());
179 const v8::HeapGraphNode* bindings =
180 GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
181 CHECK_NE(NULL, bindings);
182 CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
183 CHECK_EQ(4, bindings->GetChildrenCount());
185 const v8::HeapGraphNode* bound_this = GetProperty(
186 f, v8::HeapGraphEdge::kShortcut, "bound_this");
188 CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
190 const v8::HeapGraphNode* bound_function = GetProperty(
191 f, v8::HeapGraphEdge::kShortcut, "bound_function");
192 CHECK(bound_function);
193 CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
195 const v8::HeapGraphNode* bound_argument = GetProperty(
196 f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
197 CHECK(bound_argument);
198 CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
202 TEST(HeapSnapshotEntryChildren) {
203 v8::HandleScope scope;
209 const v8::HeapSnapshot* snapshot =
210 v8::HeapProfiler::TakeSnapshot(v8_str("children"));
211 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
212 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
213 const v8::HeapGraphEdge* prop = global->GetChild(i);
214 CHECK_EQ(global, prop->GetFromNode());
216 const v8::HeapGraphNode* a =
217 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
219 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
220 const v8::HeapGraphEdge* prop = a->GetChild(i);
221 CHECK_EQ(a, prop->GetFromNode());
226 TEST(HeapSnapshotCodeObjects) {
227 v8::HandleScope scope;
231 "function lazy(x) { return x - 1; }\n"
232 "function compiled(x) { return x + 1; }\n"
233 "var anonymous = (function() { return function() { return 0; } })();\n"
235 const v8::HeapSnapshot* snapshot =
236 v8::HeapProfiler::TakeSnapshot(v8_str("code"));
238 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
239 const v8::HeapGraphNode* compiled =
240 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
241 CHECK_NE(NULL, compiled);
242 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
243 const v8::HeapGraphNode* lazy =
244 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
245 CHECK_NE(NULL, lazy);
246 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
247 const v8::HeapGraphNode* anonymous =
248 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
249 CHECK_NE(NULL, anonymous);
250 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
251 v8::String::AsciiValue anonymous_name(anonymous->GetName());
252 CHECK_EQ("", *anonymous_name);
254 // Find references to code.
255 const v8::HeapGraphNode* compiled_code =
256 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
257 CHECK_NE(NULL, compiled_code);
258 const v8::HeapGraphNode* lazy_code =
259 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
260 CHECK_NE(NULL, lazy_code);
262 // Verify that non-compiled code doesn't contain references to "x"
263 // literal, while compiled code does. The scope info is stored in FixedArray
264 // objects attached to the SharedFunctionInfo.
265 bool compiled_references_x = false, lazy_references_x = false;
266 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
267 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
268 const v8::HeapGraphNode* node = prop->GetToNode();
269 if (node->GetType() == v8::HeapGraphNode::kArray) {
270 if (HasString(node, "x")) {
271 compiled_references_x = true;
276 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
277 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
278 const v8::HeapGraphNode* node = prop->GetToNode();
279 if (node->GetType() == v8::HeapGraphNode::kArray) {
280 if (HasString(node, "x")) {
281 lazy_references_x = true;
286 CHECK(compiled_references_x);
287 CHECK(!lazy_references_x);
291 TEST(HeapSnapshotHeapNumbers) {
292 v8::HandleScope scope;
295 "a = 1; // a is Smi\n"
296 "b = 2.5; // b is HeapNumber");
297 const v8::HeapSnapshot* snapshot =
298 v8::HeapProfiler::TakeSnapshot(v8_str("numbers"));
299 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
300 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
301 const v8::HeapGraphNode* b =
302 GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
304 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
307 TEST(HeapSnapshotSlicedString) {
308 v8::HandleScope scope;
311 "parent_string = \"123456789.123456789.123456789.123456789.123456789."
312 "123456789.123456789.123456789.123456789.123456789."
313 "123456789.123456789.123456789.123456789.123456789."
314 "123456789.123456789.123456789.123456789.123456789.\";"
315 "child_string = parent_string.slice(100);");
316 const v8::HeapSnapshot* snapshot =
317 v8::HeapProfiler::TakeSnapshot(v8_str("strings"));
318 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
319 const v8::HeapGraphNode* parent_string =
320 GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
321 CHECK_NE(NULL, parent_string);
322 const v8::HeapGraphNode* child_string =
323 GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
324 CHECK_NE(NULL, child_string);
325 const v8::HeapGraphNode* parent =
326 GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
327 CHECK_EQ(parent_string, parent);
330 TEST(HeapSnapshotInternalReferences) {
331 v8::HandleScope scope;
332 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
333 global_template->SetInternalFieldCount(2);
334 LocalContext env(NULL, global_template);
335 v8::Handle<v8::Object> global_proxy = env->Global();
336 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
337 CHECK_EQ(2, global->InternalFieldCount());
338 v8::Local<v8::Object> obj = v8::Object::New();
339 global->SetInternalField(0, v8_num(17));
340 global->SetInternalField(1, obj);
341 const v8::HeapSnapshot* snapshot =
342 v8::HeapProfiler::TakeSnapshot(v8_str("internals"));
343 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
344 // The first reference will not present, because it's a Smi.
345 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
346 // The second reference is to an object.
347 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
351 // Trying to introduce a check helper for uint32_t causes many
352 // overloading ambiguities, so it seems easier just to cast
353 // them to a signed type.
354 #define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \
355 CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b))
356 #define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \
357 CHECK((a) != (b)) // NOLINT
359 TEST(HeapEntryIdsAndArrayShift) {
360 v8::HandleScope scope;
364 "function AnObject() {\n"
365 " this.first = 'first';\n"
366 " this.second = 'second';\n"
368 "var a = new Array();\n"
369 "for (var i = 0; i < 10; ++i)\n"
370 " a.push(new AnObject());\n");
371 const v8::HeapSnapshot* snapshot1 =
372 v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
375 "for (var i = 0; i < 1; ++i)\n"
378 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
380 const v8::HeapSnapshot* snapshot2 =
381 v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
383 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
384 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
385 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
386 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
388 const v8::HeapGraphNode* a1 =
389 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
391 const v8::HeapGraphNode* k1 =
392 GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
394 const v8::HeapGraphNode* a2 =
395 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
397 const v8::HeapGraphNode* k2 =
398 GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
401 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
402 CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId());
405 TEST(HeapEntryIdsAndGC) {
406 v8::HandleScope scope;
411 "function B(x) { this.x = x; }\n"
413 "var b = new B(a);");
414 v8::Local<v8::String> s1_str = v8_str("s1");
415 v8::Local<v8::String> s2_str = v8_str("s2");
416 const v8::HeapSnapshot* snapshot1 =
417 v8::HeapProfiler::TakeSnapshot(s1_str);
419 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
421 const v8::HeapSnapshot* snapshot2 =
422 v8::HeapProfiler::TakeSnapshot(s2_str);
424 CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000);
425 CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
426 snapshot2->GetMaxSnapshotJSObjectId());
428 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
429 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
430 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
431 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
432 const v8::HeapGraphNode* A1 =
433 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
435 const v8::HeapGraphNode* A2 =
436 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
438 CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId());
439 CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId());
440 const v8::HeapGraphNode* B1 =
441 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
443 const v8::HeapGraphNode* B2 =
444 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
446 CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId());
447 CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId());
448 const v8::HeapGraphNode* a1 =
449 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
451 const v8::HeapGraphNode* a2 =
452 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
454 CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId());
455 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
456 const v8::HeapGraphNode* b1 =
457 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
459 const v8::HeapGraphNode* b2 =
460 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
462 CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId());
463 CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId());
467 TEST(HeapSnapshotRootPreservedAfterSorting) {
468 v8::HandleScope scope;
470 const v8::HeapSnapshot* snapshot =
471 v8::HeapProfiler::TakeSnapshot(v8_str("s"));
472 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
473 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
474 snapshot))->GetSortedEntriesList();
475 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
476 CHECK_EQ(root1, root2);
480 TEST(HeapEntryDominator) {
481 // The graph looks like this:
493 // The dominator for all nodes is node6.
495 v8::HandleScope scope;
499 "function X(a, b) { this.a = a; this.b = b; }\n"
500 "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
502 "node6.a.a.b = node6.b.a; // node1 -> node2\n"
503 "node6.b.a.a = node6.a.a; // node2 -> node1\n"
504 "node6.b.a.b = node6.b.b; // node2 -> node3\n"
505 "node6.b.b.a = node6.b.a; // node3 -> node2\n"
508 const v8::HeapSnapshot* snapshot =
509 v8::HeapProfiler::TakeSnapshot(v8_str("dominators"));
511 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
512 CHECK_NE(NULL, global);
513 const v8::HeapGraphNode* node6 =
514 GetProperty(global, v8::HeapGraphEdge::kProperty, "node6");
515 CHECK_NE(NULL, node6);
516 const v8::HeapGraphNode* node5 =
517 GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
518 CHECK_NE(NULL, node5);
519 const v8::HeapGraphNode* node4 =
520 GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
521 CHECK_NE(NULL, node4);
522 const v8::HeapGraphNode* node3 =
523 GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
524 CHECK_NE(NULL, node3);
525 const v8::HeapGraphNode* node2 =
526 GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
527 CHECK_NE(NULL, node2);
528 const v8::HeapGraphNode* node1 =
529 GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
530 CHECK_NE(NULL, node1);
532 CHECK_EQ(node6, node1->GetDominatorNode());
533 CHECK_EQ(node6, node2->GetDominatorNode());
534 CHECK_EQ(node6, node3->GetDominatorNode());
535 CHECK_EQ(node6, node4->GetDominatorNode());
536 CHECK_EQ(node6, node5->GetDominatorNode());
542 class TestJSONStream : public v8::OutputStream {
544 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
545 explicit TestJSONStream(int abort_countdown)
546 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
547 virtual ~TestJSONStream() {}
548 virtual void EndOfStream() { ++eos_signaled_; }
549 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
550 if (abort_countdown_ > 0) --abort_countdown_;
551 if (abort_countdown_ == 0) return kAbort;
552 CHECK_GT(chars_written, 0);
553 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
554 memcpy(chunk.start(), buffer, chars_written);
557 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
561 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
562 int eos_signaled() { return eos_signaled_; }
563 int size() { return buffer_.size(); }
566 i::Collector<char> buffer_;
568 int abort_countdown_;
571 class AsciiResource: public v8::String::ExternalAsciiStringResource {
573 explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
574 length_ = string.length();
576 virtual const char* data() const { return data_; }
577 virtual size_t length() const { return length_; }
585 TEST(HeapSnapshotJSONSerialization) {
586 v8::HandleScope scope;
589 #define STRING_LITERAL_FOR_TEST \
590 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
592 "function A(s) { this.s = s; }\n"
593 "function B(x) { this.x = x; }\n"
594 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
595 "var b = new B(a);");
596 const v8::HeapSnapshot* snapshot =
597 v8::HeapProfiler::TakeSnapshot(v8_str("json"));
598 TestJSONStream stream;
599 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
600 CHECK_GT(stream.size(), 0);
601 CHECK_EQ(1, stream.eos_signaled());
602 i::ScopedVector<char> json(stream.size());
603 stream.WriteTo(json);
605 // Verify that snapshot string is valid JSON.
606 AsciiResource json_res(json);
607 v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
608 env->Global()->Set(v8_str("json_snapshot"), json_string);
609 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
610 "var parsed = JSON.parse(json_snapshot); true;");
611 CHECK(!snapshot_parse_result.IsEmpty());
613 // Verify that snapshot object has required fields.
614 v8::Local<v8::Object> parsed_snapshot =
615 env->Global()->Get(v8_str("parsed"))->ToObject();
616 CHECK(parsed_snapshot->Has(v8_str("snapshot")));
617 CHECK(parsed_snapshot->Has(v8_str("nodes")));
618 CHECK(parsed_snapshot->Has(v8_str("edges")));
619 CHECK(parsed_snapshot->Has(v8_str("strings")));
621 // Get node and edge "member" offsets.
622 v8::Local<v8::Value> meta_analysis_result = CompileRun(
623 "var meta = parsed.snapshot.meta;\n"
624 "var edges_index_offset = meta.node_fields.indexOf('edges_index');\n"
625 "var node_fields_count = meta.node_fields.length;\n"
626 "var edge_fields_count = meta.edge_fields.length;\n"
627 "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
628 "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
629 "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
630 "var property_type ="
631 " meta.edge_types[edge_type_offset].indexOf('property');\n"
632 "var shortcut_type ="
633 " meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
634 "parsed.nodes.concat(0, 0, 0, 0, 0, 0, parsed.edges.length);");
635 CHECK(!meta_analysis_result.IsEmpty());
637 // A helper function for processing encoded nodes.
639 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
640 " var nodes = parsed.nodes;\n"
641 " var edges = parsed.edges;\n"
642 " var strings = parsed.strings;\n"
643 " for (var i = nodes[pos + edges_index_offset],\n"
644 " count = nodes[pos + node_fields_count + edges_index_offset];\n"
645 " i < count; i += edge_fields_count) {\n"
646 " if (edges[i + edge_type_offset] === prop_type\n"
647 " && strings[edges[i + edge_name_offset]] === prop_name)\n"
648 " return edges[i + edge_to_node_offset];\n"
652 // Get the string index using the path: <root> -> <global>.b.x.s
653 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
654 "GetChildPosByProperty(\n"
655 " GetChildPosByProperty(\n"
656 " GetChildPosByProperty("
657 " parsed.edges[parsed.nodes[edges_index_offset]"
658 " + edge_to_node_offset],"
659 " \"b\", property_type),\n"
660 " \"x\", property_type),"
661 " \"s\", property_type)");
662 CHECK(!string_obj_pos_val.IsEmpty());
664 static_cast<int>(string_obj_pos_val->ToNumber()->Value());
665 v8::Local<v8::Object> nodes_array =
666 parsed_snapshot->Get(v8_str("nodes"))->ToObject();
667 int string_index = static_cast<int>(
668 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
669 CHECK_GT(string_index, 0);
670 v8::Local<v8::Object> strings_array =
671 parsed_snapshot->Get(v8_str("strings"))->ToObject();
672 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
673 v8::Local<v8::String> ref_string =
674 CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
675 #undef STRING_LITERAL_FOR_TEST
676 CHECK_EQ(*v8::String::Utf8Value(ref_string),
677 *v8::String::Utf8Value(string));
681 TEST(HeapSnapshotJSONSerializationAborting) {
682 v8::HandleScope scope;
684 const v8::HeapSnapshot* snapshot =
685 v8::HeapProfiler::TakeSnapshot(v8_str("abort"));
686 TestJSONStream stream(5);
687 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
688 CHECK_GT(stream.size(), 0);
689 CHECK_EQ(0, stream.eos_signaled());
694 class TestStatsStream : public v8::OutputStream {
702 first_interval_index_(-1) { }
703 TestStatsStream(const TestStatsStream& stream)
704 : v8::OutputStream(stream),
705 eos_signaled_(stream.eos_signaled_),
706 updates_written_(stream.updates_written_),
707 entries_count_(stream.entries_count_),
708 entries_size_(stream.entries_size_),
709 intervals_count_(stream.intervals_count_),
710 first_interval_index_(stream.first_interval_index_) { }
711 virtual ~TestStatsStream() {}
712 virtual void EndOfStream() { ++eos_signaled_; }
713 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
717 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
718 int updates_written) {
720 ASSERT(updates_written);
721 updates_written_ += updates_written;
723 if (first_interval_index_ == -1 && updates_written != 0)
724 first_interval_index_ = buffer[0].index;
725 for (int i = 0; i < updates_written; ++i) {
726 entries_count_ += buffer[i].count;
727 entries_size_ += buffer[i].size;
732 int eos_signaled() { return eos_signaled_; }
733 int updates_written() { return updates_written_; }
734 uint32_t entries_count() const { return entries_count_; }
735 uint32_t entries_size() const { return entries_size_; }
736 int intervals_count() const { return intervals_count_; }
737 int first_interval_index() const { return first_interval_index_; }
741 int updates_written_;
742 uint32_t entries_count_;
743 uint32_t entries_size_;
744 int intervals_count_;
745 int first_interval_index_;
750 static TestStatsStream GetHeapStatsUpdate() {
751 TestStatsStream stream;
752 v8::HeapProfiler::PushHeapObjectsStats(&stream);
753 CHECK_EQ(1, stream.eos_signaled());
758 TEST(HeapSnapshotObjectsStats) {
759 v8::HandleScope scope;
762 v8::HeapProfiler::StartHeapObjectsTracking();
763 // We have to call GC 5 times. In other case the garbage will be
764 // the reason of flakiness.
765 for (int i = 0; i < 5; ++i) {
766 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
770 // Single chunk of data expected in update. Initial data.
771 TestStatsStream stats_update = GetHeapStatsUpdate();
772 CHECK_EQ(1, stats_update.intervals_count());
773 CHECK_EQ(1, stats_update.updates_written());
774 CHECK_LT(0, stats_update.entries_size());
775 CHECK_EQ(0, stats_update.first_interval_index());
778 // No data expected in update because nothing has happened.
779 CHECK_EQ(0, GetHeapStatsUpdate().updates_written());
781 v8::HandleScope inner_scope_1;
784 // Single chunk of data with one new entry expected in update.
785 TestStatsStream stats_update = GetHeapStatsUpdate();
786 CHECK_EQ(1, stats_update.intervals_count());
787 CHECK_EQ(1, stats_update.updates_written());
788 CHECK_LT(0, stats_update.entries_size());
789 CHECK_EQ(1, stats_update.entries_count());
790 CHECK_EQ(2, stats_update.first_interval_index());
793 // No data expected in update because nothing happened.
794 CHECK_EQ(0, GetHeapStatsUpdate().updates_written());
797 v8::HandleScope inner_scope_2;
800 uint32_t entries_size;
802 v8::HandleScope inner_scope_3;
807 // Single chunk of data with three new entries expected in update.
808 TestStatsStream stats_update = GetHeapStatsUpdate();
809 CHECK_EQ(1, stats_update.intervals_count());
810 CHECK_EQ(1, stats_update.updates_written());
811 CHECK_LT(0, entries_size = stats_update.entries_size());
812 CHECK_EQ(3, stats_update.entries_count());
813 CHECK_EQ(4, stats_update.first_interval_index());
818 // Single chunk of data with two left entries expected in update.
819 TestStatsStream stats_update = GetHeapStatsUpdate();
820 CHECK_EQ(1, stats_update.intervals_count());
821 CHECK_EQ(1, stats_update.updates_written());
822 CHECK_GT(entries_size, stats_update.entries_size());
823 CHECK_EQ(1, stats_update.entries_count());
824 // Two strings from forth interval were released.
825 CHECK_EQ(4, stats_update.first_interval_index());
830 // Single chunk of data with 0 left entries expected in update.
831 TestStatsStream stats_update = GetHeapStatsUpdate();
832 CHECK_EQ(1, stats_update.intervals_count());
833 CHECK_EQ(1, stats_update.updates_written());
834 CHECK_EQ(0, stats_update.entries_size());
835 CHECK_EQ(0, stats_update.entries_count());
836 // The last string from forth interval was released.
837 CHECK_EQ(4, stats_update.first_interval_index());
841 // Single chunk of data with 0 left entries expected in update.
842 TestStatsStream stats_update = GetHeapStatsUpdate();
843 CHECK_EQ(1, stats_update.intervals_count());
844 CHECK_EQ(1, stats_update.updates_written());
845 CHECK_EQ(0, stats_update.entries_size());
846 CHECK_EQ(0, stats_update.entries_count());
847 // The only string from the second interval was released.
848 CHECK_EQ(2, stats_update.first_interval_index());
851 v8::Local<v8::Array> array = v8::Array::New();
852 CHECK_EQ(0, array->Length());
853 // Force array's buffer allocation.
854 array->Set(2, v8_num(7));
856 uint32_t entries_size;
858 // Single chunk of data with 2 entries expected in update.
859 TestStatsStream stats_update = GetHeapStatsUpdate();
860 CHECK_EQ(1, stats_update.intervals_count());
861 CHECK_EQ(1, stats_update.updates_written());
862 CHECK_LT(0, entries_size = stats_update.entries_size());
863 // They are the array and its buffer.
864 CHECK_EQ(2, stats_update.entries_count());
865 CHECK_EQ(8, stats_update.first_interval_index());
868 for (int i = 0; i < 100; ++i)
869 array->Set(i, v8_num(i));
872 // Single chunk of data with 1 entry expected in update.
873 TestStatsStream stats_update = GetHeapStatsUpdate();
874 CHECK_EQ(1, stats_update.intervals_count());
875 // The first interval was changed because old buffer was collected.
876 // The second interval was changed because new buffer was allocated.
877 CHECK_EQ(2, stats_update.updates_written());
878 CHECK_LT(entries_size, stats_update.entries_size());
879 CHECK_EQ(2, stats_update.entries_count());
880 CHECK_EQ(8, stats_update.first_interval_index());
883 v8::HeapProfiler::StopHeapObjectsTracking();
887 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
888 const v8::HeapGraphNode* node,
889 int level, int max_level) {
890 if (level > max_level) return;
891 CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
892 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
893 const v8::HeapGraphEdge* prop = node->GetChild(i);
894 const v8::HeapGraphNode* child =
895 snapshot->GetNodeById(prop->GetToNode()->GetId());
896 CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId());
897 CHECK_EQ(prop->GetToNode(), child);
898 CheckChildrenIds(snapshot, child, level + 1, max_level);
903 TEST(HeapSnapshotGetNodeById) {
904 v8::HandleScope scope;
907 const v8::HeapSnapshot* snapshot =
908 v8::HeapProfiler::TakeSnapshot(v8_str("id"));
909 const v8::HeapGraphNode* root = snapshot->GetRoot();
910 CheckChildrenIds(snapshot, root, 0, 3);
911 // Check a big id, which should not exist yet.
912 CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
916 TEST(HeapSnapshotGetSnapshotObjectId) {
917 v8::HandleScope scope;
919 CompileRun("globalObject = {};\n");
920 const v8::HeapSnapshot* snapshot =
921 v8::HeapProfiler::TakeSnapshot(v8_str("get_snapshot_object_id"));
922 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
923 const v8::HeapGraphNode* global_object =
924 GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
925 CHECK(global_object);
927 v8::Local<v8::Value> globalObjectHandle =
928 env->Global()->Get(v8::String::New("globalObject"));
929 CHECK(!globalObjectHandle.IsEmpty());
930 CHECK(globalObjectHandle->IsObject());
932 v8::SnapshotObjectId id =
933 v8::HeapProfiler::GetSnapshotObjectId(globalObjectHandle);
934 CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId),
936 CHECK_EQ(static_cast<int>(id), global_object->GetId());
940 TEST(HeapSnapshotUnknownSnapshotObjectId) {
941 v8::HandleScope scope;
943 CompileRun("globalObject = {};\n");
944 const v8::HeapSnapshot* snapshot =
945 v8::HeapProfiler::TakeSnapshot(v8_str("unknown_object_id"));
946 const v8::HeapGraphNode* node =
947 snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
948 CHECK_EQ(NULL, node);
954 class TestActivityControl : public v8::ActivityControl {
956 explicit TestActivityControl(int abort_count)
957 : done_(0), total_(0), abort_count_(abort_count) {}
958 ControlOption ReportProgressValue(int done, int total) {
961 return --abort_count_ != 0 ? kContinue : kAbort;
963 int done() { return done_; }
964 int total() { return total_; }
973 TEST(TakeHeapSnapshotAborting) {
974 v8::HandleScope scope;
977 const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
978 TestActivityControl aborting_control(1);
979 const v8::HeapSnapshot* no_snapshot =
980 v8::HeapProfiler::TakeSnapshot(v8_str("abort"),
981 v8::HeapSnapshot::kFull,
983 CHECK_EQ(NULL, no_snapshot);
984 CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
985 CHECK_GT(aborting_control.total(), aborting_control.done());
987 TestActivityControl control(-1); // Don't abort.
988 const v8::HeapSnapshot* snapshot =
989 v8::HeapProfiler::TakeSnapshot(v8_str("full"),
990 v8::HeapSnapshot::kFull,
992 CHECK_NE(NULL, snapshot);
993 CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
994 CHECK_EQ(control.total(), control.done());
995 CHECK_GT(control.total(), 0);
1001 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1003 TestRetainedObjectInfo(int hash,
1004 const char* group_label,
1006 intptr_t element_count = -1,
1010 group_label_(group_label),
1012 element_count_(element_count),
1014 instances.Add(this);
1016 virtual ~TestRetainedObjectInfo() {}
1017 virtual void Dispose() {
1021 virtual bool IsEquivalent(RetainedObjectInfo* other) {
1022 return GetHash() == other->GetHash();
1024 virtual intptr_t GetHash() { return hash_; }
1025 virtual const char* GetGroupLabel() { return group_label_; }
1026 virtual const char* GetLabel() { return label_; }
1027 virtual intptr_t GetElementCount() { return element_count_; }
1028 virtual intptr_t GetSizeInBytes() { return size_; }
1029 bool disposed() { return disposed_; }
1031 static v8::RetainedObjectInfo* WrapperInfoCallback(
1032 uint16_t class_id, v8::Handle<v8::Value> wrapper) {
1033 if (class_id == 1) {
1034 if (wrapper->IsString()) {
1035 v8::String::AsciiValue ascii(wrapper);
1036 if (strcmp(*ascii, "AAA") == 0)
1037 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1038 else if (strcmp(*ascii, "BBB") == 0)
1039 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1041 } else if (class_id == 2) {
1042 if (wrapper->IsString()) {
1043 v8::String::AsciiValue ascii(wrapper);
1044 if (strcmp(*ascii, "CCC") == 0)
1045 return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1052 static i::List<TestRetainedObjectInfo*> instances;
1058 const char* group_label_;
1060 intptr_t element_count_;
1065 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1069 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1070 v8::HeapGraphNode::Type type,
1072 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1073 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1074 if (node->GetType() == type && strcmp(name,
1075 const_cast<i::HeapEntry*>(
1076 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1084 TEST(HeapSnapshotRetainedObjectInfo) {
1085 v8::HandleScope scope;
1088 v8::HeapProfiler::DefineWrapperClass(
1089 1, TestRetainedObjectInfo::WrapperInfoCallback);
1090 v8::HeapProfiler::DefineWrapperClass(
1091 2, TestRetainedObjectInfo::WrapperInfoCallback);
1092 v8::Persistent<v8::String> p_AAA =
1093 v8::Persistent<v8::String>::New(v8_str("AAA"));
1094 p_AAA.SetWrapperClassId(1);
1095 v8::Persistent<v8::String> p_BBB =
1096 v8::Persistent<v8::String>::New(v8_str("BBB"));
1097 p_BBB.SetWrapperClassId(1);
1098 v8::Persistent<v8::String> p_CCC =
1099 v8::Persistent<v8::String>::New(v8_str("CCC"));
1100 p_CCC.SetWrapperClassId(2);
1101 CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1102 const v8::HeapSnapshot* snapshot =
1103 v8::HeapProfiler::TakeSnapshot(v8_str("retained"));
1105 CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
1106 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
1107 CHECK(TestRetainedObjectInfo::instances[i]->disposed());
1108 delete TestRetainedObjectInfo::instances[i];
1111 const v8::HeapGraphNode* native_group_aaa = GetNode(
1112 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1113 CHECK_NE(NULL, native_group_aaa);
1114 CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1115 const v8::HeapGraphNode* aaa = GetNode(
1116 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1117 CHECK_NE(NULL, aaa);
1118 CHECK_EQ(2, aaa->GetChildrenCount());
1120 const v8::HeapGraphNode* native_group_ccc = GetNode(
1121 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1122 const v8::HeapGraphNode* ccc = GetNode(
1123 native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1124 CHECK_NE(NULL, ccc);
1126 const v8::HeapGraphNode* n_AAA = GetNode(
1127 aaa, v8::HeapGraphNode::kString, "AAA");
1128 CHECK_NE(NULL, n_AAA);
1129 const v8::HeapGraphNode* n_BBB = GetNode(
1130 aaa, v8::HeapGraphNode::kString, "BBB");
1131 CHECK_NE(NULL, n_BBB);
1132 CHECK_EQ(1, ccc->GetChildrenCount());
1133 const v8::HeapGraphNode* n_CCC = GetNode(
1134 ccc, v8::HeapGraphNode::kString, "CCC");
1135 CHECK_NE(NULL, n_CCC);
1137 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1138 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1139 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1143 class GraphWithImplicitRefs {
1145 static const int kObjectsCount = 4;
1146 explicit GraphWithImplicitRefs(LocalContext* env) {
1147 CHECK_EQ(NULL, instance_);
1149 for (int i = 0; i < kObjectsCount; i++) {
1150 objects_[i] = v8::Persistent<v8::Object>::New(v8::Object::New());
1152 (*env)->Global()->Set(v8_str("root_object"), objects_[0]);
1154 ~GraphWithImplicitRefs() {
1158 static void gcPrologue() {
1159 instance_->AddImplicitReferences();
1163 void AddImplicitReferences() {
1165 v8::V8::AddImplicitReferences(
1166 v8::Persistent<v8::Object>::Cast(objects_[0]), &objects_[1], 1);
1167 // Adding two more references(note length=2 in params): 1 -> 2, 1 -> 3
1168 v8::V8::AddImplicitReferences(
1169 v8::Persistent<v8::Object>::Cast(objects_[1]), &objects_[2], 2);
1172 v8::Persistent<v8::Value> objects_[kObjectsCount];
1173 static GraphWithImplicitRefs* instance_;
1176 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1179 TEST(HeapSnapshotImplicitReferences) {
1180 v8::HandleScope scope;
1183 GraphWithImplicitRefs graph(&env);
1184 v8::V8::SetGlobalGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1186 const v8::HeapSnapshot* snapshot =
1187 v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs"));
1189 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1190 const v8::HeapGraphNode* obj0 = GetProperty(
1191 global_object, v8::HeapGraphEdge::kProperty, "root_object");
1193 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1194 const v8::HeapGraphNode* obj1 = GetProperty(
1195 obj0, v8::HeapGraphEdge::kInternal, "native");
1197 int implicit_targets_count = 0;
1198 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1199 const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1200 v8::String::AsciiValue prop_name(prop->GetName());
1201 if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1202 strcmp("native", *prop_name) == 0) {
1203 ++implicit_targets_count;
1206 CHECK_EQ(2, implicit_targets_count);
1207 v8::V8::SetGlobalGCPrologueCallback(NULL);
1211 TEST(DeleteAllHeapSnapshots) {
1212 v8::HandleScope scope;
1215 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1216 v8::HeapProfiler::DeleteAllSnapshots();
1217 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1218 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
1219 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1220 v8::HeapProfiler::DeleteAllSnapshots();
1221 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1222 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
1223 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("2")));
1224 CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
1225 v8::HeapProfiler::DeleteAllSnapshots();
1226 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1230 TEST(DeleteHeapSnapshot) {
1231 v8::HandleScope scope;
1234 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1235 const v8::HeapSnapshot* s1 =
1236 v8::HeapProfiler::TakeSnapshot(v8_str("1"));
1238 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1239 unsigned uid1 = s1->GetUid();
1240 CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1));
1241 const_cast<v8::HeapSnapshot*>(s1)->Delete();
1242 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1243 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1));
1245 const v8::HeapSnapshot* s2 =
1246 v8::HeapProfiler::TakeSnapshot(v8_str("2"));
1248 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1249 unsigned uid2 = s2->GetUid();
1250 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
1251 CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2));
1252 const v8::HeapSnapshot* s3 =
1253 v8::HeapProfiler::TakeSnapshot(v8_str("3"));
1255 CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
1256 unsigned uid3 = s3->GetUid();
1257 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
1258 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
1259 const_cast<v8::HeapSnapshot*>(s2)->Delete();
1260 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
1261 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2));
1262 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
1263 const_cast<v8::HeapSnapshot*>(s3)->Delete();
1264 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
1265 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3));
1270 v8::HandleScope scope;
1273 CompileRun("document = { URL:\"abcdefgh\" };");
1275 const v8::HeapSnapshot* snapshot =
1276 v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1277 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1278 CHECK_NE(NULL, global);
1279 CHECK_EQ("Object / abcdefgh",
1280 const_cast<i::HeapEntry*>(
1281 reinterpret_cast<const i::HeapEntry*>(global))->name());
1285 TEST(DocumentWithException) {
1286 v8::HandleScope scope;
1290 "this.__defineGetter__(\"document\", function() { throw new Error(); })");
1291 const v8::HeapSnapshot* snapshot =
1292 v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1293 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1294 CHECK_NE(NULL, global);
1296 const_cast<i::HeapEntry*>(
1297 reinterpret_cast<const i::HeapEntry*>(global))->name());
1301 TEST(DocumentURLWithException) {
1302 v8::HandleScope scope;
1306 "function URLWithException() {}\n"
1307 "URLWithException.prototype = { get URL() { throw new Error(); } };\n"
1308 "document = { URL: new URLWithException() };");
1309 const v8::HeapSnapshot* snapshot =
1310 v8::HeapProfiler::TakeSnapshot(v8_str("document"));
1311 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1312 CHECK_NE(NULL, global);
1314 const_cast<i::HeapEntry*>(
1315 reinterpret_cast<const i::HeapEntry*>(global))->name());
1319 TEST(NoHandleLeaks) {
1320 v8::HandleScope scope;
1323 CompileRun("document = { URL:\"abcdefgh\" };");
1325 v8::Handle<v8::String> name(v8_str("leakz"));
1326 int count_before = i::HandleScope::NumberOfHandles();
1327 v8::HeapProfiler::TakeSnapshot(name);
1328 int count_after = i::HandleScope::NumberOfHandles();
1329 CHECK_EQ(count_before, count_after);
1333 TEST(NodesIteration) {
1334 v8::HandleScope scope;
1336 const v8::HeapSnapshot* snapshot =
1337 v8::HeapProfiler::TakeSnapshot(v8_str("iteration"));
1338 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1339 CHECK_NE(NULL, global);
1340 // Verify that we can find this object by iteration.
1341 const int nodes_count = snapshot->GetNodesCount();
1343 for (int i = 0; i < nodes_count; ++i) {
1344 if (snapshot->GetNode(i) == global)
1351 TEST(GetHeapValue) {
1352 v8::HandleScope scope;
1355 CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
1356 const v8::HeapSnapshot* snapshot =
1357 v8::HeapProfiler::TakeSnapshot(v8_str("value"));
1358 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1359 CHECK(global->GetHeapValue()->IsObject());
1360 v8::Local<v8::Object> js_global =
1361 env->Global()->GetPrototype().As<v8::Object>();
1362 CHECK(js_global == global->GetHeapValue());
1363 const v8::HeapGraphNode* obj = GetProperty(
1364 global, v8::HeapGraphEdge::kProperty, "a");
1365 CHECK(obj->GetHeapValue()->IsObject());
1366 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1367 CHECK(js_obj == obj->GetHeapValue());
1368 const v8::HeapGraphNode* s_prop =
1369 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1370 v8::Local<v8::String> js_s_prop =
1371 js_obj->Get(v8_str("s_prop")).As<v8::String>();
1372 CHECK(js_s_prop == s_prop->GetHeapValue());
1373 const v8::HeapGraphNode* n_prop =
1374 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1375 v8::Local<v8::Number> js_n_prop =
1376 js_obj->Get(v8_str("n_prop")).As<v8::Number>();
1377 CHECK(js_n_prop == n_prop->GetHeapValue());
1381 TEST(GetHeapValueForDeletedObject) {
1382 v8::HandleScope scope;
1385 // It is impossible to delete a global property, so we are about to delete a
1386 // property of the "a" object. Also, the "p" object can't be an empty one
1387 // because the empty object is static and isn't actually deleted.
1388 CompileRun("a = { p: { r: {} } };");
1389 const v8::HeapSnapshot* snapshot =
1390 v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1391 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1392 const v8::HeapGraphNode* obj = GetProperty(
1393 global, v8::HeapGraphEdge::kProperty, "a");
1394 const v8::HeapGraphNode* prop = GetProperty(
1395 obj, v8::HeapGraphEdge::kProperty, "p");
1397 // Perform the check inside a nested local scope to avoid creating a
1398 // reference to the object we are deleting.
1399 v8::HandleScope scope;
1400 CHECK(prop->GetHeapValue()->IsObject());
1402 CompileRun("delete a.p;");
1403 CHECK(prop->GetHeapValue()->IsUndefined());
1407 static int StringCmp(const char* ref, i::String* act) {
1408 i::SmartArrayPointer<char> s_act = act->ToCString();
1409 int result = strcmp(ref, *s_act);
1411 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act);
1416 TEST(GetConstructorName) {
1417 v8::HandleScope scope;
1421 "function Constructor1() {};\n"
1422 "var obj1 = new Constructor1();\n"
1423 "var Constructor2 = function() {};\n"
1424 "var obj2 = new Constructor2();\n"
1426 "obj3.constructor = function Constructor3() {};\n"
1428 "// Slow properties\n"
1429 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1430 "obj4.constructor = function Constructor4() {};\n"
1433 "obj6.constructor = 6;");
1434 v8::Local<v8::Object> js_global =
1435 env->Global()->GetPrototype().As<v8::Object>();
1436 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1437 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1438 CHECK_EQ(0, StringCmp(
1439 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1440 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1441 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1442 CHECK_EQ(0, StringCmp(
1443 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1444 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1445 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1446 CHECK_EQ(0, StringCmp(
1447 "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1448 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1449 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1450 CHECK_EQ(0, StringCmp(
1451 "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1452 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1453 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1454 CHECK_EQ(0, StringCmp(
1455 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1456 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1457 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1458 CHECK_EQ(0, StringCmp(
1459 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1463 TEST(FastCaseGetter) {
1464 v8::HandleScope scope;
1467 CompileRun("var obj1 = {};\n"
1468 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1471 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1472 " return this.value_ = value;\n"
1474 const v8::HeapSnapshot* snapshot =
1475 v8::HeapProfiler::TakeSnapshot(v8_str("fastCaseGetter"));
1477 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1478 CHECK_NE(NULL, global);
1479 const v8::HeapGraphNode* obj1 =
1480 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1481 CHECK_NE(NULL, obj1);
1482 const v8::HeapGraphNode* getterFunction =
1483 GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter");
1484 CHECK_NE(NULL, getterFunction);
1485 const v8::HeapGraphNode* setterFunction =
1486 GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter");
1487 CHECK_NE(NULL, setterFunction);
1491 bool HasWeakEdge(const v8::HeapGraphNode* node) {
1492 for (int i = 0; i < node->GetChildrenCount(); ++i) {
1493 const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
1494 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
1500 bool HasWeakGlobalHandle() {
1501 const v8::HeapSnapshot* snapshot =
1502 v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
1503 const v8::HeapGraphNode* gc_roots = GetNode(
1504 snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1505 CHECK_NE(NULL, gc_roots);
1506 const v8::HeapGraphNode* global_handles = GetNode(
1507 gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
1508 CHECK_NE(NULL, global_handles);
1509 return HasWeakEdge(global_handles);
1513 static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
1518 TEST(WeakGlobalHandle) {
1519 v8::HandleScope scope;
1522 CHECK(!HasWeakGlobalHandle());
1524 v8::Persistent<v8::Object> handle =
1525 v8::Persistent<v8::Object>::New(v8::Object::New());
1526 handle.MakeWeak(NULL, PersistentHandleCallback);
1528 CHECK(HasWeakGlobalHandle());
1532 TEST(WeakGlobalContextRefs) {
1533 v8::HandleScope scope;
1536 const v8::HeapSnapshot* snapshot =
1537 v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
1538 const v8::HeapGraphNode* gc_roots = GetNode(
1539 snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1540 CHECK_NE(NULL, gc_roots);
1541 const v8::HeapGraphNode* global_handles = GetNode(
1542 gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
1543 CHECK_NE(NULL, global_handles);
1544 const v8::HeapGraphNode* global_context = GetNode(
1545 global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext");
1546 CHECK_NE(NULL, global_context);
1547 CHECK(HasWeakEdge(global_context));
1551 TEST(SfiAndJsFunctionWeakRefs) {
1552 v8::HandleScope scope;
1556 "fun = (function (x) { return function () { return x + 1; } })(1);");
1557 const v8::HeapSnapshot* snapshot =
1558 v8::HeapProfiler::TakeSnapshot(v8_str("fun"));
1559 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1560 CHECK_NE(NULL, global);
1561 const v8::HeapGraphNode* fun =
1562 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
1563 CHECK(HasWeakEdge(fun));
1564 const v8::HeapGraphNode* shared =
1565 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
1566 CHECK(HasWeakEdge(shared));
1570 TEST(NoDebugObjectInSnapshot) {
1571 v8::HandleScope scope;
1574 v8::internal::Isolate::Current()->debug()->Load();
1575 CompileRun("foo = {};");
1576 const v8::HeapSnapshot* snapshot =
1577 v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1578 const v8::HeapGraphNode* root = snapshot->GetRoot();
1579 int globals_count = 0;
1580 for (int i = 0; i < root->GetChildrenCount(); ++i) {
1581 const v8::HeapGraphEdge* edge = root->GetChild(i);
1582 if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
1584 const v8::HeapGraphNode* global = edge->GetToNode();
1585 const v8::HeapGraphNode* foo =
1586 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
1587 CHECK_NE(NULL, foo);
1590 CHECK_EQ(1, globals_count);
1594 TEST(PersistentHandleCount) {
1595 v8::HandleScope scope;
1598 // V8 also uses global handles internally, so we can't test for an absolute
1600 int global_handle_count = v8::HeapProfiler::GetPersistentHandleCount();
1602 // Create some persistent handles.
1603 v8::Persistent<v8::String> p_AAA =
1604 v8::Persistent<v8::String>::New(v8_str("AAA"));
1605 CHECK_EQ(global_handle_count + 1,
1606 v8::HeapProfiler::GetPersistentHandleCount());
1607 v8::Persistent<v8::String> p_BBB =
1608 v8::Persistent<v8::String>::New(v8_str("BBB"));
1609 CHECK_EQ(global_handle_count + 2,
1610 v8::HeapProfiler::GetPersistentHandleCount());
1611 v8::Persistent<v8::String> p_CCC =
1612 v8::Persistent<v8::String>::New(v8_str("CCC"));
1613 CHECK_EQ(global_handle_count + 3,
1614 v8::HeapProfiler::GetPersistentHandleCount());
1616 // Dipose the persistent handles in a different order.
1618 CHECK_EQ(global_handle_count + 2,
1619 v8::HeapProfiler::GetPersistentHandleCount());
1621 CHECK_EQ(global_handle_count + 1,
1622 v8::HeapProfiler::GetPersistentHandleCount());
1624 CHECK_EQ(global_handle_count, v8::HeapProfiler::GetPersistentHandleCount());
1628 TEST(AllStrongGcRootsHaveNames) {
1629 v8::HandleScope scope;
1632 CompileRun("foo = {};");
1633 const v8::HeapSnapshot* snapshot =
1634 v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1635 const v8::HeapGraphNode* gc_roots = GetNode(
1636 snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
1637 CHECK_NE(NULL, gc_roots);
1638 const v8::HeapGraphNode* strong_roots = GetNode(
1639 gc_roots, v8::HeapGraphNode::kObject, "(Strong roots)");
1640 CHECK_NE(NULL, strong_roots);
1641 for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
1642 const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
1643 CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
1644 v8::String::AsciiValue name(edge->GetName());
1645 CHECK(isalpha(**name));
1650 TEST(NoRefsToNonEssentialEntries) {
1651 v8::HandleScope scope;
1653 CompileRun("global_object = {};\n");
1654 const v8::HeapSnapshot* snapshot =
1655 v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
1656 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1657 const v8::HeapGraphNode* global_object =
1658 GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
1659 CHECK_NE(NULL, global_object);
1660 const v8::HeapGraphNode* properties =
1661 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
1662 CHECK_EQ(NULL, properties);
1663 const v8::HeapGraphNode* elements =
1664 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
1665 CHECK_EQ(NULL, elements);